PyQt5. Возможна ли автоматическая генерация и обработка сигнала при создании экземпляра класса?

Помогите реализовать идею. Код предельно упрощен, но суть будет ясна.
После поиска объектов запрашивается детальная информация по выбранному объекту в момент создания экзепляра класса DetailBox через вызов функции self.get_obj_detail().

Дальше логика простая: если информация найдена, то отображается DetailBox с формами для визуализации информации.
Если же информация не найдена, создается сигнал, который после обработки вместо DetailBox покажет Error404Box.

И вроде все нормально, но работает только по клику если self.get_object_detail() прикрутить к какому-то QPushButton.

Как я понял emit возможен только для QObject. \Как это можно обойти или я рою не в ту сторону?

main.py:

import sys
from PyQt5.QtCore import Qt, pyqtSlot, pyqtSignal
from PyQt5.QtWidgets import (
    QApplication, QMainWindow,
    QWidget, QGroupBox, QGridLayout, QHBoxLayout, QTabWidget, QLabel, QLineEdit, QPushButton, QSpacerItem, QSizePolicy
)


class Error404Box(QGroupBox):

    def __init__(self, parent=None):

        super(Error404Box, self).__init__(parent)
        self.setTitle("&Error404")

        g_layout = QGridLayout(self)
        g_layout.addWidget(QLabel("Error404"), 0, 0)


class DetailBox(QGroupBox):

    error_signal = pyqtSignal(str, str)

    def __init__(self, object_pkid: str, parent=None):

        super(DetailBox, self).__init__(parent)

        self.object_pkid = object_pkid

        self.get_obj_detail()    # Так не работает

        self.setTitle("&Obj Detail")

        self.detail_tabs = QTabWidget()
        self.tab_detail = QWidget()

        self.detail_tabs.addTab(self.tab_detail, "Obj Detail")
        self.detail_tabs.setStyleSheet("""
            QTabBar::tab {
                height: 35px;
            }
        """)

        self.copy_button = QPushButton("Copy")
        self.copy_button.setStyleSheet("""
            QPushButton {
                min-width: 70px;
                min-height: 20px;
                margin: 0px;
            }
        """)
        self.delete_button = QPushButton("Delete")
        self.delete_button.setStyleSheet("""
            QPushButton {
                min-width: 70px;
                min-height: 20px;
                margin: 0px;
            }
        """)
        self.delete_button.clicked.connect(self.get_obj_detail)  # В этом случае все работает корректно

        h_layout = QHBoxLayout(self)
        h_layout.addWidget(self.copy_button)
        h_layout.addWidget(self.delete_button)

        obj_events_buttons = QWidget()
        obj_events_buttons.setLayout(h_layout)

        self.detail_tabs.setCornerWidget(obj_events_buttons, Qt.TopRightCorner)

        g_layout = QGridLayout(self)
        g_layout.addWidget(self.detail_tabs)

        self.tabs_forms_build()

    def tabs_forms_build(self):

            self.pattern_line = QLineEdit()
            self.description_line = QLineEdit()
            self.v_spacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)

            self.tab_detail.g_layout = QGridLayout(self)

            self.tab_detail.g_layout.addWidget(QLabel(
                "<span style=\" font-size:10pt; font-weight:600;\">Object Definition</span>"), 0, 0, 1, 4)
            self.tab_detail.g_layout.addWidget(QLabel("Object"), 1, 0, 1, 1)
            self.tab_detail.g_layout.addWidget(QLabel("Description"), 2, 0, 1, 1)
            self.tab_detail.g_layout.addWidget(QLabel("..."), 3, 0, 1, 1)
            self.tab_detail.g_layout.addWidget(self.pattern_line, 1, 1, 1, 4)
            self.tab_detail.g_layout.addWidget(self.description_line, 2, 1, 1, 4)
            self.tab_detail.g_layout.addWidget(QLabel("..."), 3, 1, 1, 4)
            self.tab_detail.g_layout.addItem(self.v_spacer, 4, 2, 1, 1)

            self.tab_detail.setLayout(self.tab_detail.g_layout)

    @ pyqtSlot()
    def get_obj_detail(self):

        self.resp = None   # some_fx(self.object_pkid) - backend для запроса информации, возвращает Dict or None
        if not self.resp:
            self.error_signal.emit("Error404", self.object_pkid)
        print(self.error_signal)


class MainApp(QMainWindow):
    def __init__(self):
        super().__init__()
        self.centralWidget = QWidget()
        self.setCentralWidget(self.centralWidget)

        self.resize(800, 500)
        self.setWindowTitle("Device Search")

        self.result_box = DetailBox("000000-000-000000")
        self.result_box.error_signal.connect(self.show_error)

        self.layout = QGridLayout(self.centralWidget)
        self.layout.addWidget(self.result_box, 0, 0, 1, 1)

    @pyqtSlot(str, str)
    def show_error(self, error_type, pkid):

        self.result_box.deleteLater()
        print("GOT SIGNAL", error_type, pkid)
        self.result_box = Error404Box()
        self.layout.addWidget(self.result_box, 0, 0, 1, 1)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = MainApp()
    ex.show()
    sys.exit(app.exec_())

Ответы (1 шт):

Автор решения: S. Nick

С сигналом все нормально, т.к. QGroupBox наследуется от QObject.

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

В том месте где вы вызываете, не срабатывает потому что объект result_box еще не сформирован.

я отметил для вас два варианта как и где вызывать get_obj_detail(), чтобы работало правильно.

import sys
from PyQt5.QtCore import Qt, pyqtSlot, pyqtSignal, QTimer
from PyQt5.QtWidgets import (
    QApplication, QMainWindow,
    QWidget, QGroupBox, QGridLayout, QHBoxLayout, QTabWidget, QLabel, QLineEdit, QPushButton, QSpacerItem, QSizePolicy
)


class Error404Box(QGroupBox):
    def __init__(self, parent=None):
        super(Error404Box, self).__init__(parent)
        self.setTitle("&Error404")

        g_layout = QGridLayout(self)
        g_layout.addWidget(QLabel("Error404"), 0, 0)


class DetailBox(QGroupBox):
    error_signal = pyqtSignal(str, str)

    def __init__(self, object_pkid: str, parent=None):
        super(DetailBox, self).__init__(parent)
        self.setTitle("&Obj Detail")
        
        self.object_pkid = object_pkid

#        self.get_obj_detail()                          # Так не работает
# +++ так работает !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 
        QTimer.singleShot(10, self.get_obj_detail)

        self.detail_tabs = QTabWidget()
        self.tab_detail = QWidget()

        self.detail_tabs.addTab(self.tab_detail, "Obj Detail")
        self.detail_tabs.setStyleSheet("""
            QTabBar::tab {
                height: 35px;
            }
        """)
        self.copy_button = QPushButton("Copy")
        self.copy_button.setStyleSheet("""
            QPushButton {
                min-width: 70px;
                min-height: 20px;
                margin: 0px;
            }
        """)
        self.delete_button = QPushButton("Delete")
        self.delete_button.setStyleSheet("""
            QPushButton {
                min-width: 70px;
                min-height: 20px;
                margin: 0px;
            }
        """)
        # В этом случае все работает корректно
        self.delete_button.clicked.connect(self.get_obj_detail)  

# !!!
        h_layout = QHBoxLayout()                              # - (self)
        h_layout.addWidget(self.copy_button)
        h_layout.addWidget(self.delete_button)

        obj_events_buttons = QWidget()
        obj_events_buttons.setLayout(h_layout)

        self.detail_tabs.setCornerWidget(obj_events_buttons, Qt.TopRightCorner)

        g_layout = QGridLayout(self)
        g_layout.addWidget(self.detail_tabs)

        self.tabs_forms_build()

    def tabs_forms_build(self):
            self.pattern_line = QLineEdit()
            self.description_line = QLineEdit()
            self.v_spacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)

            self.tab_detail.g_layout = QGridLayout(self)

            self.tab_detail.g_layout.addWidget(QLabel(
                "<span style=\" font-size:10pt; font-weight:600;\">Object Definition</span>"), 0, 0, 1, 4)
            self.tab_detail.g_layout.addWidget(QLabel("Object"), 1, 0, 1, 1)
            self.tab_detail.g_layout.addWidget(QLabel("Description"), 2, 0, 1, 1)
            self.tab_detail.g_layout.addWidget(QLabel("..."), 3, 0, 1, 1)
            self.tab_detail.g_layout.addWidget(self.pattern_line, 1, 1, 1, 4)
            self.tab_detail.g_layout.addWidget(self.description_line, 2, 1, 1, 4)
            self.tab_detail.g_layout.addWidget(QLabel("..."), 3, 1, 1, 4)
            self.tab_detail.g_layout.addItem(self.v_spacer, 4, 2, 1, 1)

            self.tab_detail.setLayout(self.tab_detail.g_layout)

    @pyqtSlot()
    def get_obj_detail(self):
        # some_fx(self.object_pkid) - 
        # backend для запроса информации, возвращает Dict or None
        self.resp = None   
        if not self.resp:
            self.error_signal.emit("Error404", self.object_pkid)
        print(f'def get_obj_detail(self): {self.error_signal}')


class MainApp(QMainWindow):
    def __init__(self):
        super().__init__()
        self.resize(800, 500)
        self.setWindowTitle("Device Search")
        
        self.centralWidget = QWidget()
        self.setCentralWidget(self.centralWidget)

        self.result_box = DetailBox("000000-000-000000")
        self.result_box.error_signal.connect(self.show_error)

        self.layout = QGridLayout(self.centralWidget)
        self.layout.addWidget(self.result_box, 0, 0, 1, 1)

# +++ или здесь !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!       
#        self.result_box.get_obj_detail()

    @pyqtSlot(str, str)
    def show_error(self, error_type, pkid):
        self.result_box.deleteLater()
        print("GOT SIGNAL", error_type, pkid)
        self.result_box = Error404Box()
        self.layout.addWidget(self.result_box, 0, 0, 1, 1)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = MainApp()
    ex.show()
    sys.exit(app.exec_())
→ Ссылка