1

マルチプロセッシング プロセスによって更新される PySide GUI を作成しようとしています。たとえば、計算後に更新されるウィンドウにテキストを表示する PySide GUI です。QThread を使用することで、問題なく GUI を更新できます。ただし、QThread の代わりにマルチプロセッシング プロセスを使用して同じことを行おうとすると (sys.exit の直前の 2 行のコードを参照)、エラーが発生します。最小限の例を次に示します。

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 はメイン プロセスからしか操作できないため、新しいプロセスから 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

もう 1 つの提案は、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)))
于 2020-06-17T16:44:58.067 に答える