【问题标题】:How to stop nested computation and close Toplevel window in tkinter如何在 tkinter 中停止嵌套计算并关闭顶层窗口
【发布时间】:2021-08-08 23:34:24
【问题描述】:

我在 tkinter 脚本中有一个 Toplevel 窗口,其中包含两个对应于嵌套循环的进度条。我想在 Toplevel 窗口中添加一个停止按钮来终止执行并关闭 Toplevel 窗口而不是根。

from tkinter import Tk, Toplevel,Button,Label,TOP,BOTH,DoubleVar
from tkinter.ttk import Progressbar
import time 

root = Tk()
root.configure(bg='lightgray')
root.wm_title("Main")


progress_window = Toplevel()

progress_window.attributes('-topmost', 'true')
progress_window.configure(bg='lightgrey')
progress_window.wm_title("Progress")
progress_window_label = Label(root, text="")
progress_window.geometry('600x300')
progress_window_label.pack()

M=4 # outer loop
N=5 # inner loop

progress1_var=DoubleVar()
progress1bar=Progressbar(master=progress_window,variable=progress1_var,length=M)    
progress1bar.pack(side=TOP,ipady=5,fill=BOTH,expand=True)
progress1_var.set(0)
progress1bar.update()
progress1bar_label=Label(master=progress_window,text='Bar1',bg='lightgray')
progress1bar_label.pack(side=TOP,pady=5,fill=BOTH, expand=True)

progress2_var = DoubleVar()
progress2bar=Progressbar(master=progress_window,variable=progress2_var,length=N)
progress2bar.pack(side=TOP,ipady=5,fill=BOTH, expand=True)
​progress2_var.set(0)
progress2bar.update()
progress2bar_label=Label(master=progress_window,text='Bar2',bg='lightgray')
progress2bar_label.pack(side=TOP,pady=5,fill=BOTH, expand=True)

def _stop():
    return

stop_button=Button(master=progress_window, text="Cancel",command=_stop)
stop_button.pack(side=TOP,pady=5,fill=BOTH, expand=True)


​for t in range(M):

       ​progress1_var.set(t/M)
       ​progress1bar_label.config(text='Bar 1:   '+str(round((t+1)/M*100,3))+'%')
       ​progress1bar.update()

       ​progress2_var.set(0)
       ​progress2bar.update()
   ​
       ​for i in range(N):
           ​progress2_var.set(i/N)
           ​time.sleep(1.0) #Sleep to slow down execution and view progress window
           ​progress2bar_label.config(text='Bar 2:  '+str(round((i+1)/N*100,3))+'%')
           ​progress2bar.update()


​progress_window.destroy()
root.mainloop()

停止按钮出现在应该但不起作用的位置并停止执行。这一定是一个非常根本的错误,但我不知道如何纠正它。

【问题讨论】:

  • 我希望你知道调用_stop() 绝对不会做任何事情(除了浪费极少量的资源),我建议在那些循环中增加一些标志并使用一些 if 语句会破坏它们

标签: python tkinter


【解决方案1】:

我做了一些修改,主要是为rootprogress_window 添加了protocol

我还创建了一个名为killed 的标志,用于中断for 循环。我已将它们放在由after 调用的函数中。

这会停止TclError: Invalid command name .!toplevel.!label2

from tkinter import Tk, Toplevel, Button, Label, TOP, BOTH, DoubleVar
from tkinter.ttk import Progressbar
import time 

killed  =  False

root  =  Tk()
root.configure(bg = 'lightgray')
root.wm_title("Main")

root.update()

progress_window  =  Toplevel()

def stop():
    global killed
    killed = True
    progress_window.destroy()
    root.destroy()

# Added protocol controls for exit

root.protocol( "WM_DELETE_WINDOW", stop )
progress_window.protocol( "WM_DELETE_WINDOW", stop )

progress_window.attributes('-topmost', 'true')
progress_window.configure(bg = 'lightgrey')
progress_window.wm_title("Progress")
progress_window_label  =  Label(root, text = "")
progress_window.geometry('600x300')
progress_window_label.pack()

M = 4 # outer loop
N = 5 # inner loop

progress1_var = DoubleVar()
progress1bar = Progressbar(master = progress_window, variable = progress1_var, length = M)    
progress1bar.pack(side = TOP, ipady = 5, fill = BOTH, expand = True)
progress1_var.set(0)
progress1bar.update()
progress1bar_label = Label(master = progress_window, text = 'Bar1', bg = 'lightgray')
progress1bar_label.pack(side = TOP, pady = 5, fill = BOTH, expand = True)

progress2_var  =  DoubleVar()
progress2bar = Progressbar(master = progress_window, variable = progress2_var, length = N)
progress2bar.pack(side = TOP, ipady = 5, fill = BOTH, expand = True)

progress2_var.set(0)
progress2bar.update()

progress2bar_label = Label(master = progress_window, text = 'Bar2', bg = 'lightgray')
progress2bar_label.pack(side = TOP, pady = 5, fill = BOTH, expand = True)

def process():
    for t in range(M):
        if killed:
            break
        progress1_var.set(t/M)
        progress1bar_label.config(text = "Bar 1:    " + str( round( (t+1)/M*100, 3)) + "%")
        progress1bar.update()
        progress2_var.set(0)
        progress2bar.update()
        for i in range(N):
            progress2_var.set(i/N)
            time.sleep(1.0)
            if killed:
                break
            progress2bar_label.config(text = "Bar 2:    " + str( round( (i+1)/N*100, 3)) + "%")
            progress2bar.update()

stop_button = Button(master = progress_window, text = "Cancel", command = stop)
stop_button.pack(side = TOP, pady = 5, fill = BOTH, expand = True)

root.after( 1000, process )
root.mainloop()

【讨论】:

  • 非常好!只是几个问题。对于我实际的、更复杂的脚本,进度条是在一个带有六个参数的冗长函数中创建的。当progress_window 关闭时,我希望root 保持打开状态,因为它将包含一个图形画布。然后我假设 root.destroy() 将从上面删除。还有,root.after(1000,process) 和 root.update() 真的只是在代替定时器吗?
  • 我建议放弃sleep 并使用after,尽管这需要对您的核心程序进行相当彻底的更改。我还注意到您的主要root 窗口没有做太多,我认为您将能够继续开发您的代码。如果您不想关闭所有窗口,请专门为root 创建另一个函数,但与stop 相同(您可能需要创建另一个killed 标志并将其链接到“协议”。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-27
  • 2012-11-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多