TKinter - 如何使用停止按钮停止循环?

时间:2016-09-18 08:08:56

标签: python tkinter

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

#!/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()

5 个答案:

答案 0 :(得分:6)

您的代码有几个问题。首先,您不应在Tkinter程序中使用time.sleep(),因为它会干扰mainloop()。相反,通常使用通用窗口小部件方法.after()来安排函数在指定的延迟之后运行。

其次,您没有正确使用全局变量。当您为函数中的命名变量赋值时,它将创建一个局部变量,除非该名称先前已声明为global。因此,例如,您的stop()函数正在创建名为running的局部变量,并将其值设置为0,更改具有相同名称的全局变量的值。< / p>

之前的规则并不适用于仅引用(读取)变量的当前值。这就是为什么没有在Freq中声明Durstart()全局变量的原因。

另一个问题是sec % 1 == 0函数中的start()。任何值% 1都是0。要检查奇数/均匀度,请使用sec % 2

这是一个可以重新格式化的工作版本,可以更紧密地关注PEP 8 - Style Guide for Python Code

import Tkinter
import tkMessageBox
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

top = Tkinter.Tk()
top.title('MapAwareness')
top.geometry('200x100')

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()

答案 1 :(得分:2)

您的代码有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()

答案 2 :(得分:1)

问题是start()中的while循环阻止了GUI处理程序mainloop()。尝试在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

答案 3 :(得分:1)

再次击败拳击,但这里什么都没有。如上所述,使用after功能来阻止mainloop阻止 看到: tkinter: how to use after method

#!/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()

答案 4 :(得分:0)

我使用了线程和全局变量来满足您的需求。如果您了解它们的工作原理,则不必那么复杂。只需添加几行,然后对现有行进行少量更改,即可正常工作。浏览以查看对原始代码所做的更改。

    #!/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()