问题:

  1. 什么是最好的实践 跟踪一个花纹的 进展不锁定GUI ("不答复")?
  2. 一般来说,什么是最好的做法 穿因为它适用于GUI 发展?

问题的背景:

  • 我有一个PyQt GUI for Windows。
  • 它是用来处理集HTML 文件。
  • 它需要三秒钟 三个小时来处理一套 文件。
  • 我想要能够处理 多组在同一时间。
  • 我不想GUI锁。
  • 我在寻找线程模块 要实现这一点。
  • 我是相对较新穿线。
  • GUI已经取得进展的一个酒吧。
  • 我想让它显示的进展 选定的线。
  • 显示结果的选择 线的如果它的完成。
  • 我使用的是蟒蛇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)

任何评论对这种方法(例如缺点、陷阱,称赞,等等。) 将不胜感激。

决议:

我最终使用的每类和相关的信号,并插槽之间的通信线。这主要是因为我的计划已经采用脱/PyQt4的图形用户界面目/部件。这种解决方案还需要少量改变我的现有编码,以实现。

这里是一个链接到一个适用建篇文章,介绍了如何脱处理线和信号, http://www.linuxjournal.com/article/9602.摘录如下:

幸运的是,夸脱许可证 信号和槽可以连接 跨线,只要作为线 正在运行他们自己的事件的循环。这是一个更清洁的方法 通信相比,发 接收事件,因为它避免了 所有的簿记和中间 QEvent衍生的课程,成为 有必要在任何重要 应用程序。之间的通信 线现在成为问题 连接信号从一个线程 槽在另一,mutexing 和线安全问题交换 数据之间线由 夸脱

为什么是必要的运行事件 环路内的每个螺纹给你 要连接的信号?的原因 有与间线 通信机制的使用脱 当连接信号从一个 纹的时隙的另外一个线程。当这样一个连接而成的, 称为一个排队连接。当信号都发射过 排队连接,隙调用 下一个时间的目的对象是 事件循环是执行。如果槽 不是被直接援引通过一个 信号从另外一个线程,隙 将执行相同的上下文 叫线。通常情况下,这是 不是你想要的(和特别是不 你想要什么,如果您使用的是一个 数据库连接,该数据库 连接可以使用仅由 线创建)。排队 连接,正确地将调度 信号线目和 援引了老虎在它自己的背景下通过 小猪-背上的事件的系统。这正是我们想要的 间线通信在哪 一些线处理 数据库的连接。该脱 信号/老虎机制是在根个 实现中间线 事件通过的方案上面所概述的, 但有一个更清洁和 更易于使用的接口。

注: eliben 还有一个很好的答案,并且如果我没有使用PyQt4,它处理线的安全和mutexing,他的解决方案将是我的选择。

有帮助吗?

解决方案

如果你想要使用信号,以表明取得进展的主线,那么你真的应该使用PyQt的每类而不是螺纹类从蟒蛇的线的模块。

一个简单的例子,它使用每、信号和隙,可以发现在PyQt Wiki:

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

其他提示

地蟒蛇队不会的工作因为你必须阻止在队列中获得(),其桶塞了你的用户界面。

脱基本上实现了一个排队系统在内的交叉线通信。试试这个电话从任何线后一个电话一个槽中。

QtCore.特化qmetaobject.来自()

这是笨重的是缺乏记录,但是它应该做你想做的,甚至从一个非夸脱线。

你也可以用事件机械。见QApplication(或QCoreApplication)用于方法的名称,如"职位"。

编辑:这里有一个更完整的例子...

我创造了自己的类基于QWidget.它有一个老虎接受串;我把它定义是这样的:

@QtCore.pyqtSlot(str)
def add_text(self, text):
   ...

后来,我的创建的一个实例,这个小部件中主要GUI线。从主GUI线或任何其他线(敲木头),我可以叫:

QtCore.QMetaObject.invokeMethod(mywidget, "add_text", QtCore.Q_ARG(str,"hello world"))

笨重的,但它得到你那里。

丹。

我建议你使用排队,而不是信号。我个人找到一个更强大的和可以理解的编程方式,因为它更加同步的。

线应该得到"工作"一个队列中,并把结果在另一个队。但第三个队可以使用的线通知和信息,如错误和"进展报告"。一旦你结构你的代码这样,它变得更简单的管理。

这种方式,一个单一的"工作队"和"结果的队列中"也可以采用由一个小组的工作程序线,这条路线的所有信息,在线进入主GUI线。

如果你的方法"processDoc"不会改变任何其他数据(只是看起来的一些数据和返回这并不改变变量或性质的父类)可以使用Py_BEGIN_ALLOW_THREADS和Py_END_ALLOW_THREADS macroses( 在这里看到细节 )。所以,该文件将处理在线不会锁定的解释程序和用户界面,将进行更新。

你总是会有这个问题在蟒蛇。谷歌吉尔"的全球翻译工作者锁定"的更多的背景。有两个一般建议的方式得到解决该问题,你是遇到:使用 扭曲的, 或者使用一个模块,类似于 多处理 模块介绍了在2.5.

扭曲将要求您了解异步编程技术,这可能会造成混乱的开始,但将是有益的,如果你需要编写高吞吐量网络的应用程序,将更有益于你在长期运行。

多处理模块将叉的一个新的进程,并使用IPC使它的行为,如果你有真实的线程。唯一的缺点是,你会需要python2.5安装这是相当新的和inst'包括在大多数Linux发行版或OS x默认。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top