[英]Tkinter - How to stop a loop with a stop button?
我有這個程序,它每秒發出嗶嗶聲,直到它停止。 問題是,在我按下“開始”並開始發出嗶嗶聲后,我無法單擊“停止”按鈕,因為 window 凍結了。 歡迎任何幫助。
#!/usr/bin/python
import Tkinter, tkMessageBox, time, winsound, msvcrt
running = True
Freq = 2500
Dur = 150
top = Tkinter.Tk()
top.title('MapAwareness')
top.geometry('200x100') # Size 200, 200
def start():
sec = 0
while running:
if sec % 1 == 0:
winsound.Beep(Freq, Dur)
time.sleep(1)
sec += 1
def stop():
running = False
startButton = Tkinter.Button(top, height=2, width=20, text ="Start", command = start)
stopButton = Tkinter.Button(top, height=2, width=20, text ="Stop", command = stop)
startButton.pack()
stopButton.pack()
top.mainloop()
您的代碼有幾處錯誤。 首先你不應該使用time.sleep()
因為它與干擾的Tkinter的程序mainloop()
相反,通常使用通用小部件方法.after()
來安排函數在指定延遲后運行。
其次,您沒有正確使用全局變量。 當您為函數中的命名變量賦值時,它將創建一個局部變量,除非該名稱之前已聲明為global
。 例如,您的stop()
函數正在創建一個名為running
的局部變量並將其值設置為 0,而不是更改具有相同名稱的全局變量的值。
上一條規則不僅僅適用於引用(讀取)變量的當前值。 這就是為什么不在start()
聲明Freq
和Dur
全局變量是可以的。
另一個問題是start()
函數中的sec % 1 == 0
。 任何值% 1
都是0
。 要檢查奇數/偶數,請使用sec % 2
。
這是一個工作版本,它也經過重新格式化以更緊密地遵循PEP 8 - Python 代碼風格指南。
try:
import tkinter as tk
except ModuleNotFoundError:
import Tkinter as tk # Python 2.
import time
import winsound
FREQ = 2500
DUR = 150
after_id = None
secs = 0
def beeper():
global after_id
global secs
secs += 1
if secs % 2 == 0: # Every other second.
winsound.Beep(FREQ, DUR)
after_id = top.after(1000, beeper) # Check again in 1 second.
def start():
global secs
secs = 0
beeper() # Start repeated checking.
def stop():
global after_id
if after_id:
top.after_cancel(after_id)
after_id = None
if __name__ == '__main__':
top = tk.Tk()
top.title('MapAwareness')
top.geometry('200x100')
startButton = tk.Button(top, height=2, width=20, text="Start", command=start)
stopButton = tk.Button(top, height=2, width=20, text="Stop", command=stop)
startButton.pack()
stopButton.pack()
top.mainloop()
由於這個答案已經變得相當流行,我想談談另一個稍微更高級的話題——即如何通過消除幾乎所有全局變量的需要,使代碼更加面向對象來簡化事情。
try:
import tkinter as tk
except ModuleNotFoundError:
import Tkinter as tk # Python 2.
import time
import winsound
FREQ = 2500
DUR = 150
class Application(tk.Frame, object):
def __init__(self, master=None):
super(Application, self).__init__(master) # Call baseclass constructor.
self.after_id = None
self.secs = 0
# Create widgets,
startButton = tk.Button(top, height=2, width=20, text="Start", command=self.start)
stopButton = tk.Button(top, height=2, width=20, text="Stop", command=self.stop)
startButton.pack()
stopButton.pack()
def beeper(self):
self.secs += 1
if self.secs % 2 == 0: # Every other second.
winsound.Beep(FREQ, DUR)
self.after_id = top.after(1000, self.beeper) # Check again in 1 second.
def start(self):
self.secs = 0
self.beeper() # Start repeated checking.
def stop(self):
if self.after_id:
top.after_cancel(self.after_id)
self.after_id = None
if __name__ == '__main__':
top = tk.Tk()
app = Application()
app.master.title('MapAwareness')
app.master.geometry('200x100')
app.mainloop()
你的代碼有top.mainloop()
,它里面有一個while
循環,最重要的是你在def start():
有一個 while 循環。 所以它就像循環內循環。
您可以創建一個函數來為循環體執行您想要的操作。 它應該只執行一次循環迭代。 完成后,它需要安排自己在將來的某個時間再次使用after
調用。 未來多遠定義了循環運行的速度。
然后您可以使用after_cancel
取消該事件。 下面的代碼對我有用
import Tkinter, tkMessageBox, time, winsound, msvcrt
Freq = 2500
Dur = 150
top = tkinter.Tk()
top.title('MapAwareness')
top.geometry('200x100') # Size 200, 200
def start():
global job1
if running == True:
winsound.Beep(Freq, Dur)
job1 = top.after(1000, start) # reschedule event in 1 seconds
def stop():
global job1
top.after_cancel(job1)
startButton = tkinter.Button(top, height=2, width=20, text ="Start", command = start)
stopButton = tkinter.Button(top, height=2, width=20, text ="Stop", command = stop)
startButton.pack()
stopButton.pack()
#top.after(1000, start)
top.mainloop()
問題是start()
中的 while 循環阻塞了 GUI 處理程序mainloop()
。 嘗試在start()
使用Tk.after()
start()
:
def start(force=True):
global running
if force:
running = True
if running:
winsound.Beep(Freq, Dur)
top.after(1000, start, False)
並更改stop()
:
def stop():
global running
running = False
再次受到打擊,但這里什么也沒有。 如上所述,使用after
函數來防止主mainloop
阻塞。
請參閱: tkinter:如何使用 after 方法
#!/usr/bin/python
import Tkinter, tkMessageBox, time
Freq = 2500
Dur = 150
top = Tkinter.Tk()
top.title('MapAwareness')
top.geometry('200x100') # Size 200, 200
def start():
print ("Beep")
top.after(1000, start)
def stop():
print ("Stop")
top.quit()
startButton = Tkinter.Button(top, height=2, width=20, text ="Start", command = start)
stopButton = Tkinter.Button(top, height=2, width=20, text ="Stop", command = stop)
startButton.pack()
stopButton.pack()
top.mainloop()
我使用線程和全局變量來滿足您的需要。 如果您了解它們的工作原理,就不會那么復雜。 只需添加幾行並對您現有的行進行微小更改,它就可以工作。 仔細查看對原始代碼所做的更改。
#!/usr/bin/python
import tkinter
from tkinter import messagebox
import time, winsound, msvcrt
from threading import Thread
running = True
Freq = 2500
Dur = 150
top = tkinter.Tk()
top.title('MapAwareness')
top.geometry('200x100') # Size 200, 200
def button_click():
global running #create global
running = True
# Create new thread
t = Thread(target = start)
# Start new thread
t.start()
def start():
sec = 0
while running:
if running == False:
break
if sec % 1 == 0:
winsound.Beep(Freq, Dur)
time.sleep(1)
sec += 1
def stop():
global running #create global
running = False
startButton = tkinter.Button(top, height=2, width=20, text ="Start", command = button_click) #Change to call button_click instead start
stopButton = tkinter.Button(top, height=2, width=20, text ="Stop", command = stop)
startButton.pack()
stopButton.pack()
top.mainloop()
from threading import Thread
def stttart():
t1=Thread(target=start)
t1.start()
def start():
...
def stop():
...
startButton = Tkinter.Button(top, height=2, width=20, text ="Start", command = stttart)
stopButton = Tkinter.Button(top, height=2, width=20, text ="Stop", command = stop)
startButton.pack()
stopButton.pack()
https://www.geeksforgeeks.org/how-to-use-thread-in-tkinter-python/
去年,這幾個月來一直是我的大問題
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.