如何在新的Tkinter窗口打开时运行可取消的功能

时间:2016-06-07 02:19:36

标签: python python-3.x tkinter python-multiprocessing

在tkinter中,我想创建以下内容:

按下按钮foo时,函数foo立即开始运行。将弹出一个新窗口,其中包含"取消"允许用户在中途终止进程的按钮(因为我的实际过程最多可能需要30分钟)。如果foo运行完成,则窗口自行关闭并通知用户该过程已完成。

以下代码演示了我的玩具问题:

from tkinter import ttk, messagebox, Toplevel, Tk
import time
import multiprocessing

def foo():
    for i in range(100):
        print(i)
        time.sleep(0.1)

class TerminatedProcess(Exception):
    def __init__(self, str = "Process was terminated"):
        self.error_str = str

class ProcessWindow(Toplevel):
    def __init__(self, parent, process):
        Toplevel.__init__(self, parent)
        self.parent = parent
        self. process = process

        terminate_button = ttk.Button(self, text="Cancel", command=self.cancel)
        terminate_button.grid(row=0, column=0)

        self.grab_set() # so you can't push submit multiple times

    def cancel(self):
        self.process.terminate()
        self.destroy()
        raise TerminatedProcess

    def launch(self):
        self.process.start()
        self.process.join()
        self.destroy()

class MainApplication(ttk.Frame):
    def __init__(self, parent, *args, **kwargs):
        ttk.Frame.__init__(self, parent, *args, **kwargs)
        self.parent = parent

        self.button = ttk.Button(self, text = "foo", command = self.callback)
        self.button.grid(row = 0, column = 0)

    def callback(self):
        try:
            proc = multiprocessing.Process(target=foo)
            process_window = ProcessWindow(self, proc)
            process_window.launch()
        except TerminatedProcess as e: # raised if foo is cancelled in other window
            messagebox.showinfo(title="Cancelled", message=e.error_str)
        else:
            messagebox.showinfo(message="sucessful run", title="Finished")

def main():
    root = Tk()
    my_app = MainApplication(root, padding=(4))
    my_app.grid(column=0, row=0)
    root.mainloop()

if __name__ == '__main__':
    main()

使用这段代码,当按下按钮foo时,确实会生成process_window,因为函数foo运行,并且我得到了一个"运行成功"对话框,但窗口永远不会弹出!这是一个问题,因为用户需要选择终止foo。有趣的是,如果我删除process_window.launch(),我仍然会得到"运行成功"消息(如预期的那样),但process_window显示出来。

我已经尝试将启动命令放在初始化中,但这也使窗口不显示。

我也尝试在窗口声明之外启动该过程,如下所示:

proc.start()
process_window = ProcessWindow(self, proc)
proc.join()

窗口仍然无法显示,但进程已运行。

我的问题是,我如何安排这些部分来生成ProcessWindow并在ProcessWindow打开时自动启动进程?

更新(解决方法):我知道我可以在ProcessWindow中创建一个启动按钮来运行该功能(然后在按下按钮后禁用该按钮),但我仍然想知道如何实现原问题中描述了什么。

1 个答案:

答案 0 :(得分:1)

也许你正在寻找这样的东西:

from tkinter import ttk, messagebox, Toplevel, Tk
import time
import multiprocessing

def foo():
    for i in range(100):
        print(i)
        time.sleep(0.1)

class ProcessWindow(Toplevel):
    def __init__(self, parent, process):
        Toplevel.__init__(self, parent)
        self.parent = parent
        self.process = process

        terminate_button = ttk.Button(self, text="Cancel", command=self.cancel)
        terminate_button.grid(row=0, column=0)

        self.grab_set() # so you can't push submit multiple times

    def cancel(self):
        self.process.terminate()
        self.destroy()
        messagebox.showinfo(title="Cancelled", message='Process was terminated')

    def launch(self):
        self.process.start()
        self.after(10, self.isAlive)            # Starting the loop to check when the process is going to end

    def isAlive(self):
        if self.process.is_alive():                     # Process still running
            self.after(100, self.isAlive)               # Going again...
        elif self:
            # Process finished
            messagebox.showinfo(message="sucessful run", title="Finished")
            self.destroy()

class MainApplication(ttk.Frame):
    def __init__(self, parent, *args, **kwargs):
        ttk.Frame.__init__(self, parent, *args, **kwargs)
        self.parent = parent

        self.button = ttk.Button(self, text = "foo", command = self.callback)
        self.button.grid(row = 0, column = 0)

    def callback(self):
        proc = multiprocessing.Process(target=foo)
        process_window = ProcessWindow(self, proc)
        process_window.launch()

def main():
    root = Tk()
    my_app = MainApplication(root, padding=(4))
    my_app.grid(column=0, row=0)
    root.mainloop()

if __name__ == '__main__':
    main()