Проблема с пониманием connectSlotsByName() в pyqt?

Я не мог понять метод connectSlotsByName(), который преимущественно используется pyuic4. Поскольку класс один в файле PyQt, это нормально, поскольку мы можем использовать self, который будет связан с одним объектом повсюду. Но когда мы попытаемся чтобы использовать различные классы из разных файлов, возникает проблема и необходимость использования connectSlotsByName(). Вот что я обнаружил, что странно..

Я создал виджет с накоплением..

  • Я разместил на нем свой первый виджет. На нем есть кнопка «Далее >».

  • При нажатии «Далее» он скрывает текущий виджет и добавляет другой виджет с кнопкой «щелкни меня».

Проблема здесь в том, что событие щелчка для кнопки «нажми меня» за секунду не фиксируется. Это минимальный пример, который я могу привести для моей исходной проблемы. Пожалуйста, помогите мне..

Это файл № 1.. (у которого есть родительский сложенный виджет и его первая страница). При нажатии «Далее» добавляется вторая страница с кнопкой «clickme» в файле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)

сигнал щелчка во втором диалоговом окне не обрабатывается. Заранее спасибо.. Может быть вопрос новичка..


person Jeba    schedule 17.03.2010    source источник


Ответы (6)


Лучше задать вопрос: «Почему бы просто не использовать сигналы и слоты нового стиля?». Они намного проще и не требуют никаких странных соглашений об именах:

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_())
person Mark Visser    schedule 07.04.2010
comment
Ответ будет таким: если вы используете файлы пользовательского интерфейса, скомпилированные в Qt uic, вам нужно только позаботиться об имени функции и о том, что оно оформлено. Если у вас много виджетов, было бы неплохо просто установить имя функции и BaM! Весь код подключения сокращен до 1 строки... Таким образом^ (вам даже не нужно его украшать!), но у вас будет 2 места для изменения всякий раз, когда что-то меняется. - person ewerybody; 19.01.2021

Во-первых, вот минимальный рабочий пример:

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_PyQtObjectName_PyQtSignalName.

Обратите внимание, что если бы я пропустил @QtCore.pyqtSlot() в примере, слот выполнялся бы один раз для каждой соответствующей перегрузки (в данном случае дважды).

Вам НЕОБХОДИМО вызвать connectSlotsByNames напрямую, потому что нет moc, который сделает это за вас, когда вы используете QT в C++, и вы не используете файлы uic и .ui. Если вы хотите подключать слоты неявно (я всегда так делаю, за исключением слотов, подключенных напрямую в .ui), вам лучше использовать более питоновский синтаксис: button.clicked.connect(self._mySlot).

И взгляните на https://riverbankcomputing.com/static/Docs/PyQt5/signals_slots.html#connecting-slots-by-name

person Maxim Popravko    schedule 23.03.2010

Вам не нужно вызывать 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 (предпочтительно что-то другое, чем "pushButton", чтобы избежать путаницы с кнопкой в ​​widget2).

person Jason Coon    schedule 17.03.2010

Большое спасибо jcoon за ваш ответ.. Но после очень долгого биения головой о стену я нашел решение..

Проблема была..

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

вместо объекта..

person Jeba    schedule 22.03.2010

Вот код @MarkVisser QT4, обновленный до QT5:

from sys import argv, exit
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import QApplication, QWidget


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

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

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

        self._button.clicked.connect(self._print_message)

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


if __name__ == "__main__":
    app = QApplication(argv)
    main = MyWidget()
    main.show()
    exit(app.exec_())
person Mike Slinn    schedule 09.11.2020

Еще один минимальный рабочий пример с Qt для Python, также известным как PySide2/6.
Ключевые ингредиенты:

  • виджет для подключения ДОЛЖЕН иметь .setObjectName
  • функция для подключения ДОЛЖНА быть украшена @QtCore.Slot()
  • оба объекта (функция И виджет) ДОЛЖНЫ быть членами переданного объекта (здесь self)
from PySide2 import QtCore, QtWidgets
# or from PySide6 import QtCore, QtWidgets


class Widget(QtWidgets.QWidget):
    def __init__(self):
        super(Widget, self).__init__()
        layout = QtWidgets.QVBoxLayout(self)
        self.button = QtWidgets.QPushButton(self)
        self.button.setObjectName('button')
        self.button.setText('Click Me!')
        layout.addWidget(self.button)

        QtCore.QMetaObject.connectSlotsByName(self)

    @QtCore.Slot()
    def on_button_clicked(self):
        print(f'Hai from {self.sender()}')


if __name__ == '__main__':
    app = QtWidgets.QApplication([])
    main = Widget()
    main.show()
    app.exec_()

Я действительно не мог сделать его меньше ????

person ewerybody    schedule 19.01.2021