Сигнал PyQt5 излучается между двумя классами

#python #pyqt5

#python #pyqt5

Вопрос:

Я просто пытаюсь отправить сигнал из класса A в класс B, но это не работает, я не вижу печати. Я, конечно, делаю что-то не так, но я знаю, что. Вот краткий код, который покажет вам проблему, спасибо.

 from PyQt5 import QtWidgets, QtCore
import sys


class B(object):
    def __init__(self, parent=None):
        super(B, self).__init__(parent)
        self._initSlot()

    def _initSlot(self):
        a = A()
        a.assetSelectionChanged.connect(self._doSomething)

    @QtCore.pyqtSlot()
    def _doSomething(self):
        print('do something')


class A(QtWidgets.QWidget):

    assetSelectionChanged = QtCore.pyqtSignal()

    def __init__(self, parent=None):
        super(A, self).__init__(parent)
        self._initUI()

    def _initUI(self):

        self.treeWidgetAssets = QtWidgets.QTreeWidget()
        for i in range(1, 11, 1):
            QtWidgets.QTreeWidgetItem(self.treeWidgetAssets, [str(i)])

        self.mainLayout = QtWidgets.QVBoxLayout()
        self.setLayout(self.mainLayout)
        self.mainLayout.addWidget(self.treeWidgetAssets)
        self.treeWidgetAssets.itemSelectionChanged.connect(self.onAssetSelectionChanged)

    def onAssetSelectionChanged(self):
        self.assetSelectionChanged.emit()


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    dlg = A()
    dlg.show()
    sys.exit(app.exec_())
 

Ответ №1:

С этим кодом возникают различные проблемы:

  • Экземпляр B никогда не создается
  • прямые object подклассы не требуют вызова super().__init__() , и даже если вы это сделаете, вам, конечно, не следует добавлять произвольные аргументы, поскольку они не будут приняты, что приведет к сбою
  • B должен наследовать от QObject
  • Новый экземпляр не должен быть объявлен в _initSlot : он уже существует, и тот, который вы там создаете, будет немедленно собран мусором, потому что его ссылка является локальной и будет удалена сразу после _initSlot возврата
 class B(QtCore.QObject):
    def __init__(self, parent=None):
        super(B, self).__init__(parent)
        self._initSlot()

    def _initSlot(self):
        if self.parent():
            self.parent().assetSelectionChanged.connect(self._doSomething)

    @QtCore.pyqtSlot()
    def _doSomething(self):
        print('do something')


class A(QtWidgets.QWidget):

    assetSelectionChanged = QtCore.pyqtSignal()

    def __init__(self, parent=None):
        super(A, self).__init__(parent)
        self._initUI()
        self.b = B(self)

    # ...
 

Комментарии:

1. Спасибо за вашу помощь и объяснение!

Ответ №2:

Как вариант. Я отметил строки, которые я изменил для вас.

 import sys
from PyQt5 import QtWidgets, QtCore


class B(QtCore.QObject):
    def __init__(self, parent=None):
        super(B, self).__init__(parent)
#        self._initSlot()
#    def _initSlot(self):
#        a = A()
#        a.assetSelectionChanged.connect(self._doSomething)

    @QtCore.pyqtSlot(str)
    def _doSomething(self, text):
        print(f'do something: clicked -> {text}')


class A(QtWidgets.QWidget):
    assetSelectionChanged = QtCore.pyqtSignal(str)                       # 1.   str 

    def __init__(self, parent=None):
        super(A, self).__init__(parent)
        
        self._initUI()
        
        self.b = B(self)                                                 # !!!
        self.assetSelectionChanged[str].connect(self.b._doSomething)     # 3.   str 

    def _initUI(self):
        self.treeWidgetAssets = QtWidgets.QTreeWidget()
        for i in range(1, 11, 1):
            QtWidgets.QTreeWidgetItem(self.treeWidgetAssets, [str(i)])

        self.mainLayout = QtWidgets.QVBoxLayout()
        self.setLayout(self.mainLayout)
        self.mainLayout.addWidget(self.treeWidgetAssets)
        self.treeWidgetAssets.itemSelectionChanged.connect(self.onAssetSelectionChanged)

    def onAssetSelectionChanged(self):
        text = self.treeWidgetAssets.selectedItems()[0].text(0)
        self.assetSelectionChanged.emit(text)                            # 2.   text 


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    dlg = A()
    dlg.show()
    sys.exit(app.exec_())
 

введите описание изображения здесь

Комментарии:

1. Поскольку для сигнала существует только одна перегрузка, нет необходимости указывать его с [str] помощью .

2. Спасибо за вашу помощь! Излучение сигнала с помощью str определенно упростит мой код 🙂