我正在尝试创建一个由多处理过程更新的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中运行计算。但计算本身非常繁重,所以我真的需要在一个单独的进程(和核心)中运行它。谢谢!
答案 0 :(得分: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)))