CGBCSH
CGBCSH

Reputation: 53

pyqt5 QApplication.topLevelWidgets issue

The purpose of "Qthread_check.py" is to find MS Office files.

and deliver the result to "main_gui.py" So I have to find "main_gui.FindExcel"

I used QApplication.topLevelWidgets to find "main_gui.FindExcel"

however, TopLevelWidgets and isinstance have different results than I thought.

main_gui.py

[... pyqt5 import...]
import sys
import os
import check_core
import Qthread_check

class FindExcel(QWidget):
    def __init__(self):
        super().__init__()
        self.label_width = 90
        self.core = check_core.CheckCore()
        self.th = Qthread_check.Search()
        self.run_pw = Qthread_check.SetPW()
        self.initro_msg = "hihi"
        self.init_gui()

    def init_gui(self):
        # intro msg
        intro_label = QLabel()
        intro_label.setText(self.initro_msg)

    [... Widget And layout ...]

class MainWin(QMainWindow):
    def __init__(self):
        super().__init__()
        self.main_ui_layout()

    def main_ui_layout(self):
        # create QTabWidget and add Tab..
        tab_w = QTabWidget()
        tab_w.addTab(GeneralCheck(), "General Check")
        tab_w.addTab(FindExcel(), "MS Office PW")

        self.setWindowTitle("T.T")

        # Set main Central Widget
        self.setCentralWidget(tab_w)
        self.setGeometry(100, 100, 900, 900)
        self.show()

if __name__ == "__main__":
    app = QApplication(sys.argv)
    start = MainWin()
    sys.exit(app.exec_())

Qthread_check.py

from PyQt5.QtCore import QThread, pyqtSignal, QWaitCondition, QMutex
from PyQt5.QtWidgets import QApplication
import Main_gui

class Search(QThread):
    file_changed = pyqtSignal(str)
    done_msg = pyqtSignal(dict)

    def __init__(self):
        QThread.__init__(self)
        self.cond = QWaitCondition()
        self.mutex = QMutex()
        self._status = False
        for w in QApplication.topLevelWidgets():
            print(w, isinstance(w, Main_gui.FindExcel)) #<<=== Why is this no match?
        print("")

        self.main_gui = [w for w in QApplication.topLevelWidgets() if isinstance(w, Main_gui.FindExcel)][0] #<== self.main_gui = []
    def __del__(self):
        self.wait()

    def run(self):
    [...]

Why do i get the following results? I don't know why the self.main_gui is empty.

I think this is true. ==> <__main__.FindExcel object at 0x031FFC60> False

but..

# for w in QApplication.topLevelWidgets():
#           print(w, isinstance(w, Main_gui.FindExcel))
#        print("")

# result from above

<__main__.FindExcel object at 0x031FFC60> False <==== why?
<PyQt5.QtWidgets.QTabWidget object at 0x031FF990> False
<__main__.MainWin object at 0x031FF7B0> False
<PyQt5.QtWidgets.QMenu object at 0x031FF8F0> False
<PyQt5.QtWidgets.QMenu object at 0x031FF940> False

Process finished with exit code 1

plz help...

    def __init__(self):
        QThread.__init__(self)
        self.cond = QWaitCondition()
        self.mutex = QMutex()
        self._status = False
        for w in QApplication.topLevelWidgets():
            print(w, w.__class__, Main_gui.FindExcel, isinstance(w, Main_gui.FindExcel))
        print("")

output

<PyQt5.QtWidgets.QMenu object at 0x0383F940> <class 'PyQt5.QtWidgets.QMenu'> <class 'Main_gui.FindExcel'> False
<PyQt5.QtWidgets.QTabWidget object at 0x0383F990> <class 'PyQt5.QtWidgets.QTabWidget'> <class 'Main_gui.FindExcel'> False
<PyQt5.QtWidgets.QMenu object at 0x0383F8F0> <class 'PyQt5.QtWidgets.QMenu'> <class 'Main_gui.FindExcel'> False
<__main__.FindExcel object at 0x0383FC60> <class '__main__.FindExcel'> <class 'Main_gui.FindExcel'> False
<__main__.MainWin object at 0x0383F7B0> <class '__main__.MainWin'> <class 'Main_gui.FindExcel'> False

Upvotes: 3

Views: 711

Answers (1)

eyllanesc
eyllanesc

Reputation: 243945

Explanation:

As you indicate when executing:

print(w, w.__class__, Main_gui.FindExcel, isinstance(w, Main_gui.FindExcel))

The output is:

# ...
<__main__.FindExcel object at 0x0383FC60> <class '__main__.FindExcel'> <class 'Main_gui.FindExcel'> False
# ...

It is observed that the __class__ of the object is __main__.FindExcel which is different from Main_gui.FindExcel that I suspect is generating the problem.

It seems that isinstance() does not consider the case that the class and instance are created in different scopes. Also, maybe it's a PyQt5 bug.

Solution:

Solution 1:

Although I don't see the need to use topLevelWidgets() since you can pass the FindExcel directly:

# ...
class FindExcel(QWidget):
    def __init__(self):
        super().__init__()
        self.label_width = 90
        self.core = check_core.CheckCore()
        self.th = Qthread_check.Search(self)
        # ...
# ...
class Search(QThread):
    file_changed = pyqtSignal(str)
    done_msg = pyqtSignal(dict)

    def __init__(self, main_gui):
        QThread.__init__(self)
        self.cond = QWaitCondition()
        self.mutex = QMutex()
        self._status = False
        self.main_gui = main_gui
        # ...

Solution 2:

Another option is to restructure your project by moving the if __name__ == "__main__": code to another file:

|-- another_file.py
|-- Main_gui.py
`-- Qthread_check.py

another_file.py

from PyQt5.QtWidgets import QApplication

from Main_gui import MainWin


if __name__ == "__main__":
    app = QApplication(sys.argv)
    start = MainWin()
    sys.exit(app.exec_())

Solution 3:

Or if you still want to use topLevelWidgets() then you can use the QMetaObject to get the class name and then use it to filter it:

# ...
class Search(QThread):
    file_changed = pyqtSignal(str)
    done_msg = pyqtSignal(dict)

    def __init__(self):
        QThread.__init__(self)
        self.cond = QWaitCondition()
        self.mutex = QMutex()
        self._status = False
        self.main_gui = None
        for w in QtWidgets.QApplication.topLevelWidgets():
            if w.metaObject().className() == "FindExcel":
                self.main_gui = w 
                break
        print(self.main_gui)
        # ...

Note: FindExcel is a topLevelWidget only in the construction because then it is part of the QTabWidget and therefore it is no longer a window.

Upvotes: 2

Related Questions