TKinter - How to stop a loop with a stop button?

前端 未结 5 1621
礼貌的吻别
礼貌的吻别 2020-12-20 20:16

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 becaus

相关标签:
5条回答
  • 2020-12-20 20:58

    I used thread and global variable to fit your need. Not so complicated if you understand how they work. Just an addition of few lines and minor change to your existing line, and it works. Look through to see the changes made to your original code.

        #!/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()
    
    0 讨论(0)
  • 2020-12-20 21:01

    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.

    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.

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

    Another problem is with the sec % 1 == 0 in your start() function. Any value % 1 is 0. To check odd/evenness use sec % 2.

    Here's a working version which has also been reformatted to follow PEP 8 - Style Guide for Python Code more closely.

    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()
    
    0 讨论(0)
  • 2020-12-20 21:06

    You code have top.mainloop() which has a while loop running inside it and on top of that you also have a while loop inside def start():. So it is like loop inside loop.

    You can create a function that does what you want for the body of the loop. It should do exactly one iteration of the loop. Once it is done, it needs to arrange for itself to be called again some time in the future using after. How far in the future defines how fast your loop runs.

    And you can then use after_cancel to cancel the event. Below code worked for me

    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()
    
    0 讨论(0)
  • 2020-12-20 21:12

    The problem is that the while loop in start() blocks the GUI handler mainloop(). Try using Tk.after() in start():

    def start(force=True):
        global running
        if force:
            running = True
        if running:
            winsound.Beep(Freq, Dur)
            top.after(1000, start, False)
    

    And change stop():

    def stop():
        global running
        running = False
    
    0 讨论(0)
  • 2020-12-20 21:14

    Beaten to the punch again but here goes nothing. As above use the after function to prevent the mainloop blocking.
    See: 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()
    
    0 讨论(0)
提交回复
热议问题