【问题标题】:multiprocessing GUI schemas to combat the "Not Responding" blocking多处理 GUI 模式以对抗“无响应”阻塞
【发布时间】:2013-03-19 20:38:08
【问题描述】:

创建多处理/GUI 编码系统的最佳方法是什么?

我想为互联网社区创建一个地方来查找有关如何在 python 中使用multiprocessing 模块的示例。

我在 Internet 上看到了几个在主模块中调用的简单全局函数的 multiprocessing 进程的小例子,但我发现这很少能轻易地转化为任何人实际对 GUI 所做的任何事情。我认为许多程序将具有他们希望在单独进程中用作对象方法的功能(可能是其他对象的聚合等),并且可能单个 GUI 元素将具有需要调用它的关联对象流程等

例如,我有一个相对复杂的程序,我在为其获取响应式 GUI 时遇到问题,我认为这是由于我对 multiprocessingQThread 的线程缺乏了解。但是,我知道下面给出的示例至少会以我想要的方式在进程之间传递信息(由于能够执行 print 语句),但我的 GUI 仍然处于锁定状态。有谁知道这可能是什么原因造成的,如果我对多线程/多处理架构缺乏了解,这仍然是一个问题?

这是我正在做的一个小的伪代码示例:

class Worker:
    ...
    def processing(self, queue):
        # put stuff into queue in a loop

# This thread gets data from Worker
class Worker_thread(QThread):
    def __init__(self):
        ...
        # make process with Worker inside
    def start_processing(self):
        # continuously get data from Worker
        # send data to Tab object with signals/slots

class Tab(QTabWidget):
    # spawn a thread separate from main GUI thread

    # update GUI using slot
    def update_GUI()

这段代码是完全可编译的例子,体现了我的程序的覆盖结构:

from PyQt4 import QtCore, QtGui
import multiprocessing as mp
import numpy as np
import sys
import time

# This object can hold several properties which will be used for the processing
# and will be run in the background, while it updates a thread with all of it's progress
class Worker:
    def __init__(self, some_var):
        self.some_var = some_var
        self.iteration = 0

    def some_complex_processing(self, queue):
        for i in range(0,5000):
            self.iteration += 1
            queue.put(self.iteration)
        queue.put('done with processing')

# This Woker_thread is a thread which will spawn a separate process (Worker).
# This separate is needed in order to separate the data retrieval
# from the main GUI thread, which should only quickly update when needed 
class Worker_thread(QtCore.QThread):
    # signals and slots are used to communicate back to the main GUI thread
    update_signal = QtCore.pyqtSignal(int)
    done_signal = QtCore.pyqtSignal()

    def __init__(self, parent, worker):
        QtCore.QThread.__init__(self, parent)
        self.queue = mp.Queue()
        self.worker = worker
        self.parent = parent
        self.process = mp.Process(target=self.worker.some_complex_processing, args=(self.queue,))

    # When the process button is pressed, this function will start getting data from Worker
    # this data is then retrieved by the queue and pushed through a signal
    # to Tab.update_GUI
    @QtCore.pyqtSlot()
    def start_computation(self):
        self.process.start()
        while(True):
            try:
                message = self.queue.get()
                self.update_signal.emit(message)
            except EOFError:
                pass
            if message == 'done with processing':
                self.done_signal.emit()
                break
            #self.parent.update_GUI(message)
        self.process.join()
        return

# Each tab will start it's own thread, which will spawn a process
class Tab(QtGui.QTabWidget):
    start_comp = QtCore.pyqtSignal()
    def __init__(self, parent, this_worker):
        self.parent = parent
        self.this_worker = this_worker
        QtGui.QTabWidget.__init__(self, parent)

        self.treeWidget = QtGui.QTreeWidget(self)
        self.properties = QtGui.QTreeWidgetItem(self.treeWidget, ["Properties"])
        self.step = QtGui.QTreeWidgetItem(self.properties, ["Iteration #"])

        self.thread = Worker_thread(parent=self, worker=self.this_worker)
        self.thread.update_signal.connect(self.update_GUI)
        self.thread.done_signal.connect(self.thread.quit)
        self.start_comp.connect(self.thread.start_computation)
        self.thread.start()

    ###############################
    # Here is what should update the GUI at every iteration of Worker.some_complex_processing()
    # The message appears to be getting sent, due to seeing the print statement in the console, but the GUI is not updated.
    @QtCore.pyqtSlot(int)
    def update_GUI(self, iteration):
        self.step.setText(0, str(iteration))
        #time.sleep(0.1)
        print iteration

    def start_signal_emit(self):
        self.start_comp.emit()

# GUI stuff
class MainWindow(QtGui.QMainWindow):
    def __init__(self, parent = None):
        QtGui.QMainWindow.__init__(self)
        self.tab_list = []
        self.setTabShape(QtGui.QTabWidget.Rounded)
        self.centralwidget = QtGui.QWidget(self)
        self.top_level_layout = QtGui.QGridLayout(self.centralwidget)

        self.tabWidget = QtGui.QTabWidget(self.centralwidget)
        self.top_level_layout.addWidget(self.tabWidget, 1, 0, 25, 25)

        process_button = QtGui.QPushButton("Process")
        self.top_level_layout.addWidget(process_button, 0, 1)
        QtCore.QObject.connect(process_button, QtCore.SIGNAL("clicked()"), self.process)

        self.setCentralWidget(self.centralwidget)
        self.centralwidget.setLayout(self.top_level_layout)

        # Make Tabs in loop from button
        for i in range(0,10):
            name = 'tab' + str(i)
            self.tab_list.append(Tab(self.tabWidget, Worker(name)))
            self.tabWidget.addTab(self.tab_list[-1], name)

    # Do the processing
    def process(self):
        for tab in self.tab_list:
            tab.start_signal_emit()
        return

if __name__ == "__main__":
    app = QtGui.QApplication([])
    win = MainWindow()
    win.show()
    sys.exit(app.exec_())

更多信息: 我正在编写一个程序,我想从中产生几个进程,并让它们在整个处理过程中不断显示它们的进度。我希望程序是多处理的,以便尽可能地从程序中获得最佳速度。

目前,我正在尝试使用线程来生成进程并使用信号和插槽来更新 GUI,同时队列不断检索数据。使用 print 语句时,queuessignalsslots 似乎可以工作,但无法更新 GUI。如果有人对我应该如何构建它以使程序更易于管理有任何其他建议,我想学习。

编辑:我已经进行了 Min Lin 提出的调整,另外将 Worker 设置为 QObject 以便 moveToThread() 可以工作。
这是我目前拥有的新代码:

from PyQt4 import QtCore, QtGui
import multiprocessing as mp
import numpy as np
import sys
import time

class Worker(QtCore.QObject):
    update_signal = QtCore.pyqtSignal(int)
    done_signal = QtCore.pyqtSignal()

    def __init__(self, some_var):
        QtCore.QObject.__init__(self, parent=None)
        self.some_var = some_var
        self.iteration = 0
        self.queue = mp.Queue()
        self.process = mp.Process(target=self.some_complex_processing, args=(self.queue,))

    def some_complex_processing(self, queue):
        for i in range(0,5000):
            self.iteration += 1
            queue.put(self.iteration)
        queue.put('done with processing')

    @QtCore.pyqtSlot()
    def start_computation(self):
        self.process.start()
        while(True):
            try:
                message = self.queue.get()
                self.update_signal.emit(message)
            except EOFError:
                pass
            if message == 'done with processing':
                self.done_signal.emit()
                break
        self.process.join()
        return



class Tab(QtGui.QTabWidget):
    start_comp = QtCore.pyqtSignal()
    def __init__(self, parent, this_worker):
        self.parent = parent
        self.this_worker = this_worker
        QtGui.QTabWidget.__init__(self, parent)

        self.treeWidget = QtGui.QTreeWidget(self)
        self.properties = QtGui.QTreeWidgetItem(self.treeWidget, ["Properties"])
        self.step = QtGui.QTreeWidgetItem(self.properties, ["Iteration #"])

        # Use QThread is enough
        self.thread = QtCore.QThread();
        # Change the thread affinity of worker to self.thread.
        self.this_worker.moveToThread(self.thread);
        self.this_worker.update_signal.connect(self.update_GUI)
        self.this_worker.done_signal.connect(self.thread.quit)
        self.start_comp.connect(self.this_worker.start_computation)
        self.thread.start()

    ###############################
    # Here is what should update the GUI at every iteration of Worker.some_complex_processing()
    # The message appears to be getting sent, due to seeing the print statement in the console, but the GUI is not updated.
    @QtCore.pyqtSlot(int)
    def update_GUI(self, iteration):
        self.step.setText(0, str(iteration))
        #time.sleep(0.1)
        print iteration

    def start_signal_emit(self):
        self.start_comp.emit()

# GUI stuff
class MainWindow(QtGui.QMainWindow):
    def __init__(self, parent = None):
        QtGui.QMainWindow.__init__(self)
        self.tab_list = []
        self.setTabShape(QtGui.QTabWidget.Rounded)
        self.centralwidget = QtGui.QWidget(self)
        self.top_level_layout = QtGui.QGridLayout(self.centralwidget)

        self.tabWidget = QtGui.QTabWidget(self.centralwidget)
        self.top_level_layout.addWidget(self.tabWidget, 1, 0, 25, 25)

        process_button = QtGui.QPushButton("Process")
        self.top_level_layout.addWidget(process_button, 0, 1)
        QtCore.QObject.connect(process_button, QtCore.SIGNAL("clicked()"), self.process)

        self.setCentralWidget(self.centralwidget)
        self.centralwidget.setLayout(self.top_level_layout)

        # Make Tabs in loop from button
        for i in range(0,10):
            name = 'tab' + str(i)
            self.tab_list.append(Tab(self.tabWidget, Worker(name)))
            self.tabWidget.addTab(self.tab_list[-1], name)

    # Do the processing
    def process(self):
        for tab in self.tab_list:
            tab.start_signal_emit()
        return

if __name__ == "__main__":
    app = QtGui.QApplication([])
    win = MainWindow()
    win.show()
    sys.exit(app.exec_())

感谢您提供的所有答案,我很欣赏每个人在描述他们认为是解决方案的想法时所采用的详细程度,但不幸的是,我还不能执行在在 GUI 上显示对象的属性时它们所属的对象。
但是,我从这篇文章中学到了很多东西,这让我意识到我目前拥有的线程版本正在挂起 GUI,因为 GUI 更新功能太大并且需要太多处理。

所以,我在我的多线程版本中采用了QTimer() 方法,它的性能要好得多!我会建议任何面临类似问题的人至少尝试类似的事情。

我不知道这种解决 GUI 更新问题的方法,现在它是对我所面临问题的伪或临时修复。

【问题讨论】:

标签: python multithreading qt user-interface multiprocessing


【解决方案1】:

GUI 应用程序非常适合测试东西,因为它很容易生成新任务并可视化正在发生的事情,所以我写了一个小示例应用程序(Screenshot,代码如下),因为我确实想学习它为我自己。

起初,我采取了与您类似的方法,尝试实现消费者/生产者模式,我在后台进程中苦苦挣扎,执行无限循环以等待新工作,并为自己负责来回通信。然后我发现了Pool 接口,然后我可以用几行代码替换所有那些可怕的代码。您只需要一个池和几个回调:

#!/usr/bin/env python3
import multiprocessing, time, random, sys
from PySide.QtCore import * # equivalent: from PyQt4.QtCore import *
from PySide.QtGui import *   # equivalent: from PyQt4.QtGui import *

def compute(num):
    print("worker() started at %d" % num)
    random_number = random.randint(1, 6)
    if random_number in (2, 4, 6):
        raise Exception('Random Exception in _%d' % num)
    time.sleep(random_number)
    return num

class MainWindow(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)
        self.toolBar = self.addToolBar("Toolbar")
        self.toolBar.addAction(QAction('Add Task', self, triggered=self.addTask))
        self.list = QListWidget()
        self.setCentralWidget(self.list)

        # Pool of Background Processes
        self.pool = multiprocessing.Pool(processes=4)

    def addTask(self):
        num_row = self.list.count()
        self.pool.apply_async(func=compute, args=(num_row,), callback=self.receiveResult,
                              error_callback=self.receiveException)
        item = QListWidgetItem("item %d" % num_row)
        item.setForeground(Qt.gray)
        self.list.addItem(item)

    def receiveResult(self, result):
        assert isinstance(result, int)
        print("end_work(), where result is %s" % result)
        self.list.item(result).setForeground(Qt.darkGreen)

    def receiveException(self, exception):
        error = str(exception)
        _pos = error.find('_') + 1
        num_row = int(error[_pos:])
        item = self.list.item(num_row)
        item.setForeground(Qt.darkRed)
        item.setText(item.text() + ' Retry...')
        self.pool.apply_async(func=compute, args=(num_row,), callback=self.receiveResult,
                              error_callback=self.receiveException)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    main_window = MainWindow()
    main_window.show()
    sys.exit(app.exec_())

编辑:我做了另一个例子,使用 QTimer 而不是回调,定期检查队列中的条目,更新 QProgressBar:

#!/usr/bin/env python3
import multiprocessing, multiprocessing.pool, time, random, sys
from PySide.QtCore import *
from PySide.QtGui import *

def compute(num_row):
    print("worker started at %d" % num_row)
    random_number = random.randint(1, 10)
    for second in range(random_number):
        progress = float(second) / float(random_number) * 100
        compute.queue.put((num_row, progress,))
        time.sleep(1)
    compute.queue.put((num_row, 100))

def pool_init(queue):
    # see http://stackoverflow.com/a/3843313/852994
    compute.queue = queue

class MainWindow(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)
        self.toolBar = self.addToolBar("Toolbar")
        self.toolBar.addAction(QAction('Add Task', self, triggered=self.addTask))
        self.table = QTableWidget()
        self.table.verticalHeader().hide()
        self.table.setColumnCount(2)
        self.setCentralWidget(self.table)

        # Pool of Background Processes
        self.queue = multiprocessing.Queue()
        self.pool = multiprocessing.Pool(processes=4, initializer=pool_init, initargs=(self.queue,))

        # Check for progress periodically
        self.timer = QTimer()
        self.timer.timeout.connect(self.updateProgress)
        self.timer.start(2000)

    def addTask(self):
        num_row = self.table.rowCount()
        self.pool.apply_async(func=compute, args=(num_row,))
        label = QLabel("Queued")
        bar = QProgressBar()
        bar.setValue(0)
        self.table.setRowCount(num_row + 1)
        self.table.setCellWidget(num_row, 0, label)
        self.table.setCellWidget(num_row, 1, bar)

    def updateProgress(self):
        if self.queue.empty(): return
        num_row, progress = self.queue.get() # unpack
        print("received progress of %s at %s" % (progress, num_row))
        label = self.table.cellWidget(num_row, 0)
        bar = self.table.cellWidget(num_row, 1)
        bar.setValue(progress)
        if progress == 100:
            label.setText('Finished')
        elif label.text() == 'Queued':
            label.setText('Downloading')
        self.updateProgress() # recursion

if __name__ == '__main__':
    app = QApplication(sys.argv)
    main_window = MainWindow()
    main_window.show()
    sys.exit(app.exec_())

【讨论】:

  • 感谢@valmynd,我相信这对社区中的许多人都非常有帮助。但是,我注意到这里的apply_async 函数使用了为callback 提供的方法的返回值。换句话说,callback=self.recieveResult 被调用,并将来自compute 的结果作为参数给出。您如何使这种设置成为可能,以便在循环中的任何点或在compute 调用的函数中等从进程中获取数据?
  • @chase 我发现使用这样的回调的最简单方法是将函数作为参数传递给您的compute。这样它就可以随时调用recieveResult。唯一的问题是这不是线程安全的,如果您的处理函数执行任何复杂的操作,可能会出现问题。当然,解决这个问题的一种方法是创建一个结果队列,您的compute fxn 写入该结果队列并从您的处理函数读取。如果处理程序在自己的线程中,我觉得它会工作。
  • @chase 我编辑了我的答案并添加了另一个没有回调的示例,它可以满足您的要求并且可以被认为更安全。第一个示例有一个严重的缺陷:无法从回调方法中更新 QProgressbar,因为它似乎在 GUI 线程之外运行(here 有人遇到过类似问题)。
  • 感谢@valmynd,看起来您实现了@DaveTheScientist 在他之前的评论中描述的内容。我喜欢这个解决方案,因为它看起来实际上是在更新 GUI 进度条!但是,这可能只是我的设置,但 GUI 交互似乎不适用于给定的第二个设置。在addTask 函数启动之前单击时,列会从灰色变为蓝色,而一旦单击“添加任务”按钮,列将不再交互。是不是因为 self.timer 连接到 self.updateProgess ,它在同一个线程中?
  • @chase 抱歉,我无法在我的机器上重现此行为,我在 Ubuntu 12.04 上使用 Python 3.2 并在 Windows 8 中使用 Python 3.3 进行了尝试(没有尝试使用 PyQt 而不是 PySide,尽管)。即使以 10 毫秒而不是 2000 毫秒的间隔调用 QTimer.start() 也效果很好。这种反应迟钝是否只发生在很短的时间内?我认为updateProgess() 放在另一个线程中不会有帮助,因为GIL。我会坚持下去,也许我有机会在上网本上测试一下。
【解决方案2】:

非常感谢您发布这个问题,以及所有贡献者的意见。它为我试验 PyQt 和多处理提供了一个有用的脚手架。

我从问题中列出的第二个代码示例开始。我的更改和 cmets:

  • 在 Windows 上,Process.__init__()所有 参数必须是可腌制的。您会看到@valmynd 将他/她的compute 函数设为顶级函数,正是出于这个原因。这部分是因为多处理将重新导入包含目标函数的模块。为了提醒自己这一点,我尝试将目标函数放入它自己的模块中(并确保任何信息都作为可腌制参数传递)。我已经将复杂的处理功能移到了它自己的名为workermodule.py的模块中。
  • 如果复杂处理函数中没有足够的工作,循环完成太快,以至于无法在 GUI 中看到任何更改。因此,我在复杂的处理函数中添加了一些额外的(无用的)工作。如评论中所述,您可以time.sleep,但将所有内核点亮一会儿会更令人满意。
  • 通过以下两个代码 sn-ps,我得到了一个平滑的 GUI,迭代值不断更新,子流程全速运行。
  • 请注意,self.process 可以使用 两个 队列作为参数创建,一个用于输入,一个用于输出。然后复杂的处理函数必须定期检查输入队列的数据。

workermodule.py:

import time

def some_complex_processing(queue):
    iteration = 0
    for i in range(0,5000):
        iteration += 1
        queue.put(iteration)
        #time.sleep(20e-3) # ms
        # You could time.sleep here to simulate a
        # long-running process, but just to prove
        # that we're not cheating, let's make this
        # process work hard, while the GUI process
        # should still have smooth drawing.
        for x in range(100000):
            y = x
    queue.put('done with processing')

mainfile.py:

from PyQt4 import QtCore, QtGui
import multiprocessing as mp
import sys
import workermodule

class Worker(QtCore.QObject):
    update_signal = QtCore.pyqtSignal(int)
    done_signal = QtCore.pyqtSignal()

    def __init__(self, some_var):
        QtCore.QObject.__init__(self, parent=None)
        self.some_var = some_var
        self.queue = mp.Queue()
        self.process = mp.Process(
            target=workermodule.some_complex_processing,
            args=(self.queue,)
            )

    @QtCore.pyqtSlot()
    def start_computation(self):
        self.process.start()
        while True:
            try:
                message = self.queue.get()
                self.update_signal.emit(message)
            except EOFError:
                pass
            if message == 'done with processing':
                self.done_signal.emit()
                break
        self.process.join()
        return

class Tab(QtGui.QTabWidget):
    start_comp = QtCore.pyqtSignal()
    def __init__(self, parent, this_worker):
        self.parent = parent
        self.this_worker = this_worker
        QtGui.QTabWidget.__init__(self, parent)

        self.treeWidget = QtGui.QTreeWidget(self)
        self.properties = QtGui.QTreeWidgetItem(self.treeWidget, ["Properties"])
        self.step = QtGui.QTreeWidgetItem(self.properties, ["Iteration #"])

        # Use QThread is enough
        self.thread = QtCore.QThread();
        # Change the thread affinity of worker to self.thread.
        self.this_worker.moveToThread(self.thread);
        self.this_worker.update_signal.connect(self.update_GUI)
        self.this_worker.done_signal.connect(self.thread.quit)
        self.start_comp.connect(self.this_worker.start_computation)
        self.thread.start()

    ###############################
    # Here is what should update the GUI at every iteration of Worker.some_complex_processing()
    # The message appears to be getting sent, due to seeing the print statement in the console, but the GUI is not updated.
    @QtCore.pyqtSlot(int)
    def update_GUI(self, iteration):
        self.step.setText(0, str(iteration))
        print iteration

    def start_signal_emit(self):
        self.start_comp.emit()

# GUI stuff
class MainWindow(QtGui.QMainWindow):
    def __init__(self, parent = None):
        QtGui.QMainWindow.__init__(self)
        self.tab_list = []
        self.setTabShape(QtGui.QTabWidget.Rounded)
        self.centralwidget = QtGui.QWidget(self)
        self.top_level_layout = QtGui.QGridLayout(self.centralwidget)

        self.tabWidget = QtGui.QTabWidget(self.centralwidget)
        self.top_level_layout.addWidget(self.tabWidget, 1, 0, 25, 25)

        process_button = QtGui.QPushButton("Process")
        self.top_level_layout.addWidget(process_button, 0, 1)
        QtCore.QObject.connect(process_button, QtCore.SIGNAL("clicked()"), self.process)

        self.setCentralWidget(self.centralwidget)
        self.centralwidget.setLayout(self.top_level_layout)

        # Make Tabs in loop from button
        for i in range(0,10):
            name = 'tab' + str(i)
            self.tab_list.append(Tab(self.tabWidget, Worker(name)))
            self.tabWidget.addTab(self.tab_list[-1], name)

    # Do the processing
    def process(self):
        for tab in self.tab_list:
            tab.start_signal_emit()
        return

if __name__ == "__main__":
    app = QtGui.QApplication([])
    win = MainWindow()
    win.show()
    sys.exit(app.exec_())

【讨论】:

  • 你已经用一个属性替换了一个getter函数两次,即:self.parent = parentself.centralWidget = QtGui.QWidget(self)。这不是一个很好的做法。
【解决方案3】:

你在 Worker_Thread 中所做的一切都应该被移到 Worker 中。 Qt 根据创建对象的位置为每个对象分配线程亲和性。 Worker_Thread 对象是在主线程中创建的,因此它具有主线程亲和性。如果来自主线程的信号连接到在主线程中创建的对象的插槽,则该插槽也将在主线程中执行。 (无论是 QueuedConnection 还是 DirectConnection)。并且插槽挡住了 GUI。

这样做:

class Worker:
    update_signal = QtCore.pyqtSignal(int)
    done_signal = QtCore.pyqtSignal()

    def __init__(self, some_var):
        self.some_var = some_var
        self.iteration = 0
        self.queue = mp.Queue()
        self.process = mp.Process(target=self.some_complex_processing, args=(self.queue,))

    def some_complex_processing(self, queue):
        for i in range(0,5000):
            self.iteration += 1
            queue.put(self.iteration)
        queue.put('done with processing')

    @QtCore.pyqtSlot()
    def start_computation(self):
        self.process.start()
        while(True):
            try:
                message = self.queue.get()
                self.update_signal.emit(message)
            except EOFError:
                pass
            if message == 'done with processing':
                self.done_signal.emit()
                break
        self.process.join()
        return



class Tab(QtGui.QTabWidget):
    start_comp = QtCore.pyqtSignal()
    def __init__(self, parent, this_worker):
        self.parent = parent
        self.this_worker = this_worker
        QtGui.QTabWidget.__init__(self, parent)

        self.treeWidget = QtGui.QTreeWidget(self)
        self.properties = QtGui.QTreeWidgetItem(self.treeWidget, ["Properties"])
        self.step = QtGui.QTreeWidgetItem(self.properties, ["Iteration #"])

        # Use QThread is enough
        self.thread = QtCore.QThread();
        # Change the thread affinity of worker to self.thread.
        self.this_worker.moveToThread(self.thread);
        self.this_worker.update_signal.connect(self.update_GUI)
        self.this_worker.done_signal.connect(self.thread.quit)
        self.start_comp.connect(self.this_worker.start_computation)
        self.thread.start()

    ###############################
    # Here is what should update the GUI at every iteration of Worker.some_complex_processing()
    # The message appears to be getting sent, due to seeing the print statement in the console, but the GUI is not updated.
    @QtCore.pyqtSlot(int)
    def update_GUI(self, iteration):
        self.step.setText(0, str(iteration))
        #time.sleep(0.1)
        print iteration

    def start_signal_emit(self):
        self.start_comp.emit()

【讨论】:

  • 非常感谢您的帮助。这当然已经触及问题的核心,但现在由于某种原因,我无法将消息从更新 GUI 的过程中取出。我将上传对我的答案的编辑并继续努力,看看您或我是否有任何其他想法。
  • 您知道为什么没有使用此代码在 GUI 中设置文本字段吗?
【解决方案4】:

好的,我对 Qt 本身并不熟悉,但我用 Tkinter 做过类似的工作。我很确定您在这里遇到了 Python 的 Global Interpreter Lock。具体来说,您在同一个线程中启动队列和 GUI 应用程序,因此当队列阻塞等待输入时,GUI 也会阻塞。尝试在自己的线程或进程中启动app = QtGui.QApplication([])。使用队列编写 GUI 总是有点复杂,而且我发现它通常需要比我开始预期的多至少 1 层线程。

【讨论】:

  • 谢谢@DaveTheScientist,我之前确实听过这个 GIL 对我的问题的解释,但我不太明白我是如何在与 GUI 相同的线程中创建队列的在单独的 Worker_Thread 类中调用它。它不应该将所有消息从进程传递到沿选项卡旁边创建的线程,然后将这些消息传递到我的设置中的主 GUI 线程吗?这至少是我给出的设置的目标。如果没有,但是如果不是这样,我是否应该在单独的线程中启动 GUI?
  • 好的@chase,我想我明白你是如何设置这一切的了。我现在的问题是,在您的MainWindow class 中,tab.start_signal_emit() 什么时候返回?是否有可能在每个选项卡完成之前一直阻塞?如果是这种情况,它将启动选项卡的进程,并且它们将按预期调用 self.step.setText(0, str(iteration))print,但这些 setText 调用将被添加到您的 GUI 的内部队列中,直到实际运行所有tab.start_signal_emit() 调用都返回。
  • 如果这是问题所在,我建议您在 MainWindow 的 for 循环中为每次迭代启动一个新线程(或来自 mp 的进程)。另一件事可以尝试在Worker_Thread.start_computation() 中,当它调用self.queue.get() 时,添加一个超时值:self.queue.get(True, 1.0)
  • 我使用以下代码为我的 for 循环中的每个进程创建了一个线程:# Do the processing def process(self): for tab in self.tab_list: thread = QtCore.QThread(self) tab.moveToThread(thread) tab.start_signal_emit() thread.start() return 但我仍然遇到与以前相同的问题。我也尝试了超时值,但没有运气。我认为林敏的回答与正在发生的事情有关,似乎线程亲和力也是激发您对答案的想法的原因,但还没有成功。
猜你喜欢
  • 2012-06-11
  • 2012-02-20
  • 1970-01-01
  • 1970-01-01
  • 2014-07-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多