3 votes

pyqt5 problème de QApplication.topLevelWidgets

Le but de "Qthread_check.py" est de trouver les fichiers MS Office.

et délivrer le résultat à "main_gui.py" Donc je dois trouver "main_gui.FindExcel"

Utilicé QApplication.topLevelWidgets pour trouver "main_gui.FindExcel".

cependant, TopLevelWidgets y isinstance ont des résultats différents de ceux que je pensais.

  • python 3.7
  • pyqt 5.12.2

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):
    [...]

Pourquoi est-ce que j'obtiens les résultats suivants ? Je ne sais pas pourquoi le self.main_gui est vide.

Je pense que c'est vrai. ==> <__main__.FindExcel object at 0x031FFC60> False

mais

# 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

S'il vous plaît, aidez-moi...

    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("")

sortie

<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

2voto

eyllanesc Points 79506

Explication :

Comme vous l'indiquez lors de l'exécution :

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

La sortie est :

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

On observe que la __classe__ de l'objet est __main__.FindExcel qui est différent de Main_gui.FindExcel que je soupçonne d'être à l'origine du problème.

Il semble que isinstance() ne considère pas le cas où la classe et l'instance sont créées dans des scopes différents. Par ailleurs, il s'agit peut-être d'un bug de PyQt5.

Solution :

Solution 1 :

Bien que je ne vois pas la nécessité d'utiliser topLevelWidgets() puisque vous pouvez passer directement le FindExcel :

# ...
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 :

Une autre option consiste à restructurer votre projet en déplaçant l'onglet if __name__ == "__main__": dans un autre fichier :

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

autre_fichier.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 :

Si vous souhaitez toujours utiliser topLevelWidgets(), vous pouvez utiliser le QMetaObject pour obtenir le nom de la classe et l'utiliser ensuite pour la filtrer :

# ...
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)
        # ...

Remarque : FindExcel est un topLevelWidget uniquement dans la construction car il fait alors partie du QTabWidget et n'est donc plus une fenêtre.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X