2016-02-16 20 views
1

我想通过模态QDialog显示加载进度。所以我创建一个线程来加载数据并在对话框中调用exec()PyQt对话框在线程运行时不负责

loading_progress_dialog = LoadingProgressDialog(len(filenames)) 
loadingWorker = analyzer.LoadingWorker(filenames, loading_progress_dialog.apply_progress) 
workingThread = QThread() 

workingThread.started.connect(loadingWorker.process) 
loadingWorker.finished.connect(workingThread.quit) 
workingThread.finished.connect(loading_progress_dialog.accept) 

loadingWorker.moveToThread(workingThread) 
workingThread.start() 

loading_progress_dialog.exec() 

我想对话框来负责,但它冻结,我无法加载线程运行时,它周围在屏幕上移动。

class LoadingProgressDialog(QLoadingProgressDialog, Ui_LoadingDialog): 
    def __init__(self, maxFiles): 
     super(LoadingProgressDialog, self).__init__() 
     self.setupUi(self) 

     self.progressBar.setMaximum(maxFiles) 
     self.setWindowTitle('Loading files...') 

    def apply_progress(self, delta_progress): 
     self.progressBar.setValue(delta_progress + self.progressBar.value()) 

class LoadingWorker(QtCore.QObject): 
    def __init__(self, file_names, progress_made): 
     super(LoadingWorker, self).__init__() 
     self._file_names = file_names 
     self._progress_made = progress_made 

    finished = QtCore.pyqtSignal() 

    def process(self): 
     print("Thread started") 
     # load_csv_data(self._file_names, self._progress_made)  
     QtCore.QThread.sleep(5) 
     self.finished.emit() 

我与GIL战斗还是另一个问题?我担心的第二件事情是self.finished.emit()loading_progress_dialog.exec()之间的竞赛状况。如果工作线程完成得比gui线程运行速度快exec(),对话框将不会关闭。有什么方法可以确保一切顺序正确吗?

回答

2
  1. 你的GUI冻结是因为它在同一个线程中的工人执行 - 在主线程!如果您将工作人员移至不同的线程,这会如何呢?好吧,让我们来看看究竟你做了:

    # This connects signal to the instance of worker located in main thread 
    workingThread.started.connect(loadingWorker.process) 
    
    # Creates a copy of worker in the different thread 
    loadingWorker.moveToThread(workingThread) 
    
    # Signal reaches the instance of worker it was connected to - 
    # the instance belonging to main thread! 
    workingThread.start() 
    

    修复的方法是直截了当:招工人以前连接信号给它。如果可以保证进度对话框接收命令关闭一个前显示

  2. 赛条件是不可能的:

    class LoadingWorker(QtCore.QObject): 
        [...] 
        def process(self): 
         self.ready.emit() 
         [...] 
         self.finished.emit() 
    
    loadingWorker.ready.connect(loading_progress_dialog.exec) 
    loadingWorker.finished.connect(loading_progress_dialog.close) 
    

因此,简单的程序,它由不同的线程的顺序更新UI可能是这样的:

from PyQt4 import QtGui, QtCore 
from PyQt4.QtCore import QThread 
from time import sleep 

class LoadingProgressDialog(QtGui.QDialog): 
    def __init__(self): 
     super().__init__() 
     self.setWindowTitle('Loading files...') 

    def show_progress(self, p): 
     self.setWindowTitle('Loading files... {}%'.format(p)) 

class LoadingWorker(QtCore.QObject): 
    finished = QtCore.pyqtSignal() 
    ready = QtCore.pyqtSignal() 
    report_progress = QtCore.pyqtSignal(object) 

    def process(self): 
     print('Worker thread ID: %s' % int(QThread.currentThreadId())) 
     print("Worker started") 
     self.ready.emit() 

     for p in range(0, 100, 10): 
      self.report_progress.emit(p) 
      sleep(0.2) 

     print("Worker terminates...") 
     self.finished.emit() 


if __name__ == '__main__': 
    import sys 
    app = QtGui.QApplication([]) 

    print('Main thread ID: %s' % int(QThread.currentThreadId())) 

    workingThread = QThread() 
    loadingWorker = LoadingWorker() 
    loading_progress_dialog = LoadingProgressDialog() 

    loadingWorker.ready.connect(loading_progress_dialog.exec) 
    loadingWorker.report_progress.connect(loading_progress_dialog.show_progress) 
    loadingWorker.finished.connect(workingThread.quit) 
    loadingWorker.finished.connect(loading_progress_dialog.close) 

    loadingWorker.moveToThread(workingThread) 

    workingThread.started.connect(loadingWorker.process) 
    workingThread.start() 

    sys.exit(app.exec_()) 
+2

为什么你需要连接信号之前,移动到线程的讨论可以找到[这里](http://stackoverflow.com/q/20752154/1994235)。如果你用'@ pyqtSlot'装饰插槽,那么在连接之前你不需要移动。 –