PyQt4入門教程(5)_事件與訊號
注:文中譯者的話將用方括號【】標出。
在這一部分的學習中,我們將會探索應用中將會出現的時間與訊號(events and signals)。
事件(Events)
所有的GUI應用都是由事件驅動的,這些事件主要是由應用的使用者產生的。其實這些事件也可以由其他方式產生,比如說網路連線、視窗管理系統或是一個計時器。當我們呼叫應用的exec_()方法時,應用進入到了主迴圈中,主迴圈獲取事件並且將他們傳送至各個物件。
在我們的事件模型中,有三個參與者:
- 事件來源(event source)
- 事件物件(event object)
- 事件目標(event target)
事件來源是那些狀態產生變化的物件,它產生事件。事件物件將狀態的變化封裝在了事件來源中。事件目標是希望被通知到的物件。事件來源將任務的處理委託給了事件目標。
PyQt4有一個獨特的訊號槽機制(signal and slot mechanism)來處理事件。訊號和槽被用來進行物件間的溝通。當一個特定的事件發生時,一個訊號被髮出;而槽可以是任意的Python請求。當和槽相關聯(connected)的訊號發出時,槽會被呼叫。
【大家意會一下就好】
新的介面(API)
Python4.5引入了一個全新風格的介面用來實現訊號槽系統。
老的介面風格是這樣的:
QtCore.QObject.connect(button, QtCore.SIGNAL('clicked()'), self.onClicked)
新的介面風格是這樣的:
button.clicked.connect(self.onClicked)
新的風格更加遵守Python標準。
【大家看到很多PyQt4的教程中都使用的是老的風格,這裡建議大家把PyQt版本升至4.5以上(注意不要搞成PyQt5了,4和5有些語法不一樣的),推薦使用新風格,很明顯新風格簡介清楚得多。 】
訊號和槽
下面的簡單程式演示了PyQt4中的訊號槽系統。
# -*- coding: utf-8 -*-
"""
In this example, we connect a signal
of a QtGui.QSlider to a slot
of a QtGui.QLCDNumber.
"""
import sys
from PyQt4 import QtGui, QtCore
class Example(QtGui.QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
lcd = QtGui.QLCDNumber(self)
sld = QtGui.QSlider(QtCore.Qt.Horizontal, self)
vbox = QtGui.QVBoxLayout()
vbox.addWidget(lcd)
vbox.addWidget(sld)
self.setLayout(vbox)
sld.valueChanged.connect(lcd.display)
self.setGeometry(300, 300, 250, 150)
self.setWindowTitle('Signal & slot')
self.show()
def main():
app = QtGui.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
在這個例子中,我們呈現了一個QtGui.QLCDNumber(以LCD的風格顯示數字)和一個QtGui.QSlider(滑塊)。我們通過拖動滑塊來改變LCD中的數字。
sld.valueChanged.connect(lcd.display)
這裡我們將滑塊的valueChanged訊號和LCD的display槽相關聯。
搞清三個概念:傳送者(sender)、接受者(receiver)和槽(slot)。傳送者是傳送訊號的物件,接受者是接受訊號的物件,槽是對訊號做出反映的方法。【這裡對槽的解釋比較直白】
事件控制代碼(event handler)的重製
【事件控制代碼,可以理解為事件處理程式。】
PyQt4中的事件常常通過重製事件控制代碼來實現。
# -*- coding: utf-8 -*-
"""
In this example, we reimplement an
event handler.
"""
import sys
from PyQt4 import QtGui, QtCore
class Example(QtGui.QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
self.setGeometry(300, 300, 250, 150)
self.setWindowTitle('Event handler')
self.show()
def keyPressEvent(self, e):
if e.key() == QtCore.Qt.Key_Escape:
self.close()
def main():
app = QtGui.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
在這個例子中,我們重製了keyPressEvent()這個事件控制代碼。
def keyPressEvent(self, e):
if e.key() == QtCore.Qt.Key_Escape:
self.close()
如果我們按下了Esc鍵,應用就會終止。
事件傳送者
有時知道是哪個部件傳送了訊號是一件有用的事。為了做到這一點,PyQt4有一個sender()方法。
# -*- coding: utf-8 -*-
"""
In this example, we determine the event sender
object.
"""
import sys
from PyQt4 import QtGui, QtCore
class Example(QtGui.QMainWindow):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
btn1 = QtGui.QPushButton("Button 1", self)
btn1.move(30, 50)
btn2 = QtGui.QPushButton("Button 2", self)
btn2.move(150, 50)
btn1.clicked.connect(self.buttonClicked)
btn2.clicked.connect(self.buttonClicked)
self.statusBar()
self.setGeometry(300, 300, 290, 150)
self.setWindowTitle('Event sender')
self.show()
def buttonClicked(self):
sender = self.sender()
self.statusBar().showMessage(sender.text() + ' was pressed')
def main():
app = QtGui.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
這裡我們建立了兩個按鈕,在buttonClicked()函式中我們呼叫sender()函式來知道我們點選的是哪個按鈕。
btn1.clicked.connect(self.buttonClicked)
btn2.clicked.connect(self.buttonClicked)
兩個按鈕都被關聯在了一個槽上。
def buttonClicked(self):
sender = self.sender()
self.statusBar().showMessage(sender.text() + ' was pressed')
我們通過呼叫sender()函式知道了訊號的來源。在應用的狀態列中,我們顯示出被按下的按鈕的標籤。
訊號的發出
由QtCore.QObject建立的物件可以發出訊號。下面的例子中我們會看到如何釋放傳統的訊號。
# -*- coding: utf-8 -*-
"""
In this example, we show how to emit a
signal.
"""
import sys
from PyQt4 import QtGui, QtCore
class Communicate(QtCore.QObject):
closeApp = QtCore.pyqtSignal()
class Example(QtGui.QMainWindow):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
self.c = Communicate()
self.c.closeApp.connect(self.close)
self.setGeometry(300, 300, 290, 150)
self.setWindowTitle('Emit signal')
self.show()
def mousePressEvent(self, event):
self.c.closeApp.emit()
def main():
app = QtGui.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
我們建立了一個叫做”closeApp”的新訊號,當滑鼠被點選時(滑鼠點選事件發生時)訊號會被髮出。這個訊號和QtGui.QMainWindow的close()關聯在了一起。
class Communicate(QtCore.QObject):
closeApp = QtCore.pyqtSignal()
一個訊號隨著QtCore.pyqtSignal()呼叫而建立了。
self.c.closeApp.connect(self.close)
closeApp訊號和QtGui.QMainWindow的close()槽關聯在了一起。
def mousePressEvent(self, event):
self.c.closeApp.emit()
當滑鼠指標在視窗中點選時,closeApp訊號被髮出,應用終止。