理解pyqt中的connectSlotsByName()有问题?

5 投票
6 回答
13748 浏览
提问于 2025-04-15 20:31

我不太明白connectSlotsByName()这个方法,它主要在pyuic4中使用。对于一个PyQt文件中只有一个类的情况,这个方法还好用,因为我们可以用self,它会和一个对象关联。但当我们尝试从不同的文件中使用多个类时,就会出现问题,这时候就需要用到connectSlotsByName()。我遇到了一些奇怪的情况。

我创建了一个堆叠窗口。

  • 我在上面放了第一个窗口,它有一个叫“下一步 >”的按钮。

  • 点击“下一步”后,它会隐藏当前窗口,并添加另一个窗口,这个窗口有一个“点击我”的按钮。

这里的问题是,第二个窗口中的“点击我”按钮的点击事件没有被捕捉到。这是我原始问题的一个简单示例。请帮帮我。

这是文件1(包含父堆叠窗口和它的第一页)。点击“下一步”后,它会添加第二页,第二页有“点击我”按钮,位于文件2中。

from PyQt4 import QtCore, QtGui

import file2

class Ui_StackedWidget(QtGui.QStackedWidget):

    def __init__(self,parent=None):

        QtGui.QStackedWidget.__init__(self,parent)

        self.setObjectName("self")
        self.resize(484, 370)
        self.setWindowTitle(QtGui.QApplication.translate("self", "stacked widget", None, QtGui.QApplication.UnicodeUTF8))

        self.createWidget1()

    def createWidget1(self):

        self.page=QtGui.QWidget()
        self.page.setObjectName("widget1")


        self.pushButton=QtGui.QPushButton(self.page)        
        self.pushButton.setGeometry(QtCore.QRect(150, 230, 91, 31))
        self.pushButton.setText(QtGui.QApplication.translate("self", "Next >", None, QtGui.QApplication.UnicodeUTF8))


        self.addWidget(self.page)


        QtCore.QMetaObject.connectSlotsByName(self.page)

          QtCore.QObject.connect(self.pushButton,QtCore.SIGNAL('clicked()'),self.showWidget2)


    def showWidget2(self):

        self.page.hide()

        obj=file2.widget2()
        obj.createWidget2(self)


if __name__ == "__main__":
    import sys
    app = QtGui.QApplication(sys.argv)
    ui = Ui_StackedWidget()
    ui.show()
    sys.exit(app.exec_()) 

这是文件2

from PyQt4 import QtGui,QtCore

class widget2():

    def createWidget2(self,parent):

        self.page = QtGui.QWidget()
        self.page.setObjectName("page")

        self.parent=parent

        self.groupBox = QtGui.QGroupBox(self.page)
        self.groupBox.setGeometry(QtCore.QRect(30, 20, 421, 311))
        self.groupBox.setObjectName("groupBox")
        self.groupBox.setTitle(QtGui.QApplication.translate("self", "TestGroupBox", None, QtGui.QApplication.UnicodeUTF8))

        self.pushButton = QtGui.QPushButton(self.groupBox)
        self.pushButton.setGeometry(QtCore.QRect(150, 120, 92, 28))
        self.pushButton.setObjectName("pushButton")
        self.pushButton.setText(QtGui.QApplication.translate("self", "Click Me", None, QtGui.QApplication.UnicodeUTF8))        

        self.parent.addWidget(self.page)
        self.parent.setCurrentWidget(self.page)

        QtCore.QMetaObject.connectSlotsByName(self.page)

        QtCore.QObject.connect(self.pushButton,QtCore.SIGNAL('clicked()'),self.printMessage)


    def printMessage(self):

        print("Hai")

虽然在这两个窗口(我指的是页面)中,

QtCore.QMetaObject.connectSlotsByName(self.page)

第二个对话框中的点击信号没有被处理。提前谢谢你。这可能是个初学者的问题。

6 个回答

2

你不需要调用 connectSlotsByName(),只要把那些代码删掉就行。

file2 中,调用 QtCore.QMetaObject.connectSlotsByName(self.page) 是想做这个:

QtCore.QObject.connect(self.pushButton, QtCore.SIGNAL('clicked()'), self.on_pushButton_clicked())

但是这样对你来说不管用,因为 self.on_pushBotton_clicked() 这个槽函数并没有定义。我觉得在 PyQt 中自己建立连接是最简单的... 我建议你把两个类中的 connectSlotsByName 的调用都删掉... 你根本不需要它。

另外,你的 wdiget1 类应该给它的按钮设置一个名字(最好是别叫“pushButton”,这样可以避免和 widget2 中的按钮混淆)。

4

一个更好的问题是:“为什么不直接使用新式的信号和槽呢?”它们要简单得多,而且不需要什么奇怪的命名规则:

from sys import argv, exit
from PyQt4 import QtCore, QtGui

class MyWidget(QtGui.QWidget):
    def __init__(self, parent=None):
        super(MyWidget, self).__init__(parent)

        self._layout = QtGui.QVBoxLayout()
        self.setLayout(self._layout)

        self._button = QtGui.QPushButton()
        self._button.setText('Click NOW!')
        self._layout.addWidget(self._button)

        self._button.clicked.connect(self._printMessage)

    @QtCore.pyqtSlot()
    def _printMessage(self):
        print("Hai")

if __name__ == "__main__":
    app = QtGui.QApplication(argv)

    main = MyWidget()
    main.show()
    exit(app.exec_())
5

首先,这里有一个最简单的工作示例:

from sys import argv, exit
from PyQt4 import QtCore, QtGui

class widget2(QtGui.QWidget):

    def __init__(self, args):
        self.app = MainApp(args)
        QtGui.QWidget.__init__(self)
        self.setObjectName('I')

        self._layout = QtGui.QVBoxLayout(self)
        self.setLayout(self._layout)

        self.pushButtoninWidget2 = QtGui.QPushButton(self)
        self.pushButtoninWidget2.setObjectName("pushButtoninWidget2")
        self.pushButtoninWidget2.setText('Click NOW!')
        self._layout.addWidget(self.pushButtoninWidget2)

        QtCore.QMetaObject.connectSlotsByName(self)

    @QtCore.pyqtSlot()
    def on_pushButtoninWidget2_clicked(self):
        print("Hai")

class MainApp(QtGui.QApplication):
    def __init__(self, args):
        QtGui.QApplication.__init__(self, args)

if __name__ == "__main__":
    main = widget2(argv)
    main.show()
    exit(main.app.exec_())

当你想通过名字连接槽时,必须给这些槽起个合适的名字,然后需要有人(比如moc、uic,或者你自己通过调用connectSlotsByName)来连接它们。这样的槽的正确命名方式是:“on_对象名_信号名”。

注意,如果我在示例中省略了@QtCore.pyqtSlot(),那么这个槽会对每个合适的重载执行一次(在这个例子中会执行两次)。

你确实需要直接调用connectSlotsByNames,因为在使用QT的C++时没有moc会为你处理这个,而你又不使用uic和.ui文件。如果你想隐式连接槽(我总是这样做,除了那些直接在.ui中连接的槽),最好使用更符合Python风格的语法:button.clicked.connect(self._mySlot)。

另外,可以看看这个链接:https://riverbankcomputing.com/static/Docs/PyQt5/signals_slots.html#connecting-slots-by-name

撰写回答