问题描述
环境:
Python 3.7
Qt5
Windows 10
问题:
当我执行我的代码时,它立即显示UI,然后假定在执行这些初始化任务时进行一些其他准备工作并显示加载gif。但这确实有效。 UI没有显示gif,而是被阻塞(冻结),等待我的准备脚本完成其工作。
我的脚本有一个按钮,可以运行我的主脚本“ StartMyApp”并在MyApp运行时显示动画gif,而不会冻结我的UI。我为此使用多线程。完美运作。我使用了本教程:https://www.learnpyqt.com/courses/concurrent-execution/multithreading-pyqt-applications-qthreadpool/
因此,我认为通过克隆相同的逻辑,我可以在UI的 init 中显示另一个加载gif,但它不起作用。我错过了什么。我不明白,因为“运行”按钮可以通过显示gif并运行主代码而不冻结UI来完美地工作,而我的“准备”代码则不显示gif并冻结我的UI,直到完成为止。
有人知道这个问题的根源吗?
from PyQt5 import QtWidgets,uic,QtGui
from PyQt5.QtCore import *
from PyQt5.QtGui import QMovie
import traceback,sys
class WorkerSignals(QObject):
'''
Defines the signals available from a running worker thread.
Supported signals are:
finished
No data
error
`tuple` (exctype,value,traceback.format_exc() )
result
`object` data returned from processing,anything
progress
`int` indicating % progress
'''
finished = pyqtSignal ()
error = pyqtSignal (tuple)
result = pyqtSignal (object)
progress = pyqtSignal (int)
class Worker (QRunnable):
'''
Worker thread
Inherits from QRunnable to handler worker thread setup,signals and wrap-up.
:param callback: The function callback to run on this worker thread. Supplied args and
kwargs will be passed through to the runner.
:type callback: function
:param args: Arguments to pass to the callback function
:param kwargs: Keywords to pass to the callback function
'''
def __init__(self,fn,*args,**kwargs):
super (Worker,self).__init__ ()
# Store constructor arguments (re-used for processing)
self.fn = fn
self.args = args
self.kwargs = kwargs
self.signals = WorkerSignals ()
# Add the callback to our kwargs
self.kwargs['progress_callback'] = self.signals.progress
@pyqtSlot ()
def run(self):
'''
Initialise the runner function with passed args,kwargs.
'''
# Retrieve args/kwargs here; and fire processing using them
try:
result = self.fn (*self.args,**self.kwargs)
except:
traceback.print_exc ()
exctype,value = sys.exc_info ()[:2]
self.signals.error.emit((exctype,traceback.format_exc ()))
else:
self.signals.result.emit (result) # Return the result of the processing
finally:
self.signals.finished.emit () # Done
class Ui(QtWidgets.QMainWindow):
def __init__(self):
super(Ui,self).__init__()
uic.loadUi('Ui/MyAppUI.Ui',self)
# === We display the UI ==========
self.show()
# === THis will handle the MULTITHREAD PART ===================
self.threadpool = QThreadPool()
print("Multithreading with maximum %d threads" % self.threadpool.maxThreadCount())
self.StartPreparingMyApp() #<======== This method doesn't work!!!!
# === Associate methods to the buttons of the UI ==============
self.button_Report.clicked.connect (self.ButtonStartMyAppReport)
self.button_Run.clicked.connect (self.ButtonStartMyApp)
def StartMyAppReport(self,progress_callback):
#do some stuff
def StartMyApp(self,progress_callback):
# do some stuff
def ButtonStartMyApp(self): #<=== This method works perfectly by showing the loading gif.
# Pass the function to execute
# === We need to block the Button Run and change its color
self.button_Run.setEnabled (False)
self.button_Run.setText ('Running...')
self.button_Run.setStyleSheet ("background-color: #ffcc00;")
self.label_logo.setHidden (True)
self.label_running.setHidden (False)
# === Play animated gif ================
self.gif = QMovie ('ui/animated_gif_logo_UI_.gif')
self.label_running.setMovie (self.gif)
self.gif.start ()
self.EditTextFieldUi (self.label_HeaderMsg1,'#ff8a00',"MyApp is running the tasks... You can press the button 'Report' to see what MyApp has done.")
self.EditTextFieldUi (self.label_HeaderMsg2,"Press 'button 'Quit' to stop and turn off MyApp.")
worker = Worker (self.StartMyApp) # Any other args,kwargs are passed to the run function
worker.signals.result.connect (self.print_output)
worker.signals.finished.connect (self.thread_complete)
worker.signals.progress.connect (self.progress_fn)
# Execute
self.threadpool.start (worker)
def PreparingMyApp(self,progress_callback):
#do some stuff
return "Done"
def ButtonStartMyAppReport(self):
# Pass the function to execute
worker = Worker (self.StartMyAppReport) # Any other args,kwargs are passed to the run function
worker.signals.result.connect (self.print_output)
worker.signals.finished.connect (self.thread_complete)
worker.signals.progress.connect (self.progress_fn)
# Execute
self.threadpool.start(worker)
def StartPreparingMyApp(self): #<=== This method doesn't work !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
# === Play animated gif ================
self.label_loading.setHidden (False)
self.gif_loading = QMovie ('ui/loading.gif')
self.label_loading.setMovie (self.gif_loading)
self.gif_loading.start ()
# Pass the function to execute
worker = Worker (self.PreparingMyApp) # Any other args,kwargs are passed to the run function
worker.signals.result.connect (self.print_output)
worker.signals.finished.connect (self.thread_complete)
worker.signals.progress.connect (self.progress_fn)
# Execute
self.threadpool.start (worker)
self.gif_loading.stop ()
self.label_loading.setHidden (True)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = Ui()
app.exec_()
编辑:
我添加了用Qt Designer创建的MyAppUI.ui的xml源,以重现我的示例:
https://drive.google.com/file/d/1U9x0NmZ7GP6plzvRb6YgwIqaFHCz1PMc/view?usp=sharing
解决方法
一切都对您有用。 请注意,我已转移
self.gif_loading.stop() # <---- +++
self.label_loading.setHidden(True) # <---- +++
在thread_complete
方法中添加了QtCore.QThread.msleep (5000)
进入run
方法以观察self.gif_loading
import sys
from PyQt5 import QtCore,QtWidgets,QtGui,uic
from PyQt5.Qt import *
class WorkerSignals(QObject):
finished = pyqtSignal ()
error = pyqtSignal (tuple)
result = pyqtSignal (object)
progress = pyqtSignal (int)
class Worker (QRunnable):
def __init__(self,fn,*args,**kwargs):
super (Worker,self).__init__ ()
# Store constructor arguments (re-used for processing)
self.fn = fn
self.args = args
self.kwargs = kwargs
self.signals = WorkerSignals ()
# Add the callback to our kwargs
self.kwargs['progress_callback'] = self.signals.progress
@pyqtSlot ()
def run(self):
'''
Initialise the runner function with passed args,kwargs.
'''
# Retrieve args/kwargs here; and fire processing using them
try:
result = self.fn (*self.args,**self.kwargs)
QtCore.QThread.msleep(5000) # +++ !!!!!!!!!!!!!!!!!!!!!!
except:
traceback.print_exc ()
exctype,value = sys.exc_info ()[:2]
self.signals.error.emit((exctype,value,traceback.format_exc ()))
else:
self.signals.result.emit (result) # Return the result of the processing
finally:
self.signals.finished.emit () # Done
class Ui(QtWidgets.QMainWindow):
def __init__(self):
super(Ui,self).__init__()
# uic.loadUi('Ui/MyAppUI.Ui',self) # !!!
uic.loadUi('my_app_ui.ui',self) # !!!
# === We display the UI ==========
self.show()
# === THis will handle the MULTITHREAD PART ===================
self.threadpool = QThreadPool()
print("Multithreading with maximum %d threads" % self.threadpool.maxThreadCount())
self.StartPreparingMyApp() # <======== This method work !!!!
# === Associate methods to the buttons of the UI ==============
self.button_Report.clicked.connect (self.ButtonStartMyAppReport)
self.button_Run.clicked.connect (self.ButtonStartMyApp)
def StartMyAppReport(self,progress_callback):
#do some stuff
pass # +++
def StartMyApp(self,progress_callback):
# do some stuff
pass # +++
def ButtonStartMyApp(self): #<=== This method works perfectly by showing the loading gif.
# Pass the function to execute
# === We need to block the Button Run and change its color
self.button_Run.setEnabled (False)
self.button_Run.setText ('Running...')
self.button_Run.setStyleSheet ("background-color: #ffcc00;")
self.label_logo.setHidden (True)
self.label_running.setHidden (False)
# === Play animated gif ================
self.gif = QMovie("D:/_Qt/__Qt/wait.gif") # ('ui/animated_gif_logo_UI_.gif') !!!
self.label_running.setMovie (self.gif)
self.gif.start ()
#? self.EditTextFieldUi (self.label_HeaderMsg1,'#ff8a00',#? "MyApp is running the tasks... You can press the button 'Report' to see what MyApp has done.")
#? self.EditTextFieldUi (self.label_HeaderMsg2,#? "Press 'button 'Quit' to stop and turn off MyApp.")
worker = Worker (self.StartMyApp) # Any other args,kwargs are passed to the run function
worker.signals.result.connect (self.print_output)
worker.signals.finished.connect (self.thread_complete)
worker.signals.progress.connect (self.progress_fn)
# Execute
self.threadpool.start (worker)
def PreparingMyApp(self,progress_callback):
#do some stuff
return "Done"
def ButtonStartMyAppReport(self):
# Pass the function to execute
worker = Worker (self.StartMyAppReport) # Any other args,kwargs are passed to the run function
worker.signals.result.connect (self.print_output)
worker.signals.finished.connect (self.thread_complete)
worker.signals.progress.connect (self.progress_fn)
# Execute
self.threadpool.start(worker)
def StartPreparingMyApp(self):
print("!!! <=== This method work !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
# === Play animated gif ================
self.label_loading.setHidden (False)
self.gif_loading = QMovie("D:/_Qt/__Qt/wait.gif") # ('ui/loading.gif') !!!
self.label_loading.setMovie (self.gif_loading)
self.gif_loading.start ()
# Pass the function to execute
worker = Worker (self.PreparingMyApp) # Any other args,kwargs are passed to the run function
worker.signals.result.connect (self.print_output)
worker.signals.finished.connect (self.thread_complete)
worker.signals.progress.connect (self.progress_fn)
# Execute
self.threadpool.start (worker)
# self.gif_loading.stop () # ---
# self.label_loading.setHidden (True) # ---
# +++ vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
def print_output(self,obj):
print(f'def print_output(self,obj): {obj}')
def thread_complete(self,val='finished'):
print(f'def thread_complete(self,obj): {val}')
self.gif_loading.stop () # <---- +++
self.label_loading.setHidden (True) # <---- +++
def progress_fn(self,val):
print(f'def progress_fn(self,obj): {val}')
# +++ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = Ui()
app.exec_()