在 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><html><head/><body><p>Entrez ici le dossier où ce trouve le fichier .xml à utiliser</p></body></html></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)
我想用 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><html><head/><body><p>Entrez ici le dossier où ce trouve le fichier .xml à utiliser</p></body></html></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)