Giới thiệu Signals và Slots trong PyQt



Trong bài viết này, các bạn sẽ được tìm hiểu về Signals và Slots khi làm việc với PyQt và chúng có cách hoạt động như nào trong một chương trình PyQt.

1. Giới thiệu về Signals và Slots

Thông thường, một chương trình Python có thứ tự thực hiện từ trái sang phải, từ trên xuống dưới:

  • Nhận thông tin đầu vào.
  • Xử lý dữ liệu đầu vào để tạo ra đầu vào.
  • Ghi kết quả ra màn hình hoặc vào tệp.

⇒Đây là một chương trình theo Phương pháp lập trình hướng cấu trúc.
Trong khi các bạn xây dựng một chương trình có giao diện GUI (Graphical User Interface) thì bạn sẽ sử dụng chính xác Phương pháp lập trình hướng sự kiện. Theo đó, trong mô hình lập trình theo hướng sự kiện, một chương trình sẽ có luồng như sau:

  • Tạo các thành phần trên giao diện như Label (nhãn), Line Edit (nhập dữ liệu từ bàn phím)Button (nút ấn).
  • Bắt đầu một vòng lặp sự kiện và chờ các sự kiện được xảy ra.
  • Phản hồi các sự kiện khi chúng được xảy ra bằng cách gọi các hàm thực thi tương ứng.

⇒Để kết nối các sự kiện với các hàm thực thi trong chương trình, ta sử dụng cơ chế Signals và Slots trong PyQt.

Signals

Signals là một thuộc tính đặc biệt của một đối tượng được phát ra khi một sự kiện được “phát động“. Ở đây, một sự kiện có thể là hành động của người dùng (như bấm chuột, kéo và thả, …), thời gian chờ hoặc thời gian hoàn thành một hoạt động không đồng bộ.

Slots

Một Slot được gọi là callable (có khả năng gọi được) khi nó có thể nhận một Signal và phản hồi lại nó.
Để phản hồi lại các sự kiện, bạn cần kết nối Signal với các Slot tương ứng. Theo đó, mỗi khi sự kiện được sinh ra, sự kiện phát sinh Signal và Slot được gọi và thực thi.
Trong PyQt, tất cả các lớp con của lớp QObject có thể gửi và nhận tín hiệu. Hầu như tất cả các lớp trong PyQt đều là lớp con của lớp QObject.

Ví dụ về Signals và Slots trong PyQt

Quan sát vào chương trình sau đây. Chương trình sau nhằm mục đích hiển thị một cửa sổ có một nút ấn, mỗi khi bạn nhấn vào nút ấn đó thì bạn sẽ nhận được thông báo clicked trong cửa sổ CMD (Command Prompt).

import sys
from PyQt6.QtWidgets import (QApplication, QWidget, QPushButton, QLineEdit, QVBoxLayout)

class MainWindow(QWidget):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        # thiết lập tiêu ề cửa sổ
        self.setWindowTitle('PyQt6 Signals & Slots')
        self.setGeometry(200, 200, 200, 200)

        # khởi tạo một nút ấn và kết nối với một Signal
        button = QPushButton("Click Me!")
        button.clicked.connect(self.display_information)

        # đặt button lên trên cửa sổ dùng vertical box layout
        layout = QVBoxLayout()
        self.setLayout(layout)
        layout.addWidget(button)

        # hiển thị cửa sổ
        self.show()

    # xây dựng hàm
    def display_information(self):
        print("Clicked")

# chương trình chính
if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MainWindow()
    sys.exit(app.exec())

Kết quả chương trình:

Signals và Slots trong PyQt

Signals và Slots trong PyQt

Vậy, chương trình trên hoạt động như nào?
Đầu tiên, ta tạo một nút ấn bằng QPushButton:

button = QPushButton("Click Me!")

Thứ hai, kết nối sự kiện clicked tới hàm display_information:

button.clicked.connect(self.display_information)

Tổng quát chung, cú pháp để kết nối một Signal với Slot như sau:

sender_object.signal_name.connect(receiver_object.slot_name)

Ngoài ra, bạn cũng có thể kết nối Signal với Slot khi truyền một vào đối số như sau:

button = QPushButton('Click Me!', clicked=self.display_information)

Thứ ba, định nghĩa hàm display_information. Hàm này chỉ nhằm một mục đích là hiển thị thông báo ra màn hình CMD:

    def display_information(self):
        print("Clicked")

⇒Khi bạn nhấn vào nút ấn, nút ấn sẽ phát ra một Signal để gọi đến hàm được kết nối.
Lưu ý: đoạn code đặt nút ấn trên cửa sổ sử dụng bố cục theo phương dọc, các bạn sẽ được tìm hiểu trong những bài sau:

# đặt button lên trên cửa sổ dùng vertical box layout
        layout = QVBoxLayout()
        self.setLayout(layout)
        layout.addWidget(button)

Ví dụ sử dụng Signal để gửi dữ liệu

Một Signal có thể mang theo dữ liệu cung cấp trạng thái của một đối tượng khi sự kiện xảy ra. Ví dụ, Signal textChanged của QLineEdit có văn bản được nhập vào ô textbox.
Nếu Signal mang theo dữ liệu thì Slot được kết nối cũng nhận được dữ liệu đó.
Chương trình sau đây hiển thị QLineEditQLabel với nhiệm vụ như sau: khi bạn nhập cái gì đó vào QLineEdit thì QLabel sẽ hiển thị tương ứng.

import sys
from PyQt6.QtWidgets import (QApplication, QWidget, QPushButton, QLineEdit, QVBoxLayout, QLabel)

class MainWindow(QWidget):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        # thiết lập tiêu ề cửa sổ
        self.setWindowTitle('PyQt6 Signals & Slots')
        self.setGeometry(200, 200, 200, 200)

        # tạo các thành phần giao diện
        label = QLabel()
        line_edit = QLineEdit()
        line_edit.textChanged.connect(label.setText)

        # đặt các widgets lên trên giao diện
        layout = QVBoxLayout()
        layout.addWidget(label)
        layout.addWidget(line_edit)
        self.setLayout(layout)

        # hiển thị cửa sổ
        self.show()

# chương trình chính
if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MainWindow()
    sys.exit(app.exec())

Cùng xem kết quả của chương trình trên:

Sử dụng tín hiệu gửi dữ liệu

Sử dụng tín hiệu gửi dữ liệu

Vậy, chương trình này hoạt động như nào?
Đầu tiên, tạo một QLabel. Trong đó, QLabel có phương thức setText() để thiết lập nội dung văn bản hiển thị lên nó.

label = QLabel()

Thứ hai, tạo một QLineEdit mới:

line_edit = QLineEdit()

Thứ ba, kết nối Signal textChanged với phương thức setText của QLabel:

line_edit.textChanged.connect(label.setText)

⇒Khi đó, bạn gõ nội dung gì đó vào QLineEdit thì:

  • Signal textChanged sẽ gửi nội dung văn bản được nhập.
  • QLabel nhận văn bản và chuyển nó tới phương thức setText().

Có thể bạn sẽ thích…

Trả lời