做老師佈置的大作業用了PyQt,然後信號與槽的部分要傳入參數,研究了半天,最後找到一篇不錯的教程(源地址:http://frymgump.iteye.com/blog/846557):
昨天在羣裏一個朋友提出了一個問題,要求在PyQt中click一個pushButton時給它的響應槽傳入一個和發射信號的對象屬性相關的參數,比如按順序創建了N個pushButton,把這個次序數i告訴槽函數。
這本來看上去是一個很簡單的問題,可問題就在於QPushButton只有click()等沒有任何參數的信號,而自己在寫相應的槽的時候無法讓其再傳入一個自定義的參數。而信號發射時如果不帶參數的話槽函數根本無法獲知是哪個widget向它發射了信號,自然也無法處理和發射對象相關的屬性。
當然這個問題還得從qt的信號和槽機制說起,槽函數必須和信號的參數是保持一致的,定義信號發射時帶了幾個參數,槽函數被調用時就會以這幾個參數爲入參,這是關鍵的一步。我一直覺得qt自稱最大特色的信號和槽機制是個不好的發明,也許它真的很強大,但它的機制很彆扭,不容易理解,這會給開發者帶來困惑,就像我一樣。
於是就出了這樣的問題,折騰了半天終於發現其實可以用事件響應來解決這個問題。Qt中的事件響應函數是widget對象的一個方法,也就是說任何從QWidget繼承的類的對象都可以偵聽到諸如mousePressEvent()之類的事件。這樣一來,自定義一個從QPushButton繼承的類,然後在其中重寫mousePressEvent()方法,在該方法中emit()一個自定義的信號或者直接執行本來要由槽函數完成的動作即可。完整代碼如下:
- import sys
- from PyQt4 import QtCore, QtGui
- class ManyButton(QtGui.QDialog):
- t = ['button1', 'button2', 'button3', 'button4', 'button5']
- def __init__(self, parent=None):
- QtGui.QDialog.__init__(self, parent)
- self.pb = []
- vbox = QtGui.QVBoxLayout()
- for i in range(len(self.t)):
- self.pb.append(MyButton(self.t[i], self))
- QtCore.QObject.connect(self.pb[i], QtCore.SIGNAL("myslot(str)"), self.myslot)
- vbox.addWidget(self.pb[i])
- self.setLayout(vbox)
- self.resize(250, 150)
- def myslot(self, text):
- QtGui.QMessageBox.critical(self, "MessageShow", text, QtGui.QMessageBox.Ok)
- class MyButton(QtGui.QPushButton):
- def __init__(self, text, parent):
- QtGui.QPushButton.__init__(self, text, parent)
- def mouseReleaseEvent(self, event):
- self.emit(QtCore.SIGNAL("myslot(str)"), self.text())
- if __name__ == '__main__':
- app = QtGui.QApplication(sys.argv)
- mb = ManyButton()
- mb.show()
- sys.exit(app.exec_())
從中可以看出,其實我們通常關注並以爲好像是一個函數的信號本身只是一個字符串,比如上面的myslot(str),你必須將這個字符串傳入QtCore.SIGNAL()來生成信號。而信號定義的格式必須和emit時所帶的參數個數一致,比如
self.emit(QtCore.SIGNAL("myslot(str, str)"), self.text1(), self.text2())
信號後面就必須跟上兩個參數類型str,或者不寫也行,但不能寫成
self.emit(QtCore.SIGNAL("myslot(str)"), self.text1(), self.text2()), 這時會報錯。
直接寫成這樣也是能正常運行的
self.emit(QtCore.SIGNAL("myslot"), self.text1(), self.text2())
也許信號和槽的機制在由多個發射源向多個槽發射信號時會起到很大作用,不過一般來講,感覺這種機制還是太麻煩了一點。