Mam program Pyqt5 w Pythonie 3.3, który rozpocznie nowy wątek za każdym razem, gdy przycisk zostanie naciśnięty. Ten wątek użyje wyskakujących okien dialogowych. Działa przy pierwszym naciśnięciu przycisku, jednak po raz drugi (po zakończeniu pierwszego) spowoduje awarię programu. Mogę zadzwonić do okna dialogowego tyle razy, ile chcę od wątku, ale po raz drugi wątek jest prowadzony przez program zamarza. Ten kod będzie reprodukować problem.

import sys
from threading import Thread
from PyQt5 import QtWidgets, QtCore


class Ui_Dialog(object):
    def setupUi(self, Dialog):
        Dialog.setObjectName("Dialog")
        self.pushButton = QtWidgets.QPushButton(Dialog)
        self.pushButton.setGeometry(QtCore.QRect(100, 100, 100, 50))
        self.pushButton.setObjectName("pushButton")

        self.retranslateUi(Dialog)
        QtCore.QMetaObject.connectSlotsByName(Dialog)

    def retranslateUi(self, Dialog):
        _translate = QtCore.QCoreApplication.translate
        Dialog.setWindowTitle(_translate("Dialog", "Test"))
        self.pushButton.setText(_translate("Dialog", "OK"))


class Ui_MainWindow(object):
    def setupUi(self, mainWindow):
        mainWindow.setObjectName("mainWindow")
        self.pushButton = QtWidgets.QPushButton(mainWindow)
        self.pushButton.setGeometry(QtCore.QRect(30, 20, 100, 60))
        self.pushButton.setObjectName("pushButton")

        self.retranslateUi(mainWindow)
        QtCore.QMetaObject.connectSlotsByName(mainWindow)

    def retranslateUi(self, Dialog):
        _translate = QtCore.QCoreApplication.translate
        Dialog.setWindowTitle(_translate("mainWindow", "Test"))
        self.pushButton.setText(_translate("mainWindow", "Push Me!"))


class TestDialog(QtWidgets.QDialog):
    def __init__(self, parent=None):
        super(TestDialog, self).__init__(parent)
        self.ui = Ui_Dialog()
        self.ui.setupUi(self)
        # This message simply needs to go away when the button is pushed
        self.ui.pushButton.clicked.connect(self.close)

    def show_message(self):
        super(TestDialog, self).exec_()


class Main(QtWidgets.QMainWindow):
    def __init__(self):
        super(Main, self).__init__()
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        self.dialog = TestDialog()

        self.ui.pushButton.clicked.connect(self.start_thread)

    def start_thread(self):
        t = Thread(target=self.show_dialog)
        t.daemon = True
        t.start()

    def show_dialog(self):
        # Do lots of background stuff here
        self.dialog.show_message()
        # The dialog can be shown multiple times within the same thread
        self.dialog.show_message()


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    window = Main()
    window.show()
    sys.exit(app.exec_())

Usuń komunikat okna dialogowego i działa. Dlaczego więc nie mogę zadzwonić do okna dialogowego z drugiego wątku? Nie próbuję jednocześnie uruchomić dwóch wątków, ale jeden po drugim.

1
Brickmastr 15 sierpień 2014, 17:47

2 odpowiedzi

Najlepsza odpowiedź

Zrozumiałem to, dzięki pomocy Sebastiana. Stworzyłem obiekt sygnału, podłączony do funkcji show_message. Dodałem również sygnał, aby powiedzieć wątek, gdy okno dialogowe zostało zaakceptowane. Oto kod pracy.

import sys
from threading import Thread
from PyQt5 import QtWidgets, QtCore


class Ui_Dialog(object):
    def setupUi(self, Dialog):
        Dialog.setObjectName("Dialog")
        self.pushButton = QtWidgets.QPushButton(Dialog)
        self.pushButton.setGeometry(QtCore.QRect(100, 100, 100, 50))
        self.pushButton.setObjectName("pushButton")

        self.retranslateUi(Dialog)
        QtCore.QMetaObject.connectSlotsByName(Dialog)

    def retranslateUi(self, Dialog):
        _translate = QtCore.QCoreApplication.translate
        Dialog.setWindowTitle(_translate("Dialog", "Test"))
        self.pushButton.setText(_translate("Dialog", "OK"))


class Ui_MainWindow(object):
    def setupUi(self, mainWindow):
        mainWindow.setObjectName("mainWindow")
        self.pushButton = QtWidgets.QPushButton(mainWindow)
        self.pushButton.setGeometry(QtCore.QRect(30, 20, 100, 60))
        self.pushButton.setObjectName("pushButton")

        self.retranslateUi(mainWindow)
        QtCore.QMetaObject.connectSlotsByName(mainWindow)

    def retranslateUi(self, Dialog):
        _translate = QtCore.QCoreApplication.translate
        Dialog.setWindowTitle(_translate("mainWindow", "Test"))
        self.pushButton.setText(_translate("mainWindow", "Push Me!"))


class TestDialog(QtWidgets.QDialog):
    signal = QtCore.pyqtSignal()

    def __init__(self, parent=None):
        super(TestDialog, self).__init__(parent)
        self.ui = Ui_Dialog()
        self.ui.setupUi(self)
        # This message simply needs to go away
        self.ui.pushButton.clicked.connect(self.close)

    def show_message(self):
        # Use this to display the pop-up so the text can be altered
        super(TestDialog, self).exec_()
        self.signal.emit()


class Main(QtWidgets.QMainWindow):
    signal = QtCore.pyqtSignal()

    def __init__(self):
        super(Main, self).__init__()
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        self.dialog = TestDialog()
        self.dialog_done = False

        self.ui.pushButton.clicked.connect(self.start_thread)

    def complete_dialog(self):
        self.dialog_done = True

    def wait_for_dialog(self):
        while not self.dialog_done:
            pass
        self.dialog_done = False

    def start_thread(self):
        t = Thread(target=self.show_dialog)
        t.daemon = True
        t.start()

    def show_dialog(self):
        # Do lots of background stuff here
        self.signal.emit()
        # Wait for the dialog to get closed
        self.wait_for_dialog()


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    window = Main()
    window.show()
    dialog = TestDialog()
    window.signal.connect(dialog.show_message)
    dialog.signal.connect(window.complete_dialog)
    sys.exit(app.exec_())
2
Brickmastr 15 sierpień 2014, 16:42

Dzięki odpowiedzi na @brickmastr udało mi się przerabiać na własne użycie w nieco innym scenariuszu. W przypadku, gdy inni chcą robić to, co byłem, miejmy nadzieję, że ta nieznacznie zróżnicowana metoda może pomóc. Jeśli używasz programu, w którym chcesz wyświetlić okno dialogowe "działa", a następnie zaktualizuj to pole po zakończeniu procesu, jest to jeden sposób na osiągnięcie tego. Dodatkowo, w jaki sposób możesz użyć tej samej wiadomości popup dla wielu czasochłonnych funkcji.

import PyQt5

#This below import is the python file that gets created from using QtDesigner
#And running pyuic5 to create a .py file from your .ui file - hopefully
#whomever reads this is familiar with using QtDesigner
import dialogBox as fxRun

#This is the file that would contain your primary UI, also created using QtDesigner
import mainUI
import threading

class MAIN_UI(PyQt5.QtWidgets.QMainWindow, mainUI.Ui_interface):
    startSignal = PyQt5.QtCore.pyqtSignal()
    endSignal = PyQt5.QtCore.pyqtSignal()

    def __init__(self,parent=None):
        super(MAIN_UI, self).__init__(parent)
        self.setupUi(self)
        self.buttonStartFunction1.clicked.connect(self.startFunction1)
        self.buttonStartFunction2.clicked.connect(self.startFunction2)

    def startFunction1(self):
        self.startThread(self.exampleMethod1)

   def startFunction2(self):
        self.startThread(self.exampleMethod2)

    def startThread(self,functionName):
        t = threading.Thread(target=functionName)
        t.daemon = True
        t.start()

    def exampleMethod1(self):
        #This function will show the dialog box at the beginning of the process
        # and will update the text and button once the process is complete
        FULLPROGRAM.mainUI.startSignal.emit()
        #Do lots of things here that take a long time
        FULLPROGRAM.mainUI.endSignal.emit()

    def exampleMethod2(self):
        #This can be a different function, just showing that you can send
        #whatever function into the startThread() method and it will work 
        #the same way 
        FULLPROGRAM.mainUI.startSignal.emit()
        #Do lots of things here that take a long time
        FULLPROGRAM.mainUI.endSignal.emit()

class PROCESS_BOX(PyQt5.QtWidgets.QDialog, fxRun.Ui_dialogBox):
    def __init__(self,parent=None): 
        super(PROCESS_BOX,self).__init__(parent)
        self.setupUi(self)
        self.buttonProcessCompleted.clicked.connect(self.close)

    def show_dialogbox(self): 
        self.setWindowTitle("RUNNING")
        self.labelProcessStatus.setText("PROCESSING REQUEST... \n PLEASE WAIT...")
        self.buttonProcessCompleted.setEnabled(False)
        super(PROCESS_BOX,self).exec_()

    def processComplete(self): 
        self.setWindowTitle("FINISHED")
        self.labelProcessStatus.setText("PROCESS COMPLETE! \n CLICK OK")
        self.buttonProcessCompleted.setEnabled(True)    

class FULLPROGRAM: 
    def __init__(self):
        app = PyQt5.QtWidgets.QApplication(sys.argv)
        FULLPROGRAM.fxRun = PROCESS_BOX()
        FULLPROGRAM.mainUI = MAIN_UI()
        FULLPROGRAM.mainUI.startSignal.connect(FULLPROGRAM.fxRun.show_dialogbox)
        FULLPROGRAM.mainUI.endSignal.connect(FULLPROGRAM.fxRun.processComplete)
        FULLPROGRAM.mainUI.show()
        app.exec_()

def main():
    program = FULLPROGRAM()

if __name__ == '__main__':
    main()
0
NL23codes 5 wrzesień 2018, 20:46