2012-09-04 24 views
0

爲什麼下面的例子沒有工作?如何在PyQt4中連接控制器的信號? (iOS中像PyQt4中的MVC結構)

from PyQt4 import QtGui 
import sys 

class TestView(QtGui.QWidget): 

    def __init__(self): 
     super(TestView, self).__init__() 
     self.initUI() 

    def initUI(self): 
     self.btn = QtGui.QPushButton('Button', self) 
     self.btn.resize(self.btn.sizeHint()) 
     self.btn.move(50, 50) 

class TestViewController(): 

    def __init__(self, view): 
     view.btn.clicked.connect(self.buttonClicked) 
     view.show() 

    def buttonClicked(self): 
     print 'clicked' 

def main(): 
    app = QtGui.QApplication(sys.argv) 
    view = TestView() 
    TestViewController(view) 
    app.exec_() 

if __name__ == '__main__': 
    main() 

的例子被認爲代表一個MVC結構(像在Figure 4 - 無模型),其中控制器(TestViewController)接收到視圖(TestView)的引用和點擊的信號從連接查看按鈕view.btn到其功能self.buttonClicked

我確定行view.btn.clicked.connect(self.buttonClicked)被執行,但顯然它沒有效果。有誰知道如何解決這個問題?


更新(可怕的解決方案):

在這個例子中,如果我更換線

view.btn.clicked.connect(self.buttonClicked) 

view.clicked = self.clicked 
view.btn.clicked.connect(view.clicked) 

它的工作原理。我對此仍然不滿意。

回答

2

它不工作的原因是因爲控制器類正在垃圾收集之前,你可以點擊任何東西。

當你設置view.clicked = self.clicked時,你實際上正在做的是讓控制器中的一個對象保持在視圖對象上,這樣它永遠不會被清理 - 這不是真正的解決方案。

如果您將控制器存儲到一個變量,它將保護它免受收集。

所以,如果你改變你的代碼,上面寫着:

ctrl = TestViewController(view) 

您將所有設置。

這就是說 - 你究竟在做什麼,我不確定......看起來你試圖爲Qt設置一個MVC系統 - 但Qt已經有一個相當不錯的系統, Qt Designer將界面組件從控制器邏輯(QWidget子類)分離爲UI(視圖/模板)文件。同樣,我不知道你正在嘗試做的,這可能是它的一個愚蠢的縮小版本,但是我建議你做這一切一類,像這樣:

from PyQt4 import QtGui 
import sys 

class TestView(QtGui.QWidget): 

    def __init__(self): 
     super(TestView, self).__init__() 
     self.initUI() 

    def initUI(self): 
     self.btn = QtGui.QPushButton('Button', self) 
     self.btn.resize(self.btn.sizeHint()) 
     self.btn.move(50, 50) 
     self.btn.clicked.connect(self.buttonClicked) 

    def buttonClicked(self): 
     print 'clicked' 

def main(): 
    app = QtGui.QApplication(sys.argv) 
    view = TestView() 
    view.show() 
    app.exec_() 

if __name__ == '__main__': 
    main() 

編輯:澄清的MVC的Qt

因此,上面這個例子實際上並沒有實際加載ui並創建控制器/視圖分離。這裏有點難以展示。最好通過一些基於Qt/Designer的示例/教程來工作 - 我在這裏有一個http://bitesofcode.blogspot.com/2011/10/introduction-to-designer.html,但很多可以在網上找到。

簡短的回答是,你的loadUi方法可以用PyQt4代替。UIC動態負載(也有許多不同的方式來設置了),使得您的代碼最終讀取這樣的事情:

from PyQt4 import QtGui 
import PyQt4.uic 
import sys 

class TestController(QtGui.QWidget): 

    def __init__(self): 
     super(TestController, self).__init__() 

     # load view 
     uifile = '/path/to/some/widget.ui' 
     PyQt4.uic.loadUi(uifile, self) 

     # create connections (assuming there is a widget called 'btn' that is loaded) 
     self.btn.clicked.connect(self.buttonClicked) 

    def buttonClicked(self): 
     print 'clicked' 

def main(): 
    app = QtGui.QApplication(sys.argv) 
    view = TestController() 
    view.show() 
    app.exec_() 

if __name__ == '__main__': 
    main() 

編輯2:存儲UI引用

如果它更容易爲了可視化這個概念,你還可以存儲對生成的UI對象的引用:

from PyQt4 import QtGui 
import PyQt4.uic 
import sys 

class TestController(QtGui.QWidget): 

    def __init__(self): 
     super(TestController, self).__init__() 

     # load a view from an external template 
     uifile = '/path/to/some/widget.ui' 
     self.ui = PyQt4.uic.loadUi(uifile, self) 

     # create connections (assuming there is a widget called 'btn' that is loaded) 
     self.ui.btn.clicked.connect(self.buttonClicked) 

    def buttonClicked(self): 
     print 'clicked' 

def main(): 
    app = QtGui.QApplication(sys.argv) 
    view = TestController() 
    view.show() 
    app.exec_() 

if __name__ == '__main__': 
    main() 
+0

「將控制器分配給變量」部分很好!雖然,我想指出,你的答案的第二部分似乎有點矛盾。首先,這只是我試圖實現的一個工作示例。其次,你推薦使用QtDesigner將View與邏輯分開,但是你會展示一個例子,其中程序的所有元素都在一個類中。請注意,在我的示例中,TestView類只包含UI元素,而不包含一行「邏輯」。下一步是通過控制器將「clicked」調用轉發給Model的API。 – freitass

+0

是的,你是對的 - 我根本沒有改變你的代碼,在這裏展示如何使用設計器並不那麼容易。這裏有一個教程:http://bitesofcode.blogspot.com/2011/10/introduction-to-designer.html –

+0

對,我明白你的觀點。但是因爲你的連接是在視圖內進行的,所以它必須有一個對其控制器對象的引用,對嗎?這可以通過在控制器內建立連接來解決,這將是視圖和模型之間的粘合劑。 – freitass