【发布时间】:2010-10-08 20:27:03
【问题描述】:
问题:
- 什么是最佳实践 跟踪线程的 在不锁定 GUI 的情况下进行 (“没有回应”)?
- 一般来说,最佳实践是什么? 线程,因为它适用于 GUI 开发?
问题背景:
- 我有一个适用于 Windows 的 PyQt GUI。
- 用于处理 HTML 集 文件。
- 需要三秒钟 到三个小时来处理一套 文件。
- 我希望能够处理 多套同时使用。
- 我不希望 GUI 锁定。
- 我在看线程模块 实现这一目标。
- 我对线程比较陌生。
- GUI 有一个进度条。
- 我希望它显示进度 选定的线程。
- 显示所选结果 如果它完成了线程。
- 我使用的是 Python 2.5。
我的想法:让线程在进度更新时发出 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 使用的通信机制 当从一个连接信号时 线程到另一个线程的插槽。 当建立这样的连接时,它是 称为排队连接。 当信号通过一个 排队连接,插槽被调用 下次目标对象的 事件循环被执行。如果插槽 而是由 a 直接调用 来自另一个线程的信号,那个槽 将在相同的上下文中执行 调用线程。通常,这是 不是你想要的(尤其不是 如果您使用的是您想要的 数据库连接,作为数据库 连接只能由 创建它的线程)。排队的 连接正确调度 信号给线程对象和 通过在自己的上下文中调用其插槽 捎带事件系统。 这正是我们想要的 其中线程间通信 一些线程正在处理 数据库连接。 Qt 信号/槽机制是根 线程间的实现 上面概述的事件传递方案, 但是用更清洁和 更易于使用的界面。
注意: eliben 也有一个很好的答案,如果我不使用处理线程安全和互斥的 PyQt4,他的解决方案将是我的选择.
【问题讨论】:
标签: python multithreading user-interface pyqt qthread