Python:使用多处理池从外部函数更新小部件

Posted

技术标签:

【中文标题】Python:使用多处理池从外部函数更新小部件【英文标题】:Python: Update Widgets from outside function using multipocessing Pool 【发布时间】:2014-03-07 22:04:28 【问题描述】:

如果您能告诉我为什么按确定按钮不会更新 lineedit 文本字段,我将不胜感激。

from PyQt4 import QtCore, QtGui

def externalFunc(arg):
    print '\n\t Accessing lineedit from outside. Result   "', jobDialog.lineEdit.text(), '"   OK'
    print "Attempting to change the lineEdit field to", arg
    jobDialog.lineEdit.setText(arg)
    print "...Completed."

class MainWindow(QtGui.QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.main_layout = QtGui.QVBoxLayout()

        self.lineEdit=QtGui.QLineEdit('Initial Text')
        self.main_layout.addWidget(self.lineEdit)

        ok_button = QtGui.QPushButton("OK")
        ok_button.clicked.connect(self.OK)
        self.main_layout.addWidget(ok_button)       

        central_widget = QtGui.QWidget()
        central_widget.setLayout(self.main_layout)
        self.setCentralWidget(central_widget)

    def OK(self):
        myList=['One','Two','Three']
        from multiprocessing import Pool
        pool = Pool(processes=10)
        try: pool.map_async( externalFunc, myList)
        except Exception, e: print e

if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)

    jobDialog = MainWindow()
    jobDialog.resize(480, 320)
    jobDialog.show()
    sys.exit(app.exec_())

另一个例子,这次是进度条。

这个而不是仅仅未能更新会导致整个进程崩溃。同样的问题:为什么会发生以及如何解决。

from PyQt4 import QtCore, QtGui

class PbWidget(QtGui.QProgressBar):
    def __init__(self, parent=None, total=20):
        super(PbWidget, self).__init__()
        self.setMinimum(1)
        self.setMaximum(total)        
        self._active = False  

    def update_bar(self, to_add_number):
        while True:
            time.sleep(0.01)
            value = self.value() + to_add_number            
            self.setValue(value)
            QtGui.qApp.processEvents()
            if (not self._active or value >= self.maximum()):                
                break
        self._active = False

    def closeEvent(self, event):
        self._active = False

def externalFunc(arg=None):
    print "Attempting to update Progress Bar "
    jobDialog.pb.update_bar(10)
    print "...Completed."

class MainWindow(QtGui.QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.main_layout = QtGui.QVBoxLayout()

        self.pb=PbWidget(total=101)
        self.main_layout.addWidget(self.pb)

        ok_button = QtGui.QPushButton("OK")
        ok_button.clicked.connect(self.OK)
        self.main_layout.addWidget(ok_button)       

        central_widget = QtGui.QWidget()
        central_widget.setLayout(self.main_layout)
        self.setCentralWidget(central_widget)

    def OK(self):
        # externalFunc()
        myList=[1,2,3]
        from multiprocessing import Pool
        pool = Pool(processes=10)
        pool.map_async( externalFunc, myList)

if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)

    jobDialog = MainWindow()
    jobDialog.resize(480, 320)
    jobDialog.show()
    sys.exit(app.exec_())

【问题讨论】:

【参考方案1】:

这是对我有用的解决方案的描述。

multiprocessing 的 Pool 方法(例如 ma​​p_async())启动的子进程无法与主进程中声明的变量(对象)通信(产生所有子流程的流程)。如果我们想要的只是发送任务进行处理就足够了。但通常我们需要来自子流程的“实时”反馈(当它们仍在处理收到的任务时)。为了在主进程及其所有子进程中为子进程提供变量“可见”(“共享”),我们使用 multiprocessingManager()。语法很简单:

from multiprocessing import Manager
manager = Manager()

在声明 Manager() 的实例后(这里称为 manager),我们继续声明一个变量...到目前为止,我知道 aka-dict 和 aka-list 类型。使用以下语法:

myDict=manager.dict()

除了存储和检索数据之外,这些变量还可用于设置/读取/重置用于启动/停止 while 循环函数(某种事件侦听器)的 True/False 标志。这个想法是我们在后台运行一个 while 循环,它不断地监听(监视)这种 True 或 False 变量的状态。 这是修改后的代码,其中进度条由多处理 Pool() map_async() 方法启动的子进程更新。

from PyQt4 import QtCore, QtGui

from multiprocessing import Process, Manager, Pool
manager = Manager()
myDict=manager.dict()
myDict['state'] = True
myDict['value'] = 0


class PbWidget(QtGui.QProgressBar):
    def __init__(self, parent=None, total=20):
        super(PbWidget, self).__init__()
        self.setMinimum(1)
        self.setMaximum(total)        
        self._active = False  

    def update_bar(self, to_add_number):
        while True:
            time.sleep(0.01)
            value = self.value() + to_add_number            
            self.setValue(value)
            QtGui.qApp.processEvents()
            if (not self._active or value >= self.maximum()):                
                break
        self._active = False

    def closeEvent(self, event):
        self._active = False




def externalFunc(arg=None):
    print "\t\t Attempting to update Progress Bar by changing dictionary values"
    myDict['value']=arg
    myDict['state']=True

class MainWindow(QtGui.QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.progressBarCurrentValue=0
        self.main_layout = QtGui.QVBoxLayout()

        self.pb=PbWidget(total=101)
        self.main_layout.addWidget(self.pb)

        ok_button = QtGui.QPushButton("OK")
        ok_button.clicked.connect(self.OK)
        self.main_layout.addWidget(ok_button)       

        central_widget = QtGui.QWidget()
        central_widget.setLayout(self.main_layout)
        self.setCentralWidget(central_widget)


    def OK(self):
        myList=[10,10,10,10,10,10,10,10,10,10,10,10,10]
        pool = Pool(processes=10)
        pool.map_async( externalFunc, myList)

    def EventListener(self):
        while myDict['state']:              
            value=myDict['value']
            self.pb.update_bar(value)

            self.progressBarCurrentValue=self.pb.value()
            print "...running sicne current Progress Bar value is < 99:", self.progressBarCurrentValue, myDict['state']

            if self.progressBarCurrentValue>99: 
                myDict['state'] = False
                print "...stopping"



if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)    
    jobDialog = MainWindow()
    jobDialog.resize(480, 320)
    jobDialog.show()
    jobDialog.EventListener()
    sys.exit(app.exec_())

【讨论】:

【参考方案2】:

它不起作用,因为进程之间不共享内存(通常)。因此,一个进程不能直接更新另一个进程中的小部件。即使您确实共享内存,您的程序也会经常崩溃,因为 Qt 不允许同时访问 QObject(例如通过线程或多处理)。

您需要做的是让您的子进程将消息发送回主进程的主线程,并从它们解释消息并更新小部件。

【讨论】:

我可以从 mutliprocess.pool 子进程启动的“对话”的唯一方法是写入一些可以从主进程及其所有子进程访问的“特殊”变量。如何将消息从子进程发送回主进程的主线程? 顺便说一句,如果主进程及其所有子进程(由 muliprocess.Pool() 启动的子进程)之间没有共享内存,那么为什么子进程能够使用 jobDialog.lineEdit.text() 从 lineEdit 读取?如果主进程和子进程不共享相同的对象,则不应进行读写访问。请指教。 取决于平台。例如,在 Linux 多处理系统上进行系统分叉,当您访问在主进程中创建的子进程中的变量时,会生成一个副本。在 windows 上,所有变量的副本都会立即生成。所以变量存在,但它们不指向同一个对象。您可以以某种方式共享内存,但 Qt 又不会高兴。如果您使用 Python 多处理,请查看用于在进程之间发送消息的多处理队列。

以上是关于Python:使用多处理池从外部函数更新小部件的主要内容,如果未能解决你的问题,请参考以下文章

无法使用颤振 Bloc 模式更新小部件外部的 UI

Python:在使用多处理时更新 Tkinter

PyQt QTabWidget 多角小部件

在页脚区域中为小部件添加第四列

在有状态小部件外部调用方法以使用颤振 Bloc 更新 UI

多个 Android Widget 实例仅更新最后一个小部件