Tkinter - 如何使用停止按钮停止循环? [英] Tkinter - How to stop a loop with a stop button?
问题描述
我有这个程序,它每秒都会发出哔哔声,直到它停止.问题是,在我按下开始"并发出蜂鸣声后,我无法单击停止"按钮,因为窗口冻结了.欢迎任何帮助.
I have this program which beeps every second until it's stopped. The problem is that after I press "Start" and the beeps starts, I cannot click the "Stop" button because the window freezes. Any help is welcome.
#!/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()
推荐答案
您的代码有几个问题.首先,您不应该在 Tkinter 程序中使用 time.sleep()
,因为它会干扰 mainloop()
.相反,通常使用通用小部件方法 .after()
安排函数在指定的延迟后运行.
There are several things wrong with your code. First of all you shouldn't use time.sleep()
in a Tkinter program because it interferes with the mainloop()
. Instead one typically uses the universal widget method .after()
to schedule a function to run after a specified delay.
其次,您没有正确使用全局变量.当您为函数中的命名变量赋值时,它将创建一个局部变量,除非该名称之前已声明为global
.例如,您的 stop()
函数正在创建一个名为 running
的局部变量并将其值设置为 0,不改变同名的全局变量.
Secondly you're not using global variables correctly. When you assign a value to a named variable in a function, it will create a local variable unless that name has been previous declared global
. So for instance, your stop()
function is creating a local variable named running
and setting its value to 0, not changing the value of the global variable with the same name.
上一条规则不适用于仅引用(读取)变量的当前值.这就是为什么在 start()
中没有声明 Freq
和 Dur
全局变量是可以的.
The previous rule doesn't apply to just referencing (reading) the current value of a variable. That is why it was OK to not have declared Freq
and Dur
globals in start()
.
另一个问题是start()
函数中的sec % 1 == 0
.任何值 %1
都是 0
.要检查奇数/偶数,请使用 sec % 2
.
Another problem is with the sec % 1 == 0
in your start()
function. Any value % 1
is 0
. To check odd/evenness use sec % 2
.
这是一个工作版本,它也被重新格式化以遵循 PEP 8 - 风格指南更接近于 Python 代码.
Here's a working version which has also been reformatted to follow PEP 8 - Style Guide for Python Code more closely.
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()
更新
既然这个答案已经变得相当流行,我想谈谈另一个稍微更高级的话题——即如何通过消除几乎所有全局变量的需要,使代码更加面向对象来简化事情.
Update
Since this answer has become fairly popular, I'd like touch on another slightly more advanced topic — namely how making the code more object-oriented would simplify things by eliminating the need almost all of the global variables.
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()
这篇关于Tkinter - 如何使用停止按钮停止循环?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!