0

为什么以下示例不起作用?

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 结构(如图 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)

有用。我对此仍然不满意。

4

1 回答 1

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()

编辑:澄清 Qt 的 MVC

所以上面的这个例子实际上并没有动态加载用户界面并创建一个控制器/视图分离。在这里展示有点困难。最好通过一些基于 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()
于 2012-09-04T20:15:10.553 回答