首页 > 解决方案 > 在 Pyside2 for Python 上显示应用程序后,我们如何刷新事件?

问题描述

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

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

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

这是我的类初始化(是的,它是一个线程):

    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 addEventListener(self, qtype, qname):
        self.eventlistenerresult = False
        self.__widget.findChild(self.elementtype[qtype], qname).clicked.connect(self.eventListener)

这是主程序:

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

问题是我没有刷新窗口的方法,所以没有触发事件(run() 方法中的一组有效,但后面的一组没有)

我希望能够触发名为“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()

窗口.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

主窗口.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>

标签: pythonpython-3.xpyside2

解决方案


您的问题是 self.app.exec_ () 不允许执行以下行,因为它允许执行事件循环,因此必须最后执行该行。在这种情况下,我们创建一个只调用该函数的 run 方法:

窗口.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_()

主文件

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

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

窗口.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)

主文件

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)

推荐阅读