2009年4月23日

[PyQt 教學] Part 5: Signals & Slots mechanism

[PyQt 教學] Part 4: Layout Management

回到先前的範例,一個簡單的 PushButton

#!/usr/bin/env python

import sys
from PyQt4.QtGui import *

app = QApplication(sys.argv)

button = QPushButton("&Quit")
button.show()

app.exec_()


事與願違。儘管按鈕上寫著斗大的「Quit」,猛擊幾百遍也是無動於衷。

然而其實當它被按下時,就會發出「我被按了」的訊號;只是因為沒人接收到,所以這訊號便隨風而逝瞭 QQ


為了讓 widget 間能彼此溝通,Qt 有一套 Signals & Slots 機制。看看官方的廣告:

Signals and slots are used for communication between objects. The signals and slots mechanism is a central feature of Qt and probably the part that differs most from the features provided by other frameworks.


在原本的程式碼加個一行:( code )


#!/usr/bin/env python

import sys
from PyQt4.QtGui import *

app = QApplication(sys.argv)

button = QPushButton("&Quit")
button.clicked.connect(button.close)
button.show()

app.exec_()

外在並沒有變,但是心已經變了。

新增的第 9 行:buttonclicked() 這個 signal,與 buttonclose() 這個 slot 聯繫 ( connect ) 起來。

之後當 button 被按下 ( 觸發 clicked() ) 時,送出的訊號就會把自己關掉瞭 (>ω<)♪♪

( 各個 class 所擁有的 signal 與 slot,在文檔裡都有列出。本例中 QPushButtonclicked() signal 是繼承自 QAbstractButton,而 close() slot 是繼承自 QWidget。)

這是 PyQt 4.5 起新的 pythonic 寫法,相信不難理解。要是看過之前版本的語法,就知道那不僅是一根髮指可以帶過的了......

總結 connect 的語法:

開槍的物件.signalName.connect(中槍的物件.slotName)


Qt 的 Signals & Slots 機制是用 QObject 這個 class 來實現的 ( 它是一切 Qt objects 的 base class )。signal 和 slot 都長得像一般函式一樣,而且可透過 QObject.connect() 連接在一起 ( 舊版語法 )。Class 本身的 signal 知道自己何時會被觸發,並且自動被 emit 出去 ( 也可以手動 emit )。slot 則會完成某些事,其實就是個普普通通的函式,也許會與聯繫的 signal 夾帶的參數有關。

signal 可以和許多 slot 或普通函式連接在一起 ( 多對多關係 );也可以讓 signal 與 signal 連接,產生連鎖反應。當然啦,如果 signal emit 出去卻沒人鳥的話,最後就會蒸發彷如無物瞭 (〒3〒)

signal emitter 並不需要知道是誰中槍,對於目標型別完全不必在乎;同樣地,slot 也不需要知道是誰開槍,只要知道自己中槍了就好。這個特色提供相當大的彈性;decoupling 的優勢隨著軟體愈來愈大,會愈發明顯。


Qt 主要就是靠這套機制讓 widget 間能夠相互溝通:我的 signal 摳你的 slot,你的 signal 摳他的 slot...... 摳來摳去組織出錯綜複雜的關係,讓程式能牽一髮而動全身。理解這套機制,對於上手 Qt programming 是相當關鍵的。


最後,給出另一個常見的 signals & slots 範例:( code )




#!/usr/bin/env python

import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *

app = QApplication(sys.argv)

spinBox = QSpinBox()
spinBox.setPrefix("$")
spinBox.setRange(0, 100)

slider = QSlider(Qt.Horizontal)
slider.setRange(0, 100)

spinBox.valueChanged.connect(slider.setValue)
slider.valueChanged.connect(spinBox.setValue)

layout = QHBoxLayout()
layout.addWidget(spinBox)
layout.addWidget(slider)

widget = QWidget()
widget.setLayout(layout)
widget.show()

app.exec_()

其中 PyQt4.QtCore 的導入是為了使用 Qt.Horizontal 這個 enum。

剩下的就請自行解讀吧!

[PyQt 教學] Part 6: 物件導向的寫法

4 則留言:

  1. 版主寫得很好耶~
    最近正在學PyQt4,受益良多呢

    回覆刪除
  2. 這系列真是寫得清楚易懂,看得神清氣爽好蘇湖~QAQ

    回覆刪除
  3. 對於像我這種剛使用pyqt者真的非常有用 感謝您~

    回覆刪除
  4. 這系列文章寫的太好了, 收獲好多, 感謝萬分

    回覆刪除