Tkinter - 如何使用停止按钮停止循环? [英] Tkinter - How to stop a loop with a stop button?

查看:58
本文介绍了Tkinter - 如何使用停止按钮停止循环?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有这个程序,它每秒都会发出哔哔声,直到它停止.问题是,在我按下开始"并发出蜂鸣声后,我无法单击停止"按钮,因为窗口冻结了.欢迎任何帮助.

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() 中没有声明 FreqDur 全局变量是可以的.

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆