我的想法: 更新进度时,线程会发出QtSignal,从而触发一些更新进度条的功能。在完成处理时也发出信号,以便可以显示结果。
#NOTE: this is example code for my idea, you do not have # to read this to answer the question(s). import threading from PyQt4 import QtCore, QtGui import re import copy class ProcessingThread(threading.Thread, QtCore.QObject): __pyqtSignals__ = ( "progressUpdated(str)", "resultsReady(str)") def __init__(self, docs): self.docs = docs self.progress = 0 #int between 0 and 100 self.results = [] threading.Thread.__init__(self) def getResults(self): return copy.deepcopy(self.results) def run(self): num_docs = len(self.docs) - 1 for i, doc in enumerate(self.docs): processed_doc = self.processDoc(doc) self.results.append(processed_doc) new_progress = int((float(i)/num_docs)*100) #emit signal only if progress has changed if self.progress != new_progress: self.emit(QtCore.SIGNAL("progressUpdated(str)"), self.getName()) self.progress = new_progress if self.progress == 100: self.emit(QtCore.SIGNAL("resultsReady(str)"), self.getName()) def processDoc(self, doc): ''' this is tivial for shortness sake ''' return re.findall('<a [^>]*>.*?</a>', doc) class GuiApp(QtGui.QMainWindow): def __init__(self): self.processing_threads = {} #{'thread_name': Thread(processing_thread)} self.progress_object = {} #{'thread_name': int(thread_progress)} self.results_object = {} #{'thread_name': []} self.selected_thread = '' #'thread_name' def processDocs(self, docs): #create new thread p_thread = ProcessingThread(docs) thread_name = "example_thread_name" p_thread.setName(thread_name) p_thread.start() #add thread to dict of threads self.processing_threads[thread_name] = p_thread #init progress_object for this thread self.progress_object[thread_name] = p_thread.progress #connect thread signals to GuiApp functions QtCore.QObject.connect(p_thread, QtCore.SIGNAL('progressUpdated(str)'), self.updateProgressObject(thread_name)) QtCore.QObject.connect(p_thread, QtCore.SIGNAL('resultsReady(str)'), self.updateResultsObject(thread_name)) def updateProgressObject(self, thread_name): #update progress_object for all threads self.progress_object[thread_name] = self.processing_threads[thread_name].progress #update progress bar for selected thread if self.selected_thread == thread_name: self.setProgressBar(self.progress_object[self.selected_thread]) def updateResultsObject(self, thread_name): #update results_object for thread with results self.results_object[thread_name] = self.processing_threads[thread_name].getResults() #update results widget for selected thread try: self.setResultsWidget(self.results_object[thread_name]) except KeyError: self.setResultsWidget(None)
对此方法的任何评论(例如缺点,陷阱,赞美等)将不胜感激。
我最终使用了QThread类以及相关的信号和插槽在线程之间进行通信。这主要是因为我的程序已经将Qt / PyQt4用于GUI对象/小部件。此解决方案还需要对我现有的代码进行较少的更改即可实现。
这是指向适用的Qt文章的链接,该文章解释了Qt如何处理线程和信号,网址为http://www.linuxjournal.com/article/9602。摘录如下:
幸运的是,只要线程运行自己的事件循环,Qt允许跨线程连接信号和插槽。与发送和接收事件相比,这是一种更加干净的通信方法,因为它避免了所有簿记类和QEvent派生的中间类,这些类在任何平凡的应用程序中都变得必需。现在,线程之间的通信已成为将信号从一个线程连接到另一个线程中的插槽的问题,并且在线程之间交换数据的静音和线程安全问题由Qt处理。 为什么需要在要连接信号的每个线程中运行事件循环?原因与将信号从一个线程连接到另一个线程的插槽时,Qt使用的线程间通信机制有关。建立此类连接后,称为排队连接。当通过排队的连接发出信号时,将在下次执行目标对象的事件循环时调用该插槽。如果改为由另一个线程的信号直接调用该插槽,则该插槽将在与调用线程相同的上下文中执行。通常,这不是您想要的(特别是如果您使用数据库连接,则不是您想要的,因为数据库连接只能由创建它的线程使用)。排队的连接将信号正确分派到线程对象,并通过在事件系统上piggy带支持在其自己的上下文中调用其插槽。这正是我们希望线程间通信所需要的,其中一些线程正在处理数据库连接。Qt信号/时隙机制从根本上讲是上面概述的线程间事件传递方案的实现,但具有更简洁易用的界面。
幸运的是,只要线程运行自己的事件循环,Qt允许跨线程连接信号和插槽。与发送和接收事件相比,这是一种更加干净的通信方法,因为它避免了所有簿记类和QEvent派生的中间类,这些类在任何平凡的应用程序中都变得必需。现在,线程之间的通信已成为将信号从一个线程连接到另一个线程中的插槽的问题,并且在线程之间交换数据的静音和线程安全问题由Qt处理。
为什么需要在要连接信号的每个线程中运行事件循环?原因与将信号从一个线程连接到另一个线程的插槽时,Qt使用的线程间通信机制有关。建立此类连接后,称为排队连接。当通过排队的连接发出信号时,将在下次执行目标对象的事件循环时调用该插槽。如果改为由另一个线程的信号直接调用该插槽,则该插槽将在与调用线程相同的上下文中执行。通常,这不是您想要的(特别是如果您使用数据库连接,则不是您想要的,因为数据库连接只能由创建它的线程使用)。排队的连接将信号正确分派到线程对象,并通过在事件系统上piggy带支持在其自己的上下文中调用其插槽。这正是我们希望线程间通信所需要的,其中一些线程正在处理数据库连接。Qt信号/时隙机制从根本上讲是上面概述的线程间事件传递方案的实现,但具有更简洁易用的界面。
注意: eliben 也有一个很好的答案,如果我不使用处理线程安全性和互斥的PyQt4,他的解决方案将是我的选择。
如果您想使用信号来指示主线程的进度,那么您实际上应该使用PyQt的QThread类而不是Python线程模块中的Thread类。
在PyQt Wiki上可以找到一个使用QThread,信号和插槽的简单示例:
https://wiki.python.org/moin/PyQt/Threading,_Signals_and_Slots