如何在新的 Tkinter window 打开时 运行 取消函数
How to run a cancellable function when a new Tkinter window opens
在 tkinter 中,我想创建以下内容:
当按下按钮 foo 时,函数 foo 立即开始 运行ning。一个新的 window 将弹出一个 "cancel" 按钮,让用户可以在中途终止进程(因为我的实际进程可能需要长达 30 分钟)。如果 foo 运行s 完成,window 将自行关闭并通知用户该过程已完成。
这是演示我的玩具问题的代码:
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 运行s,我会得到一个 "run successful" 对话框,但是window永远不会弹出!这是一个问题,因为用户需要有终止 foo 的选项。有趣的是,如果我删除 process_window.launch(),我仍然会收到 "run successful" 消息(如预期的那样),但是会显示 process_window。
我试过将启动命令放在初始化中,但这使得 window 也没有显示。
我还尝试在 window 声明之外启动进程,如下所示:
proc.start()
process_window = ProcessWindow(self, proc)
proc.join()
window还是没有显示出来,但是进程运行。
我的问题是,我需要如何处理运行这些片段以生成 ProcessWindow 并在 ProcessWindow 打开时自动启动进程?
更新(解决方法):我知道我可以在 ProcessWindow 中创建一个开始按钮,它将 运行 该功能(然后在按下一次按钮后禁用该按钮),但我仍然会想知道如何实现原始问题中描述的内容。
也许您正在寻找这样的东西:
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()
在 tkinter 中,我想创建以下内容:
当按下按钮 foo 时,函数 foo 立即开始 运行ning。一个新的 window 将弹出一个 "cancel" 按钮,让用户可以在中途终止进程(因为我的实际进程可能需要长达 30 分钟)。如果 foo 运行s 完成,window 将自行关闭并通知用户该过程已完成。
这是演示我的玩具问题的代码:
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 运行s,我会得到一个 "run successful" 对话框,但是window永远不会弹出!这是一个问题,因为用户需要有终止 foo 的选项。有趣的是,如果我删除 process_window.launch(),我仍然会收到 "run successful" 消息(如预期的那样),但是会显示 process_window。
我试过将启动命令放在初始化中,但这使得 window 也没有显示。
我还尝试在 window 声明之外启动进程,如下所示:
proc.start()
process_window = ProcessWindow(self, proc)
proc.join()
window还是没有显示出来,但是进程运行。
我的问题是,我需要如何处理运行这些片段以生成 ProcessWindow 并在 ProcessWindow 打开时自动启动进程?
更新(解决方法):我知道我可以在 ProcessWindow 中创建一个开始按钮,它将 运行 该功能(然后在按下一次按钮后禁用该按钮),但我仍然会想知道如何实现原始问题中描述的内容。
也许您正在寻找这样的东西:
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()