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 中运行计算。但是计算本身非常繁重,所以我真的需要完全在一个单独的进程(和核心)中运行它。谢谢!

4

1 回答 1

1

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

示例用于让用户无需等待从数据库中编辑或获取数据的耗时过程,但您希望 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)))
于 2020-06-17T16:44:58.067 回答