gpt4 book ai didi

python - PyQt:如何在不卡住 GUI 的情况下更新进度?

转载 作者:IT老高 更新时间:2023-10-28 20:54:43 24 4
gpt4 key购买 nike

问题:

  1. 最佳做法是什么跟踪线程的在不锁定 GUI 的情况下进行(“没有回应”)?
  2. 一般来说,最佳做法是线程,因为它适用于 GUI开发?

问题背景:

  • 我有一个适用于 Windows 的 PyQt GUI。
  • 用于处理 HTML 集合文件。
  • 大约需要 3 秒到三个小时来处理一套文件。
  • 我希望能够处理多套同时使用。
  • 我不希望 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 .摘录如下:

Fortunately, Qt permitssignals and slots to be connectedacross threads—as long as the threadsare running their own event loops.This is a much cleaner method ofcommunication compared to sending andreceiving events, because it avoidsall the bookkeeping and intermediateQEvent-derived classes that becomenecessary in any nontrivialapplication. Communicating betweenthreads now becomes a matter ofconnecting signals from one thread tothe slots in another, and the mutexingand thread-safety issues of exchangingdata between threads are handled byQt.

Why is it necessary to run an eventloop within each thread to which youwant to connect signals? The reasonhas to do with the inter-threadcommunication mechanism used by Qtwhen connecting signals from onethread to the slot of another thread.When such a connection is made, it isreferred to as a queued connection.When signals are emitted through aqueued connection, the slot is invokedthe next time the destination object'sevent loop is executed. If the slothad instead been invoked directly by asignal from another thread, that slotwould execute in the same context asthe calling thread. Normally, this isnot what you want (and especially notwhat you want if you are using adatabase connection, as the databaseconnection can be used only by thethread that created it). The queuedconnection properly dispatches thesignal to the thread object andinvokes its slot in its own context bypiggy-backing on the event system.This is precisely what we want forinter-thread communication in whichsome of the threads are handlingdatabase connections. The Qtsignal/slot mechanism is at root animplementation of the inter-threadevent-passing scheme outlined above,but with a much cleaner andeasier-to-use interface.

注意: eliben 也有一个很好的答案,如果我不使用处理线程安全和互斥的 PyQt4,他的解决方案将是我的选择.

最佳答案

如果你想使用信号来指示主线程的进度,那么你真的应该使用 PyQt 的 QThread 类而不是 Python 线程模块中的 Thread 类。

可以在 PyQt Wiki 上找到一个使用 QThread、信号和槽的简单示例:

https://wiki.python.org/moin/PyQt/Threading,_Signals_and_Slots

关于python - PyQt:如何在不卡住 GUI 的情况下更新进度?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/569650/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com