How to stop a loop triggered by tkinter in Python

Question:

I’m new to Python and even more so to tkinter, and I decided to try to create a start and stop button for an infinite loop through Tkinter. Unfortunately, once I click start, it won’t allow me to click stop. The start button remains indented, and I assume this is because the function it triggered is still running. How can I make this 2nd button stop the code?

import tkinter

def loop():
    global stop
    stop = False
    while True:
        if stop == True:
            break
        #The repeating code
def start():
    loop()
def stop():
    global stop
    stop = True

window = tkinter.Tk()
window.title("Loop")
startButton = tkinter.Button(window, text = "Start", command = start)
stopButton = tkinter.Button(window, text = "Pause", command = stop)
startButton.pack()
Asked By: Bugsy

||

Answers:

You’re calling while True. Long story short, Tk() has it’s own event loop. So, whenever you call some long running process it blocks this event loop and you can’t do anything. You should probably use after

I avoided using global here by just giving an attribute to window.

e.g. –

import tkinter

def stop():

    window.poll = False

def loop():

    if window.poll:
        print("Polling")
        window.after(100, loop)
    else:
        print("Stopped long running process.")

window = tkinter.Tk()
window.poll = True
window.title("Loop")
startButton = tkinter.Button(window, text = "Start", command = loop)
stopButton = tkinter.Button(window, text = "Pause", command = stop)
startButton.pack()
stopButton.pack()
window.mainloop()
Answered By: Pythonista

Thank you! @Pythonista, It works for me stopping a for loop in the tkinter. But it is likely not an effective way to do that because the frame was stuck after I pressed the stop button as demonstrated below with part of script: `

    def start_scan(self):    
        self.voltage_scan(
                      self.start.get(),
                      self.stop.get(),
                      self.step.get(),
                      self.delay.get()
                      
                      )       
                          
    def start_butt(self): 
    
        if self.poll:
            self.start_scan()
            
    def stop_butt(self):
        self.poll = False

    def voltage_scan(self, start, stop, step, delay):
        
        
        def make_plot():
            plt.scatter(mvol,mcurr)
            plt.xlabel('Voltage / V', fontsize = 12)
            plt.ylabel('Current / A', fontsize = 12)
            plt.tight_layout()
            
        if start < stop:
            stop = stop + 1
     
            
            plt.ion()  # enable interactivity
            fig = plt.figure(figsize = (5,3.5), dpi = 100)
            ax = fig.add_subplot(111)
            curr  = 0
            mvol = []
            mcurr = []
            
            for vol in range(start, stop, step):
                if self.poll:
                    mvol.append(vol)
                    #time.sleep(1 / delay)
                    self.after(1000*delay)  # 1 second delay
                    curr += 1
                    mcurr.append(curr)
                    #print(vol, curr)
                    drawnow(make_plot)
                else:
                    break

`

Wigets and liveplot after stoppig

There might be better way to do that. or at least there would be a way to refresh the frame without closing the program. bcz I have to restart the program after evrytime I press the stop button.

Answered By: Avral
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.