2016-05-12 108 views
0

我有一个加载小部件由两个标签组成,一个是状态标签,另一个是显示动画gif的标签。如果我调用show()方法处理大量内容之前,加载小部件中的gif根本不会自行更新。 gif btw没有错(循环问题等)。主代码(主叫)看起来是这样的:标签内显示的Gif不会自定义更新Pyqt5

self.loadingwidget = LoadingWidgetForm() 
self.setCentralWidget(self.loadingwidget) 
self.loadingwidget.show() 
... 
... 
heavy stuff 
... 
... 
self.loadingwidget.hide() 

Widget类:

class LoadingWidgetForm(QWidget, LoadingWidget): 
    def __init__(self, parent=None): 
     super().__init__(parent=parent) 
     self.setupUi(self) 
     self.setWindowFlags(self.windowFlags() | Qt.FramelessWindowHint) 
     self.setAttribute(Qt.WA_TranslucentBackground) 
     pince_directory = SysUtils.get_current_script_directory() # returns current working directory 
     self.movie = QMovie(pince_directory + "/media/loading_widget_gondola.gif", QByteArray()) 
     self.label_Animated.setMovie(self.movie) 
     self.movie.setScaledSize(QSize(50, 50)) 
     self.movie.setCacheMode(QMovie.CacheAll) 
     self.movie.setSpeed(100) 
     self.movie.start() 
     self.not_finished=True 
     self.update_thread = Thread(target=self.update_widget) 
     self.update_thread.daemon = True 

    def showEvent(self, QShowEvent): 
     QApplication.processEvents() 
     self.update_thread.start() 

    def hideEvent(self, QHideEvent): 
     self.not_finished = False 

    def update_widget(self): 
     while self.not_finished: 
      QApplication.processEvents() 

正如你看到我试图创建一个单独的线程,以避免工作量,但它并没有任何区别。然后,我通过重写run()方法来尝试QThread类的运气,但它也没有奏效。但是执行QApplication.processEvents()的方法很重要。我也认为我不应该使用单独的线程,我觉得应该有一个更优雅的方式来做到这一点。窗口小部件看起来像这样BTW:

gondola处理...
的GIF的完整版:gondola

提前感谢!祝你有美好的一天。

编辑:由于存在缺陷,我无法将沉重的东西移动到不同的线程。 Pexpect的spawn()方法需要派生对象以及与生成对象相关的任何操作都在同一个线程中。我不想改变整个程序的工作流程

回答

0

为了更新GUI动画,主Qt循环(位于主GUI线程中)必须正在运行和处理事件。 Qt事件循环一次只能处理一个事件,但是因为处理这些事件通常需要很短的时间,所以控制会迅速返回到循环。这允许GUI更新(重绘,包括动画等)显示平滑。

一个常见的例子是有一个按钮来启动加载文件。按下按钮会创建一个事件,并将其传递给您的代码(通过事件直接或通过信号)。现在,主线程位于长时间运行的代码中,并且事件循环处于停止状态 - 并且在长时间运行的作业(例如文件加载)完成之前将保持停顿状态。

你说得对,你可以用线程解决这个问题,但你已经倒过来了。你想把你的长时间运行的代码放到一个线程中(而不是你对processEvents的调用)。实际上,从另一个线程调用GUI(或与之交互)是崩溃的秘诀。

使用线程的最简单方法是使用QRunnerQThreadPool。这允许多个执行线程。以下代码墙为您提供了一个定制的Worker类,可以轻松处理此类问题。我通常把这个文件threads.py,以保持它的方式:

import sys 
from PyQt5.QtCore import QObject, QRunnable 


class WorkerSignals(QObject): 
    ''' 
    Defines the signals available from a running worker thread. 
    error 
     `tuple` (exctype, value, traceback.format_exc()) 

    result 
     `dict` data returned from processing 
    ''' 
    finished = pyqtSignal() 
    error = pyqtSignal(tuple) 
    result = pyqtSignal(dict) 


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

    @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, value, traceback.format_exc())) 
     else: 
      self.signals.result.emit(result) # Return the result of the processing 
     finally: 
      self.signals.finished.emit() # Done 

用上面的,你需要一个QThreadPool处理线程。您只需要创建一次,例如在应用程序初始化过程中。

from .threads import Worker # our custom worker Class 
worker = Worker(fn=<Python function>) # create a Worker object 

现在重视的信号取回的结果,或者被通知的错误:

worker.signals.error.connect(<Python function error handler>) 
worker.signals.result.connect(<Python function result handler>) 

threadpool = QThreadPool() 

现在,在Python函数传递给执行创建工作

然后,要执行此Worker,您可以将它传递给QThreadPool

threadpool.start(worker) 

一切都会自己照顾自己,对工作的结果返回给所连接的信号......和主GUI循环将可以自由地做的事情!

+0

感谢您花时间回答我的问题并给予深入解答。但据我所知,你正在创建一个与主线程分离的线程。我已经说过,由于缺陷(我的帖子结尾),我无法将繁重的工作转移到另一个线程中, – Desertricker

相关问题