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.
2 odpowiedzi
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_())
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()
Podobne pytania
Nowe pytania
python
Python to wielozadaniowy, wielozadaniowy język programowania dynamicznie typowany. Został zaprojektowany tak, aby był szybki do nauczenia się, zrozumienia i użycia oraz wymuszania czystej i jednolitej składni. Należy pamiętać, że Python 2 oficjalnie nie jest obsługiwany od 01-01-2020. Mimo to, w przypadku pytań Pythona specyficznych dla wersji, dodaj znacznik [python-2.7] lub [python-3.x]. Korzystając z wariantu Pythona (np. Jython, PyPy) lub biblioteki (np. Pandas i NumPy), należy umieścić go w tagach.