PyQT | Xử lý Sự kiện và Tương tác với Giao diện

 Ôn tập về sự kiện 

Sự kiện là những tương tác của người dùng tác động lên các thành phần của trang giao diện. 

 

Các sự kiện phổ biến 

  • Press/Release giữ thả nút, tác động lên các button. 

  • Click (Press Release) nhấn nút. tác động lên các button. 

  • Hover di chuột lên các widget. 

  • Enter/Leave tiến vào rời khỏi widget. 

Các bước lập trình sự kiện 

B1: Xác định widget mà người dùng sẽ tương tác (ví dụ self.ui.homeButton) 

B2: Xác định sự kiện mà người dùng sẽ tạo ra (ví dụ clicked) 

B3: Connect sự kiện với hàm hướng dẫn chương trình phản hồi bằng phương thức connect. 

self.ui.homeButton.connect(self.show_home) 

def show_home(self): 

    … 

 

 

 

Lập trình chuyển trang trong Stack Widget (ls8_sample/main1.py) 

Đối với sự kiện toggled của QtoolButton (nút Home trong sample là kiểu checkable nên ta sử dụng sự kiện toggled): 

  • Chỉ cần đặt tên hàm theo format on_<tên button>_<tên sự kiện> 

  • Chương trình sẽ mặc định đó hàm phản hồi cho sự kiện của nút không cần đến phương thức connect 

    def on_homeButton_toggled(self): 

        self.ui.stackedWidget.setCurrentIndex(0)


Cách sự kiện ảnh hưởng đến dữ liệu 

  • Để thực hiện các thao tác CRUD trong ứng dụng, ta cần nhấn các nút tương ứng Add, Edit, Delete. 

  • Khi các sự kiện add/edit/delete button clicked này xảy ra thì chức năng thêm/xóa/sửa tương ứng mới được thực hiện. 

à Cần phải kết nối sự kiện nhấn nút với các hàm thêm/xóa/sửa tương ứng. 

à Đồng thời, cần phải cập nhật lại dữ liệu trên ổ cứng khi các thao tác này xảy ra

Trong class MainWindow, tiến hành format lại code để setup trang CRUD 

        # Tạo database 

        self.dtb = models.AnimeDatabase() 

        self.dtb.load_data() 

 

        # Setup trang CRUD 

        self.setup_CRUD_page() 

         

        # Hiển thị cửa sổ ra màn hình 

        self.show() 

 

    def setup_CRUD_page(self): 

        # Hiển thị danh sách anime 

        anime_titles = self.dtb.get_title_list() 

        self.ui.animeList.addItems(anime_titles) 

        self.ui.animeList.setCurrentRow(0) 

 

        # Xử lý các button 

        self.ui.addButton.clicked.connect(self.add) 

        self.ui.editButton.clicked.connect(self.edit) 

        self.ui.removeButton.clicked.connect(self.delete) 

 

 

    def add(self): 

        pass 

 

    def edit(self): 

        pass 

 

    def delete(self): 

        pass 

 

    def search(self): 

        pass 


Picture 1, Hình ảnh 

 

 

Tạo folder widgets à Tạo class AddDialog trong file dialog.py 

import os 

from datetime import datetime 

 

from PyQt6 import uic 

from PyQt6.QtWidgets import QDialog, QFileDialog 

from PyQt6.QtCore import QDate, QDir 

 

from models import models 

 

UI_DIR = "ui" 

class AddDialog(QDialog): 

    """ 

    Hộp thoại Add 

    """ 

    UI_LOCATION = os.path.join(UI_DIR, "add_dialog.ui") 

    STYLE_LOCATION = os.path.join(UI_DIR, "style_popup.qss") 

 

    def __init__(self): 

        super().__init__(AddDialog) 

 

        # Load giao diện 

        self.ui = uic.loadUi(self.UI_LOCATION, self) 

        with open(self.STYLE_LOCATION, "r") as style_file: 

            style_config = style_file.read() 

        self.setStyleSheet(style_config) 

         

        # Tạo đối tượng QDir để quản lý đường dẫn 

        self.dir = QDir() 

 

        # Nút tải ảnh từ máy tính 

        self.ui.uploadImgButton.clicked.connect(self.browse_files) 

        self.ui.releasedateInput.setDisplayFormat("dd/MM/yyyy") # Format ngày tháng năm 

     

    def browse_files(self): 

        """ 

        Phương thức mở file dialog để chọn ảnh 

        """ 

        fname = QFileDialog.getOpenFileName(self, 

                                            'Open file' 

                                            './ui/images', 

                                            filter='Image files (*.png, *.jpg, *.svg)' 

                                            ) 

        self.ui.uploadImgButton.setText(fname[0]) 

        return fname 

 

    def return_input_fields(self) -> dict: 

        """ 

        Thu thập dữ liệu của tất cả các trường và trả về một dict 

        """ 

        # Xử lý trường ngày tháng năm 

        date_input = self.ui.releasedateInput.date().toPyDate() # formatted YYYY-mm-dd 

        image_path_input = self.ui.uploadImgButton.text() 

         

        # Xử lý trường URL 

        if self.ui.urlInput.text(): 

            url_input = self.ui.urlInput.text() 

        else: 

            url_input = "None" 

 

        # Trả dữ liệu 

        return { 

            "title": self.ui.titleInput.text(), 

            "release_date": date_input.strftime("%b %Y"), 

            "image": self.dir.relativeFilePath(image_path_input), 

            "rating": float(self.ui.ratingInput.text()), 

            "link": url_input 

        } 

 

 

Trong class EditDialog cần được lập trình để hiển thị những thông tin sẵn có của đối tượng: 

        # Hin thị dữ liệu hiện tại ca item 

        self.ui.titleInput.setText(edit_item.title) 

        date = datetime.strptime(edit_item.release_date, '%b %Y') 

        self.ui.releasedateInput.setDate(QDate(date.year, date.month, date.day)) 

        self.ui.uploadImgButton.setText(self.dir.relativeFilePath(edit_item.image)) 

        self.ui.ratingInput.setText(str(edit_item.rating)) 

        self.ui.urlInput.setText(edit_item.link) 


Import các class dialog 

from widgets import dialog 

 

Phương thức Add 

    def add(self): 

        curr_index = self.ui.animeList.currentRow() 

 

        # Tạo Dialog Add 

        add_dialog = dialog.AddDialog() 

        # Nếu nhn nút OK trên dialog 

        if add_dialog.exec(): 

            # Ly dữ liệu từ dialog 

            inputs = add_dialog.return_input_fields() 

            # Thêm item vào List Widget 

            self.ui.animeList.insertItem(curr_index, inputs["title"]) 

            # Thêm dữ liệu vào database 

            self.dtb.add_item(inputs) 

 

Phương thức Edit 

    def edit(self): 

        # Lấy item đang chọn 

        curr_index = self.ui.animeList.currentRow() 

        item = self.ui.animeList.item(curr_index) 

        item_title = item.text() 

        edit_item = self.dtb.get_first_item_by_title(item_title) 

 

        # Tạo Dialog Edit  

        if item is not None: 

            edit_dialog = dialog.EditDialog(edit_item) 

            # Nếu nhấn nút OK trên dialog 

            if edit_dialog.exec(): 

                # Lấy dữ liệu từ dialog 

                inputs = edit_dialog.return_input_fields() 

                # Sửa lại tên item trên List Widget 

                item.setText(inputs["title"]) 

                # Sửa dữ liệu trong database 

                self.dtb.edit_item(item_title, inputs) 

 

Phương thức Delete 

    def delete(self): 

        # Lấy item đang chọn 

        curr_index = self.ui.animeList.currentRow() 

        item = self.ui.animeList.item(curr_index) 

        item_title = item.text() 

         

        # Tạo Message Box confirm 

        if item is not None: 

            choice = QMessageBox.question(self, "Remove Anime", 

                                            "Do you want to remove this anime?", 

                                            QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No) 

            # Nếu chọn nút "Yes" 

            if choice == QMessageBox.StandardButton.Yes: 

                # Xoá item khỏi List Widget 

                item = self.ui.animeList.takeItem(curr_index) 

                # Xoá dữ liệu trong database 

                self.dtb.delete_item(item_title)


Thực hành xử lý sự kiện và tương tác với giao diện cho dự án cá nhân 

 

Mục tiêu: 

  • Xử sự kiện cho các nút trong giao diện 

  • Xử sự kiện nhấn các nút CRUD cho giao diện 

  • Xử các hộp thoại liên quan cho giao diện 

 

Nhiệm vụ: 

  • Tạo file app.py chứa code để chạy ứng dụng chính 

  • Lập trình kết nối giao diện ứng dụng với class MainWindow 

  • Lập trình sự kiện clicked của các nút trong giao diện MainWindow 

  • Lập trình kết nối giao diện hộp thoại với class Dialog (nếu ) 

  • Lập trình sự kiện mở cửa sổ duyệt file cho nút trong class Dialog (nếu ) 

  • Lập trình sự kiện mở hộp thoại xác nhận QMessageBox khi xoá đối tượng (nếu )

Mới hơn Cũ hơn