从多处理过程更新PySide GUI

问题描述 投票:1回答:1

我正在尝试创建一个通过多处理过程进行更新的PySide GUI,例如,一个PySide GUI在一个经过一些计算后即可更新的窗口中显示文本。通过使用QThread,我可以毫无问题地更新GUI。但是,如果尝试使用多处理进程而不是QThread来执行相同的操作(请参阅sys.exit之前的两行代码),则会出现错误。这是一个最小的示例:

import sys
from PySide import QtCore, QtGui
from multiprocessing import Process
import time

class GUI(QtGui.QMainWindow):

    def __init__(self):
        super(GUI, self).__init__()

        self.initUI()

    def initUI(self):

        self.text = "normal text"
        self.setGeometry(300, 300, 500, 300)
        self.setWindowTitle('TestGUI')
        self.show()

    def paintEvent(self, event):

        qp = QtGui.QPainter()
        qp.begin(self)
        self.drawText(event, qp)
        qp.end()

    def drawText(self, event, qp):

        qp.setPen(QtGui.QColor(0,0,0))
        qp.setFont(QtGui.QFont('Decorative', 50))
        qp.drawText(event.rect(), QtCore.Qt.AlignCenter, self.text)


    @QtCore.Slot(str)
    def setText(self, text):
        self.text = text
        print self.text
        self.repaint()


class Communicate(QtCore.QObject):
    updateGUI = QtCore.Signal(str)


class MyThread(QtCore.QThread):

    def __init__(self, com):
        super(MyThread, self).__init__()
        self.com = com

    def run(self):
        count = 0
        while True:
            self.com.updateGUI.emit("update %d" % count)
            count += 1
            time.sleep(1)


def loopEmit(com):
    while True:
        com.updateGUI.emit(time.ctime())
        time.sleep(1)


# Create and show GUI
app = QtGui.QApplication(sys.argv)
gui = GUI()
gui.show()

# connect signal and slot properly
com = Communicate()
com.updateGUI.connect(gui.setText)

thread = MyThread(com)
thread.start() # this works fine

time.sleep(0.5)

p = Process(target=loopEmit, args=[com])
p.start() # this breaks

sys.exit(app.exec_())

问题是,显然只能从主进程中操作GUI,因此尝试从新进程中进行操作会引发此错误:

The process has forked and you cannot use this CoreFoundation functionality safely. You MUST exec().
Break on __THE_PROCESS_HAS_FORKED_AND_YOU_CANNOT_USE_THIS_COREFOUNDATION_FUNCTIONALITY___YOU_MUST_EXEC__() to debug.

我的即时响应是-只是在QThread中运行计算。但是计算本身非常繁琐,因此我真的需要在单独的进程(和核心)中完全运行它。谢谢!

python user-interface multiprocessing pyside python-multiprocessing
1个回答
0
投票

另一个建议可能是使用Qt / PySide信号系统来完全避免任何形式的阻塞。

并且该示例用于耗时的过程来编辑或从DATABASE中获取用户不需要等待的数据,但是您希望UI在可用时进行更新。

下面是一个例子。虽然没有测试ui或测试数据,但是示例显示了一个QT线程类,该类设置为在准备好在调用应用程序中显示或使用数据时发出信号。

import pprint
try:
    from PySide import QtCore
except:
    from PySide2 import QtCore
from custom_module import DATABASE
from ui.data_widget import DataWidget

class BatchThread(QtCore.QThread):
    """
    Process the list of database batch queries as a threaded process and emit list when 
    complete.
    Or you could have the run process constantly emit signals if it is a looping process 
    you want to keep signaling it is processing.
    """
    sig = QtCore.Signal(list)
    def __init__(self, parent=None):
        QtCore.QThread.__init__(self, parent)
        self.data = {}

    def run(self):
        try:
            result = DATABASE.batchProcess(self.data)
            self.sig.emit(result)
        except:
            self.sig.emit([])


def executing_script(data=[]):
    """
    Main app that would have the UI elements you would want to update.
    """
    #   Assumption you have setup a textEdit widget called self.ui.displayWidget
    def __init__(self, given_args=[]):
        QtWidgets.QMainWindow.__init__(self, parent=None)
        self.ui = DataWidget()
        self.ui.setupUi(self)

        #   Create an instance of the independent process.
        self.async_process = BatchThread(self)
        #   Connect the sig signal to a function to execute when the result is emitted.
        self.async_process.sig.connect(self.display_result)

        #   Set the instance with data to process.
        self.async_process.data = ['<data to process you dont want to wait on>']
        #   Start it processing the data.
        self.async_process.run()
        #   Script execution continues.

    def display_result(self, result=[]):
        """
        When the process is finished, display it's result.
        Since your instance signal emits a list, that is what will be received along with the call to the function.
        """
        self.ui.displayWidget.clear()
        self.ui.displayWidget.append(str(pprint.pprint(result)))
© www.soinside.com 2019 - 2024. All rights reserved.