简体   繁体   English

从多处理进程更新 PySide GUI

[英]Update PySide GUI from multiprocessing Process

I'm trying to create a PySide GUI that gets updated by a multiprocessing Process, for example a PySide GUI that displays text in a window that gets updated after some computation.我正在尝试创建一个由多处理进程更新的 PySide GUI,例如一个 PySide GUI,它在一个窗口中显示文本,该窗口在一些计算后得到更新。 By using a QThread, I am able to update the GUI without any problems.通过使用 QThread,我可以毫无问题地更新 GUI。 However, if I try to do the same using a multiprocessing Process instead of a QThread (cf. the two lines of code just before sys.exit), I get an error.但是,如果我尝试使用多处理进程而不是 QThread 执行相同操作(参见 sys.exit 之前的两行代码),则会出现错误。 Here's a minimal example:这是一个最小的例子:

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_())

The problem is that apparently the GUI can only be manipulated from the main process, so trying to manipulate it from a new process raises this error:问题是显然只能从主进程操作 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.

My immediate response was- just run the computation in a QThread.我的直接反应是 - 只需在 QThread 中运行计算。 But the computation itself is pretty heavy and so I really need to run it in a separate process (and core) altogether.但是计算本身非常繁重,所以我真的需要在一个单独的进程(和核心)中运行它。 Thanks!谢谢!

Another suggestion might be to use the Qt/PySide signaling system to completely avoid any sort of blocking.另一个建议可能是使用 Qt/PySide 信号系统来完全避免任何类型的阻塞。

And example used for letting a time consuming process for editing or acquiring data from a DATABASE that your user doesn't need to wait for, but you want the UI to update once it is available.示例用于让用户无需等待从 DATABASE 编辑或获取数据的耗时过程,但您希望 UI 在可用时进行更新。

Below is an example.下面是一个例子。 Granted don't have a test ui nor test data to present, but the example shows a QT thread class that is set to emit a signal when data is ready to display or use in the calling application.虽然没有测试 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)))

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM