0

我需要实现以下 UI: - 有一个带有“运行实验 1/X”标签的窗口和一个按钮 - 加载窗口时,会启动一些实验。实验由os.systemof运行subprocess.Popen,它们只是预编译的 C++ 程序 - 实验应该严格地一个接一个地运行,而不是同时运行(因此我不能使用subprocess.Popen) - 实验运行时窗口应该是活动的,用户可以按下按钮 - 按下按钮时,实验停止(我们可以等到当前实验结束并停止)并且窗口关闭 - 当所有实验结束时,窗口应自行关闭

首先我尝试在 中运行实验threading.Thread,但它仍然挡住了窗口。所以我切换到multiprocessing.Process

class StoppableProcess(Process):
    def __init__(self, name, alg, proj, parent):
        Process.__init__(self)
        self.stop = False
        self.name = name
        self.alg = alg
        self.proj = proj
        self.parent = parent 

    def stop(self):
        self.stop = True

    def stopped(self):
        return self.stop

    def run(self):
        count = len([k for k in self.proj.values()])
        i = 1
        for p in self.proj.values():
            self.parent.label.setText("Running experiment " + str(i) + " / " + str(count))
            os.system("some command here")
            i += 1
            if self.stopped():
                break  
        self.parent.hide() 



class Runner(QDialog):
    def __init__(self, parent):
        QDialog.__init__(self, parent)
        self.layout = QVBoxLayout()
        self.label = QLabel("Running experiment 0 / 0")
        self.setWindowTitle("Running experiments")
        button = QPushButton("Break experiments")
        self.layout.addWidget(self.label)
        self.layout.addWidget(button)
        self.setLayout(self.layout)
        QObject.connect(button, SIGNAL("clicked()"), self.Break)

    def Run(self, name, alg, proj):
        self.thread = StoppableProcess(name, alg, proj, self)
        self.thread.start()
        self.show()
        self.thread.join()

    def Break(self):
        self.thread.stop()
        self.hide()

但是,这根本不起作用,显然是因为Runner对象应该被腌制以传递给子进程,但是腌制失败了。我正在考虑避免传递父参数并改用 Qt 信号,但也许有更好的解决方案?

4

1 回答 1

1

首先,您确实可以使用subprocess.Popen来启动后台进程并等待它们完成。请参阅文档,特别是poll()方法。运行 UI 事件循环,直到进程退出。

其次,在 Python 中避免使用线程通常是个好主意。当您想要并行化用 Python 编写的任务时,多处理模块最有用。IMO,我认为如果您只是启动外部子进程,使用子进程模块会更容易。

下面的伪代码说明了这个想法:

experiments = [...]
process = None

def start_next_experiment():
    if not experiments:
        print "Done!"
    else:
        experiment = experiments.pop()
        process = subprocess.Popen(experiment)

def on_start_clicked():
    start_next_experiment()

def on_stop_clicked():
     # Clear the queue
    experiments = []

    # optional: Kill the process
    if process:
        process.terminate()

def on_idle():
    if process:
        # use e.g. a PyQT timer to run this method periodically
        process.poll()
        if process.returncode is not None:
            process = None
            start_next_experiment()
于 2013-09-28T15:04:58.333 回答