GUI响应与多个进程不一致

2024-10-03 17:22:04 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个脚本,它允许同时执行多个进程。除了并行运行之外,这些进程还向GUI实时报告它们的属性,以允许用户跟踪进程的进度。另外,我相信我已经设置了系统,以便允许用户在任何时间点停止进程,更改该进程的属性,然后使用新属性从原来的位置恢复进程。你知道吗

但是,GUI似乎仍然对进程的启动/停止没有很好的响应,并且没有保留提供给它的属性的更改。你知道吗

我在这个脚本here上得到了一些帮助,但是这个问题似乎不适合那个论坛。你知道吗

这是我目前拥有的代码,我希望修改它以实现我上面列出的所有目标:

from PyQt4 import QtCore, QtGui
import multiprocessing as mp
import sys
import time

def make_data():
    data = {'num': 3, 'num2':4}
    return data

num = 1000000

def some_complex_processing(data, can_run):
    for i in range(data['num'], data['num'] + num):
        can_run.wait()
        data['num'] = i
        data['num2'] = i+100
        time.sleep(0.1)

class Tab(QtGui.QTabWidget):
    def __init__(self, parent, data_init):
        QtGui.QTabWidget.__init__(self, parent)
        self.top_level_layout = QtGui.QGridLayout(self)
        self.frame = QtGui.QFrame(self)
        self.top_level_layout.addWidget(self.frame)
        self.step_label = dict()
        self.step_stacked = dict()
        self.step_text = dict()
        self.step_input = dict()
        for wdgts in data_init.keys():
            self.step_label[wdgts] = QtGui.QLabel(str(wdgts))
            self.step_stacked[wdgts] = QtGui.QStackedWidget()
            self.step_text[wdgts] = QtGui.QLabel(str(data_init[wdgts]))
            self.step_input[wdgts] = QtGui.QLineEdit()
            self.step_stacked[wdgts].addWidget(self.step_text[wdgts])
            self.step_stacked[wdgts].addWidget(self.step_input[wdgts])
            self.top_level_layout.addWidget(self.step_stacked[wdgts])
            self.top_level_layout.addWidget(self.step_label[wdgts])

        self.process_button = QtGui.QPushButton("Process")
        self.top_level_layout.addWidget(self.process_button, 1, 1)
        self.process_button.clicked.connect(self.start_process)

        self.manager = mp.Manager()
        self.data = self.manager.dict(make_data())
        self.Event = self.manager.Event()
        self.process = mp.Process(target=some_complex_processing, args=(self.data,self.Event,))

        self.timer = QtCore.QTimer()
        self.timer.timeout.connect(self.update_GUI)

    def update_GUI(self):
        try:
            for wdgt in self.data.keys():
                self.step_label[wdgt].setText(str(wdgt))
                self.step_text[wdgt].setText(str(self.data[wdgt]))
                self.step_input[wdgt].setText(str(self.data[wdgt]))
        except EOFError:
            QtCore.QObject.disconnect(self.process_button, QtCore.SIGNAL("clicked()"), self.stop_process)
            QtCore.QObject.disconnect(self.process_button, QtCore.SIGNAL("clicked()"), self.start_process)
            self.timer.stop()
            self.process.join()
        return

    def start_process(self):
        self.Event.set()
        for wdgt in self.step_stacked.keys():
            self.step_stacked[wdgt].setCurrentWidget(self.step_text[wdgt])
        self.process.start()
        self.timer.start()
        self.process_button.setText('Stop Processing - (manually adjust data)')
        QtCore.QObject.disconnect(self.process_button, QtCore.SIGNAL("clicked()"), self.start_process)
        QtCore.QObject.connect(self.process_button, QtCore.SIGNAL("clicked()"), self.stop_process)
        return

    def stop_process(self):
        self.Event.clear()
        for wdgt in self.step_stacked.keys():
            self.step_stacked[wdgt].setCurrentWidget(self.step_input[wdgt])
        self.timer.stop()
        self.process_button.setText('Start Processing Again Using Data')
        QtCore.QObject.disconnect(self.process_button, QtCore.SIGNAL("clicked()"), self.stop_process)
        QtCore.QObject.connect(self.process_button, QtCore.SIGNAL("clicked()"), self.start_process)
        return

# GUI stuff
class MainWindow(QtGui.QMainWindow):
    def __init__(self, parent = None):
        QtGui.QMainWindow.__init__(self)
        screen_height = app.desktop().screenGeometry().height()
        screen_width = app.desktop().screenGeometry().width()
        self.resize(int(screen_width*0.2), int(screen_height*0.2))
        self.tab_list = []
        self.setTabShape(QtGui.QTabWidget.Rounded)
        self.centralwidget = QtGui.QWidget(self)
        self.top_level_layout = QtGui.QGridLayout(self.centralwidget)
        self.tabWidget = QtGui.QTabWidget(self.centralwidget)
        self.top_level_layout.addWidget(self.tabWidget, 1, 0, 25, 25)
        self.setCentralWidget(self.centralwidget)
        self.centralwidget.setLayout(self.top_level_layout)
        self.process_all__button = QtGui.QPushButton("Start All Processes")
        self.top_level_layout.addWidget(self.process_all__button, 0, 0)
        QtCore.QObject.connect(self.process_all__button, QtCore.SIGNAL("clicked()"), self.start_all_process)
        # Make Tabs in loop from button
        for i in range(0,10):
            super_cool_data = make_data()
            name = 'tab ' + str(i)
            self.tab_list.append(Tab(self.tabWidget, super_cool_data))
            self.tabWidget.addTab(self.tab_list[-1], name)

    def start_all_process(self):
        self.process_all__button.setText('Stop All Processing')
        QtCore.QObject.disconnect(self.process_all__button, QtCore.SIGNAL("clicked()"), self.start_all_process)
        QtCore.QObject.connect(self.process_all__button, QtCore.SIGNAL("clicked()"), self.stop_all_process)
        for i in self.tab_list:
            i.start_process()

    def stop_all_process(self):
        self.process_all__button.setText('Start All Processing')
        QtCore.QObject.disconnect(self.process_all__button, QtCore.SIGNAL("clicked()"), self.stop_all_process)
        QtCore.QObject.connect(self.process_all__button, QtCore.SIGNAL("clicked()"), self.start_all_process)
        for i in self.tab_list:
            i.stop_process()        

if __name__ == "__main__":
    app = QtGui.QApplication([])
    win = MainWindow()
    win.show()
    sys.exit(app.exec_())

更新:

我对代码做了一些修改,以反映评注中提出的建议。现在,some_complex_processing函数中的循环将不引用循环迭代次数,而是引用存储在其中的数据,这允许用户查看它们所做的更改。你知道吗

另外,我添加了一个时间间隔来更新GUI,这样它现在就不会阻塞。你知道吗

但是,尽管我添加了一个process.daemon = True,但仍然存在保留的僵尸进程的问题。另外,QtCore.QObject.disconnect操作似乎不起作用,正如我包含的几个print语句的输出所示。按钮似乎从未断开。你知道吗

新代码如下:

from PyQt4 import QtCore, QtGui
import multiprocessing as mp
import sys
import time

def make_data():
    data = {'num': 3, 'num2': 4}
    return data

num = 10**8

def some_complex_processing(data, can_run):
    for i in range(data['num'], data['num'] + num):
        can_run.wait()
        data['num'] += 1
        data['num2'] += 100

class Tab(QtGui.QTabWidget):
    def __init__(self, parent, data_init):
        QtGui.QTabWidget.__init__(self, parent)
        self.top_level_layout = QtGui.QGridLayout(self)
        self.frame = QtGui.QFrame(self)
        self.top_level_layout.addWidget(self.frame)
        self.step_label = dict()
        self.step_stacked = dict()
        self.step_text = dict()
        self.step_input = dict()
        for wdgts in data_init.keys():
            self.step_label[wdgts] = QtGui.QLabel(str(wdgts))
            self.step_stacked[wdgts] = QtGui.QStackedWidget()
            self.step_text[wdgts] = QtGui.QLabel(str(data_init[wdgts]))
            self.step_input[wdgts] = QtGui.QLineEdit()
            self.step_stacked[wdgts].addWidget(self.step_text[wdgts])
            self.step_stacked[wdgts].addWidget(self.step_input[wdgts])
            self.top_level_layout.addWidget(self.step_stacked[wdgts])
            self.top_level_layout.addWidget(self.step_label[wdgts])

        self.process_button = QtGui.QPushButton("Process")
        self.top_level_layout.addWidget(self.process_button, 1, 1)
        self.process_button.clicked.connect(self.start_process)

        self.manager = mp.Manager()
        self.data = self.manager.dict(make_data())
        self.Event = self.manager.Event()
        self.process = mp.Process(target=some_complex_processing, args=(self.data,self.Event,))
        self.process.daemon = True

        self.timer = QtCore.QTimer()
        self.timer.timeout.connect(self.update_GUI)
        self.first_start = True

    def update_GUI(self):
        try:
            for wdgt in self.data.keys():
                self.step_label[wdgt].setText(str(wdgt))
                self.step_input[wdgt].setText(str(self.data[wdgt]))
                self.step_text[wdgt].setText(str(self.data[wdgt]))       
        except EOFError:
            QtCore.QObject.disconnect(self.process_button, QtCore.SIGNAL("clicked()"), self.stop_process)
            QtCore.QObject.disconnect(self.process_button, QtCore.SIGNAL("clicked()"), self.start_process)
            self.timer.stop()
            self.process.join()
        return

    def start_process(self):
        self.Event.set()
        for wdgt in self.step_stacked.keys():
            self.step_stacked[wdgt].setCurrentWidget(self.step_text[wdgt])
        if self.first_start==True:
            print 'first start'
            self.process.start()
            self.first_start = False
        else:
            for wdgt in self.data.keys():
                self.data[wdgt] = int(self.step_input[wdgt].text())
            print 'start process again'
        self.timer.start(100)
        self.process_button.setText('Stop Processing - (manually adjust data)')
        QtCore.QObject.disconnect(self.process_button, QtCore.SIGNAL("clicked()"), self.start_process)
        QtCore.QObject.connect(self.process_button, QtCore.SIGNAL("clicked()"), self.stop_process)
        return

    def stop_process(self):
        print 'stopping proccess'
        self.Event.clear()
        for wdgt in self.step_stacked.keys():
            self.step_stacked[wdgt].setCurrentWidget(self.step_input[wdgt])
        self.timer.stop()
        self.process_button.setText('Start Processing Again Using Data')
        QtCore.QObject.disconnect(self.process_button, QtCore.SIGNAL("clicked()"), self.stop_process)
        QtCore.QObject.connect(self.process_button, QtCore.SIGNAL("clicked()"), self.start_process)
        return

# GUI stuff
class MainWindow(QtGui.QMainWindow):
    def __init__(self, parent = None):
        QtGui.QMainWindow.__init__(self)
        screen_height = app.desktop().screenGeometry().height()
        screen_width = app.desktop().screenGeometry().width()
        self.resize(int(screen_width*0.2), int(screen_height*0.2))
        self.tab_list = []
        self.setTabShape(QtGui.QTabWidget.Rounded)
        self.centralwidget = QtGui.QWidget(self)
        self.top_level_layout = QtGui.QGridLayout(self.centralwidget)
        self.tabWidget = QtGui.QTabWidget(self.centralwidget)
        self.top_level_layout.addWidget(self.tabWidget, 1, 0, 25, 25)
        self.setCentralWidget(self.centralwidget)
        self.centralwidget.setLayout(self.top_level_layout)
        self.process_all__button = QtGui.QPushButton("Start All Processes")
        self.top_level_layout.addWidget(self.process_all__button, 0, 0)
        QtCore.QObject.connect(self.process_all__button, QtCore.SIGNAL("clicked()"), self.start_all_process)
        # Make Tabs in loop from button
        for i in range(0,10):
            super_cool_data = make_data()
            name = 'tab ' + str(i)
            self.tab_list.append(Tab(self.tabWidget, super_cool_data))
            self.tabWidget.addTab(self.tab_list[-1], name)

    def start_all_process(self):
        self.process_all__button.setText('Stop All Processing')
        QtCore.QObject.disconnect(self.process_all__button, QtCore.SIGNAL("clicked()"), self.start_all_process)
        QtCore.QObject.connect(self.process_all__button, QtCore.SIGNAL("clicked()"), self.stop_all_process)
        for i in self.tab_list:
            i.start_process()

    def stop_all_process(self):
        self.process_all__button.setText('Start All Processing')
        QtCore.QObject.disconnect(self.process_all__button, QtCore.SIGNAL("clicked()"), self.stop_all_process)
        QtCore.QObject.connect(self.process_all__button, QtCore.SIGNAL("clicked()"), self.start_all_process)
        for i in self.tab_list:
            i.stop_process()        

if __name__ == "__main__":
    app = QtGui.QApplication([])
    win = MainWindow()
    win.show()
    sys.exit(app.exec_())

Tags: selfdatasignalstepbuttonallprocessstart