我有一个编写的脚本,允许同时执行多个进程。除了并行运行外,这些流程还实时向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
操作似乎不起作用,正如我所包含的几个打印语句的输出所示。按钮似乎没有断开连接。
这是新代码:
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_())