在 Python 的 Pyside2 上显示应用程序后,我们如何刷新事件?

How can we refresh events after showing application on Pyside2 for Python?

我想用 Pyside2 GUI 创建一个 python 软件。我使用 Qt Designer 设计了 ​​GUI,并生成了一个 .ui 文件,我将其加载到我的 Python 脚本中。 在显示应用程序具有模块化 class 之后,我正在寻找一种放置 "event listener" 的方法,我不想将我所有的 connect() 方法都放在 class init 中.

我无法将 class 初始化和 self.show() 分开,所以我需要将这些行放在 .show() 方法之后:

    self.XMLButtonFolder = QPushButton(...)
    self.XMLButtonFolder.clicked.connect(self.method)

这是我的 class init(是的,它是一个线程):

    def __init__(self):
        self.app = QApplication([])
        loader = QUiLoader()
        print("Loading mainwindow.ui file")
        self.window = loader.load(QFile("mainwindow.ui"))
        if self.window is not None:
            print("mainwindow.ui loaded")
        else:
            print("Error loading mainwindow.ui")


        # XML
        self.XMLButtonFolder = self.window.findChild(QPushButton, "XMLButtonFolder")
        self.XMLButtonFolder.clicked.connect(self.openBoxFolder_XML)

        # Report
        self.ReportButtonFolder = self.window.findChild(QPushButton, "ReportButtonFolder")
        self.ReportButtonFolder.clicked.connect(self.openBoxFolder_Report)

        # If you uncomment the following line, the eventListener() method will be correctly called ..
        # self.window.findChild(QPushButton, "XMLGenerateReport").clicked.connect(self.eventListener)
        self.window.show()
        sys.exit(self.app.exec_())

在同一个 class 中,我添加了一个函数来创建新连接并检测来自另一个按钮的 "clicked" 事件

    def addEventListener(self, qtype, qname):
        self.eventlistenerresult = False
        self.__widget.findChild(self.elementtype[qtype], qname).clicked.connect(self.eventListener)

这是主程序:

myapp = Application()
myapp.addEventListener("Button", "XMLGenerateReport")

问题是我没有刷新window的方法,所以事件没有触发(运行()方法中设置的有效,但之后设置的无效)

我希望能够触发名为 "XMLGenerateReport" 的按钮的事件,即使在 .show()

之后调用了 .connect() 方法

我们该怎么做?

可重现的例子:(是的,我知道,在这个例子中它只能在事件中出现,但对我来说没问题) main.py:

from Window import Application
import threading

def GenerateDocument():
    print("Document generated !")

class ProgramThread(threading.Thread):
    def run(self):
        while not myapp.getEventListenerStatus():
            time.sleep(1.0)
            print("checking Generate button")
        GenerateDocument()


myapp = Application()
myapp.addEventListener("Button", "XMLGenerateReport")
thr = ProgramThread()
thr.start()

Window.py:

from PySide2.QtUiTools import QUiLoader
from PySide2.QtWidgets import QApplication, QPushButton, QFileDialog, QWidget, QLineEdit
from PySide2.QtCore import QFile
from PySide2 import QtWidgets
import sys
import threading


class Application(QtWidgets.QWidget):
    elementtype = {
        "Button": QPushButton
    }

    eventlistenerresult = None

    app = None
    __widget = None

    XMLButtonFolder = None
    ReportButtonFolder = None

    def __init__(self):
        self.app = QApplication([])
        loader = QUiLoader()
        print("Loading mainwindow.ui file")
        self.window = loader.load(QFile("mainwindow.ui"))
        if self.window is not None:
            print("mainwindow.ui loaded")
        else:
            print("Error loading mainwindow.ui")


        # XML
        self.XMLButtonFolder = self.window.findChild(QPushButton, "XMLButtonFolder")
        self.XMLButtonFolder.clicked.connect(self.openBoxFolder_XML)

        # Report
        self.ReportButtonFolder = self.window.findChild(QPushButton, "ReportButtonFolder")
        self.ReportButtonFolder.clicked.connect(self.openBoxFolder_Report)

        # If you uncomment the following line, the eventListener() method will be correctly called ..
        # self.window.findChild(QPushButton, "XMLGenerateReport").clicked.connect(self.eventListener)
        self.window.show()
        sys.exit(self.app.exec_())

    def openBoxFolder_XML(self):
        # Works
        dialog = QtWidgets.QFileDialog(self.window)
        dialog.setFileMode(QFileDialog.ExistingFile)
        path, _ = dialog.getOpenFileName(self.window, 'Sélectionnez un fichier .xml', filter='XML files (*.xml)')
        self.window.findChild(QLineEdit, "XMLInputFolder").setText(path)

    def openBoxFolder_Report(self):
        # Works
        dialog = QFileDialog(self.window)
        dialog.setFileMode(QFileDialog.Directory)
        path, _ = dialog.getOpenFileName()
        self.window.findChild(QLineEdit, "ReportInputFolder").setText(path)

    def addEventListener(self, qtype, qname):
        self.eventlistenerresult = False
        self.window.findChild(self.elementtype[qtype], qname).clicked.connect(self.eventListener)

    def eventListener(self):
        # Never called
        print("clicked !")
        self.eventlistenerresult = True

    def getEventListenerStatus(self):
        return self.eventlistenerresult  # Return true if the button handled by addEventListener has been pressed

mainwindow.ui:

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>696</width>
    <height>222</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <widget class="QLineEdit" name="XMLInputFolder">
    <property name="geometry">
     <rect>
      <x>40</x>
      <y>60</y>
      <width>531</width>
      <height>20</height>
     </rect>
    </property>
    <property name="whatsThis">
     <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Entrez ici le dossier où ce trouve le fichier .xml à utiliser&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
    </property>
   </widget>
   <widget class="QLabel" name="XMLInputFolderLabel">
    <property name="geometry">
     <rect>
      <x>40</x>
      <y>40</y>
      <width>171</width>
      <height>16</height>
     </rect>
    </property>
    <property name="text">
     <string>Dossier contenant les fichiers .XML</string>
    </property>
   </widget>
   <widget class="QPushButton" name="XMLGenerateReport">
    <property name="geometry">
     <rect>
      <x>40</x>
      <y>150</y>
      <width>75</width>
      <height>23</height>
     </rect>
    </property>
    <property name="text">
     <string>Generate</string>
    </property>
   </widget>
   <widget class="QLineEdit" name="ReportInputFolder">
    <property name="geometry">
     <rect>
      <x>40</x>
      <y>110</y>
      <width>531</width>
      <height>20</height>
     </rect>
    </property>
   </widget>
   <widget class="QLabel" name="ReportInputFolderLabel">
    <property name="geometry">
     <rect>
      <x>40</x>
      <y>90</y>
      <width>81</width>
      <height>16</height>
     </rect>
    </property>
    <property name="text">
     <string>Dossier de sortie</string>
    </property>
   </widget>
   <widget class="QPushButton" name="XMLButtonFolder">
    <property name="geometry">
     <rect>
      <x>580</x>
      <y>60</y>
      <width>75</width>
      <height>23</height>
     </rect>
    </property>
    <property name="text">
     <string>...</string>
    </property>
   </widget>
   <widget class="QPushButton" name="ReportButtonFolder">
    <property name="geometry">
     <rect>
      <x>580</x>
      <y>110</y>
      <width>75</width>
      <height>23</height>
     </rect>
    </property>
    <property name="text">
     <string>...</string>
    </property>
   </widget>
  </widget>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>696</width>
     <height>21</height>
    </rect>
   </property>
   <widget class="QMenu" name="menuReport_Generation_Level_1">
    <property name="title">
     <string>Fichier</string>
    </property>
    <addaction name="actionQuitter"/>
   </widget>
   <addaction name="menuReport_Generation_Level_1"/>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
  <action name="actionQuitter">
   <property name="text">
    <string>Quitter</string>
   </property>
  </action>
 </widget>
 <resources/>
 <connections/>
</ui>

你的问题是 self.app.exec_ () 不允许执行以下行,因为它允许执行事件循环,因此必须最后执行该行。在本例中,我们创建了一个只调用该函数的 运行 方法:

Window.py

class Application(QtWidgets.QWidget):
    # ...

    def __init__(self):
        # ...
        self.ReportButtonFolder.clicked.connect(self.openBoxFolder_Report)

        # If you uncomment the following line, the eventListener() method will be correctly called ..
        # self.window.findChild(QPushButton, "XMLGenerateReport").clicked.connect(self.eventListener)
        self.window.show()

    def run(self):
        return self.app.exec_()

main.py

# ...
myapp = Application()
myapp.addEventListener("Button", "XMLGenerateReport")
thr = ProgramThread()
thr.start()
sys.exit(myapp.run())

尽管您之前的代码可能会带来 long-term 问题,因为 eventlistenerresult 是一个可以在 2 个线程中访问的变量,这很危险,因为线程可以竞争。我更喜欢使用信号。

Window.py

import sys
from PySide2 import QtCore, QtWidgets, QtUiTools


class Application:
    def __init__(self, arguments):
        self.app = QtWidgets.QApplication(arguments)
        loader = QtUiTools.QUiLoader()
        print("Loading mainwindow.ui file")
        self.window = loader.load(QtCore.QFile("mainwindow.ui"))
        if self.window is not None:
            print("mainwindow.ui loaded")
        else:
            print("Error loading mainwindow.ui")
            sys.exit(-1)

        self.XMLButtonFolder = self.window.findChild(
            QtWidgets.QPushButton, "XMLButtonFolder"
        )
        self.ReportButtonFolder = self.window.findChild(
            QtWidgets.QPushButton, "ReportButtonFolder"
        )
        self.XMLInputFolder = self.window.findChild(
            QtWidgets.QLineEdit, "XMLInputFolder"
        )
        self.ReportInputFolder = self.window.findChild(
            QtWidgets.QLineEdit, "ReportInputFolder"
        )
        self.XMLGenerateReport = self.window.findChild(
            QtWidgets.QPushButton, "XMLGenerateReport"
        )

        self.ReportButtonFolder.clicked.connect(self.openBoxFolder_Report)
        self.XMLButtonFolder.clicked.connect(self.openBoxFolder_XML)

        # If you uncomment the following line, the eventListener() method will be correctly called ..
        # self.window.findChild(QPushButton, "XMLGenerateReport").clicked.connect(self.eventListener)
        self.window.show()

    def run(self):
        return self.app.exec_()

    def openBoxFolder_XML(self):
        # Works
        dialog = QtWidgets.QFileDialog(self.window)
        dialog.setFileMode(QFileDialog.ExistingFile)
        path, _ = dialog.getOpenFileName(
            self.window,
            "Sélectionnez un fichier .xml",
            filter="XML files (*.xml)",
        )
        self.XMLInputFolder.setText(path)

    def openBoxFolder_Report(self):
        # Works
        dialog = QFileDialog(self.window)
        dialog.setFileMode(QFileDialog.Directory)
        path, _ = dialog.getOpenFileName()
        self.ReportInputFolder.setText(path)

main.py

import sys
from PySide2 import QtCore

from Window import Application


def GenerateDocument():
    print("Document generated !")


class Worker(QtCore.QObject):
    @QtCore.Slot()
    def task(self):
        GenerateDocument()


if __name__ == "__main__":
    myapp = Application(sys.argv)

    thread = QtCore.QThread()
    thread.start()

    worker = Worker()
    worker.moveToThread(thread)

    myapp.XMLGenerateReport.clicked.connect(worker.task)

    res = myapp.run()

    thread.quit()
    thread.wait()

    sys.exit(res)