Search code examples
pythontkinterwhile-loopbreak

How to break a while loop with a tkinter button without freezeing the executable


I've tried by now several methods found online but I can't seem to find a solution. What I want to create is to enable a while loop with a button and disable it with another button. The main issue, is that while the loop is active, it freezes my GUI when I press my "disable" button. The while loop does stop, but I have to kill the executable in task manager and restart it in order to re-enable the while loop.

Code:

from tkinter import *
import time

top = Tk()
top.title("Corsair")
top.geometry('100x100')
top.resizable(False, False)

def b1(): 
    while True:
        [MY MAIN CODE]
#       if But2.is_pressed:
#           break

but1 = Button(top, image = onbutton, text ="On", command = b1)
but2 = Button(top, image = offbutton, text ="Off", pady = 100, padx = 10, command = top.destroy)

but1.pack()
but2.pack()
top.mainloop()

I've tried; if But2.is_pressed to break the code, which freezes the GUI.

Make but2 to destroy top window, which freezes the GUI.

I've tried ;

x = 1

def b1(): 
    while True:
        if x == 1:
            [MY MAIN CODE]
        else:
            break   

def b2():
    x = 0   

but1 = Button(top, image = onbutton, text ="On", command = b1)
but2 = Button(top, image = offbutton, text ="Off", pady = 100, padx = 10, command = b2)
but3 = Button(top, text ="Exit", pady = 100, padx = 20, command = top.destroy)

Solution

  • This problem was few times on Stackoverflow:

    If you run long-running loop then you should run it in separate thread.

    But if every loop is not so long then you could use .after(milliseconds, function_name) at the end of function (instead of while and sleep) to repeat function again - and this will work as loop, and tkinter will have time to run own mainloop

    import tkinter as tk  # PEP8: `import *` is not preferred
    import time
    
    # --- functions ---  # PEP8: all functions before main code
    
    def b1(): 
        print('MY MAIN CODE')
        if running:
            # repeat after 100ms (0.1s)
            top.after(100, b1)  # funcion's name without ()
        else:
            print('STOP')
    
    def b2(): 
        global running
        
        running = False
        
    # --- main ---
    
    running = True
    
    top = tk.Tk()
    
    but1 = tk.Button(top, text="On",  command=b1)   # PEP8: inside `()` use `=` without spaces
    but2 = tk.Button(top, text="Off", command=b2)
    but1.pack()
    but2.pack()
    
    top.mainloop()
    

    EDIT:

    If you want to use button On to restart loop again then it may need extra function to set again running = True

    import tkinter as tk  # PEP8: `import *` is not preferred
    import time
    
    # --- functions ---  # PEP8: all functions before main code
    
    def b1():
        global running
    
        running = True
        
        loop()
        
    def loop():    
        print('MY MAIN CODE')
        
        if running:
            # repeat after 100ms (0.1s)
            top.after(100, loop)  # funcion's name without ()
        else:
            print('STOP')
            
    def b2(): 
        global running
        
        running = False
        
    # --- main ---
    
    running = True
    
    top = tk.Tk()
    
    but1 = tk.Button(top, text="On",  command=b1)   # PEP8: inside `()` use `=` without spaces
    but2 = tk.Button(top, text="Off", command=b2)
    but1.pack()
    but2.pack()
    
    top.mainloop()