更新於 2025/01/19閱讀時間約 9 分鐘

[Python]PyQt 中的 pyqtSignal 和 pyqtSlot 教學

PyQt 中的 pyqtSignalpyqtSlot 教學

在使用 PyQt5 開發 GUI 程式時,信號 (Signal)槽 (Slot) 是重要的機制,用於元件之間的通訊。

PyQt 提供了 pyqtSignalpyqtSlot 來自定義信號和槽,進一步實現更靈活的功能。


一、pyqtSignal:自定義信號

pyqtSignal 是用來定義自訂信號的工具。你可以使用信號來通知其他部分程式某些事件發生了。

語法

from PyQt5.QtCore import QObject, pyqtSignal

class MyObject(QObject):
my_signal = pyqtSignal([參數類型1, 參數類型2, ...])
  • 信號特性:
    • 可以攜帶參數。
    • 需要先定義信號,然後在程式中觸發 (emit)。
    • 信號可以與槽函數連接。

範例:定義和使用信號

from PyQt5.QtCore import QObject, pyqtSignal

class Worker(QObject):
# 定義一個信號,攜帶一個字符串參數
progress_signal = pyqtSignal(str)

def do_work(self):
# 當工作進行時,發出信號
for i in range(5):
self.progress_signal.emit(f"Step {i + 1} completed!")

# 創建對象
worker = Worker()

# 定義槽函數
def handle_progress(message):
print(message)

# 將信號連接到槽
worker.progress_signal.connect(handle_progress)

# 開始工作並觸發信號
worker.do_work()

輸出:

Step 1 completed!
Step 2 completed!
Step 3 completed!
Step 4 completed!
Step 5 completed!

二、pyqtSlot:明確的槽函數

pyqtSlot 是用來定義槽函數的裝飾器。儘管 Python 的普通函數也能作為槽函數,但 pyqtSlot 能帶來一些優勢,例如:

  1. 提升效能:減少 Python 和 C++ 之間的邊界開銷。
  2. 強化類型檢查:明確指定槽函數接受的參數類型。

語法

from PyQt5.QtCore import pyqtSlot

@pyqtSlot(參數類型1, 參數類型2, ...)
def your_slot_function(...):
...

範例:使用 pyqtSlot 定義槽

from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot

class Worker(QObject):
progress_signal = pyqtSignal(str)

def __init__(self):
super().__init__()
# 將信號連接到槽
self.progress_signal.connect(self.handle_progress)

@pyqtSlot(str) # 使用 pyqtSlot 裝飾器
def handle_progress(self, message):
print(f"Received message: {message}")

def do_work(self):
for i in range(3):
self.progress_signal.emit(f"Task {i + 1} done")

# 測試
worker = Worker()
worker.do_work()

輸出:

Received message: Task 1 done
Received message: Task 2 done
Received message: Task 3 done

三、綜合範例:信號與槽的完整應用

以下是一個更複雜的例子,展示信號與槽的結合用法。

from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot
from PyQt5.QtWidgets import QApplication, QLabel, QPushButton, QVBoxLayout, QWidget

class MyWidget(QWidget):
# 自訂信號,帶一個字符串參數
button_clicked_signal = pyqtSignal(str)

def __init__(self):
super().__init__()
self.setWindowTitle("信號與槽範例")

# 設置介面
self.label = QLabel("按下按鈕會觸發信號")
self.button = QPushButton("點我")
layout = QVBoxLayout()
layout.addWidget(self.label)
layout.addWidget(self.button)
self.setLayout(layout)

# 信號連接槽
self.button.clicked.connect(self.emit_signal)
self.button_clicked_signal.connect(self.update_label)

def emit_signal(self):
# 當按鈕被點擊時,發出自訂信號
self.button_clicked_signal.emit("按鈕被按下了!")

@pyqtSlot(str) # 使用 pyqtSlot 定義槽
def update_label(self, message):
self.label.setText(message)

# 測試程式
app = QApplication([])
window = MyWidget()
window.show()
app.exec_()

解釋:

  1. 信號定義:
    • button_clicked_signal = pyqtSignal(str) 定義了一個帶字符串參數的自訂信號。
  2. 信號發射:
    • 當按鈕被點擊時,執行 emit_signal,發出信號。
  3. 槽函數:
    • 使用 @pyqtSlot(str) 定義槽函數 update_label,更新介面上的標籤文字。



四、常見問題

1. pyqtSignalpyqtSlot 必須一起使用嗎?

  • 不必。普通函數也能作為槽函數,但 pyqtSlot 能提供更好的效能和類型檢查。

2. 一個信號可以連接多個槽嗎?

  • 可以,當信號發射時,所有連接的槽函數都會執行。

3. 槽函數可以有多個信號連接嗎?

  • 可以,信號之間並不衝突。

五、優化建議

  1. 使用 pyqtSlot 裝飾槽函數,提升效能。
  2. 利用信號攜帶參數,減少不必要的全域變數。
  3. 清晰地命名信號與槽,增強可讀性。
分享至
成為作者繼續創作的動力吧!
© 2025 vocus All rights reserved.