簡體   English   中英

Tkinter - 如何使用停止按鈕停止循環?

[英]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()聲明FreqDur全局變量是可以的。

另一個問題是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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM