1. 程式人生 > >PyQt4入門教程(5)_事件與訊號

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)。傳送者是傳送訊號的物件,接受者是接受訊號的物件,槽是對訊號做出反映的方法。【這裡對槽的解釋比較直白】
signal&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()函式知道了訊號的來源。在應用的狀態列中,我們顯示出被按下的按鈕的標籤。
eventsender

訊號的發出

由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訊號被髮出,應用終止。

在這一節中,我們簡要介紹了訊號槽系統。