Как мы можем обновить события после показа приложения на Pyside2 для Python? - PullRequest
1 голос
/ 02 июля 2019

Я хочу создать программное обеспечение Python с графическим интерфейсом Pyside2. Я разработал графический интерфейс с помощью Qt Designer и сгенерировал файл .ui, который загружаю в свой скрипт Python. Я ищу способ поставить «слушатель событий» после того, как приложение показывает модульный класс, я не хочу помещать все мои методы connect () в класс init.

Я не могу разделить инициализацию своего класса и self.show (), поэтому мне нужно поместить эти строки после метода .show ():

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

Это мой класс 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_())

В том же классе я добавил функцию для создания нового соединения и обнаружения события «нажал» с другой кнопки

    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», даже если метод .connect () вызывается после .show ()

Как мы можем это сделать?

Воспроизводимый пример: (да, я знаю, в этом примере это может быть только на мероприятии, но это нормально для меня) 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>

1 Ответ

1 голос
/ 03 июля 2019

Проблема в вашем случае заключается в том, что self.app.exec_ () не позволит выполнить следующие строки, так как он позволяет выполнять цикл события, поэтому эта строка должна выполняться последней. В этом случае мы создаем метод run, который вызывает только эту функцию:

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

Хотя ваш предыдущий код может вызвать долгосрочные проблемы, поскольку 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)
...