Python Forum
Creating a function interrupt button tkinter
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Creating a function interrupt button tkinter
#1
from tkinter import *

def start(num):
    global sweepCancel
    print(num)
    if num < 10:
        num = num +1
        sweepCancel=win.after(1000,start,num)
 
def stop():
    win.after_cancel(sweepCancel)

win = Tk()
InitButton = Button(win,text='start',command=lambda:start(0)  , bg = "bisque2")
InitButton.grid(row=0, column=1, pady = 20, padx = 10)
ResetButton = Button(win,text='stop',command= stop , bg = "bisque2")
ResetButton.grid(row=0, column=2, pady = 20)
mainloop()
Hello all,
I am trying to create a button to interrupt a function running in the background of tkinter. I looked through the web and found a few examples. Above is a snippet of code I got to work. The start button starts a function to count up and the stop button can halt the count. My first question is, is there a more elegant way to do this without having that global variable. I'm far from an expert in coding, but always heard global variables weren't good to use in pratice

My second question, my real application is slightly different than above. Instead of the if statement allowing the function to continue run, I wanted to call the after() command in a for loop. The code below is what I tried. Unlike the code above, the start button stays depressed and the window locks up. Any thoughts as to why? I sort of realize the above code is functionally no different than a for loop, but I'd rather the after() call a separate function (sweepFunction in this case) and not the original function (start). Thanks in advance!
from tkinter import *

def sweepFunction(argument):
    print(argument)

def start():
    global sweepCancel
    for num in range(0,10):
        sweepCancel=win.after(1000,sweepFunction(num))
 
def stop():
    win.after_cancel(sweepCancel)

win = Tk()
InitButton = Button(win,text='start',command=start  , bg = "bisque2")
InitButton.grid(row=0, column=1, pady = 20, padx = 10)
ResetButton = Button(win,text='stop',command= stop , bg = "bisque2")
ResetButton.grid(row=0, column=2, pady = 20)
mainloop()
Reply
#2
The method after blocks until the time is up.
The method is called in the for-loop of your start function.
Calling the start function blocks the GUI because the context of the function is not left, until the counter is done.

What I didn't know, that the method after blocks. I thought always, that it will put the task for the callback into the eventloop of tkinter, but this is wrong. The method after blocks until the time is up and then the callback is called, but the after method allows tkinter to do during this time other tasks like redrawing the GUI.

This is essential for GUIs not to block.


Here is the resulting example with changes (PEP8 names) and without global and the for-loop:
from tkinter import Button, Tk


class Sweep:
    def __init__(self, root, start_value, end_value=None, delay=1000):
        print("New sweep instance")
        self.root = root
        self.start_value = self.num = start_value
        self.end_value = end_value
        self.delay = delay
        self.running = False

    def start(self):
        print("Start method called")
        if not self.running:
            self.running = True
            self._loop()

    def stop(self):
        print("Stop method called")
        if self.running:
            self.running = False

    def reset(self):
        print("Reset method called")
        self.running = False
        self.num = 0

    def _loop(self):
        if self.running:

            if self.end_value is not None and self.end_value <= self.num:
                self.running = False
                print("The End")
                self.num = self.start_value # set value back to start value
                return

            print(self.num)
            self.num += 1

            # this method calls itself after self.delay ms
            self.root.after(self.delay, self._loop)


win = Tk()
sweep = Sweep(win, start_value=0, end_value=5, delay=1000)

init_button = Button(win, text="Start", command=sweep.start)
init_button.grid(row=0, column=1, pady=20, padx=10)

pause_button = Button(win, text="Stop", command=sweep.stop)
pause_button.grid(row=0, column=2, pady=20, padx=10)

reset_button = Button(win, text="Reset", command=sweep.reset)
reset_button.grid(row=0, column=3, pady=20, padx=10)

quit_button = Button(win, text="Quit", command=win.destroy)
quit_button.grid(row=1, column=0, columnspan=3, pady=20, padx=10)

win.mainloop()
Larz60+ likes this post
Almost dead, but too lazy to die: https://sourceserver.info
All humans together. We don't need politicians!
Reply
#3
Hello,
Thanks so much for your time and help. I had a follow up if you don't mind.
from tkinter import Button, Tk
  
class Sweep:
    def __init__(self, root, value_List, delay=1000):
        self.root = root
        self.value_List= value_List
        self.delay = delay
        self.running = False
        self.end_value = len(self.value_List)
        self.num = 0
 
    def start(self):
        print("Start method called")
        if not self.running:
            self.running = True
            self._loop()
 
    def stop(self):
        print("Stop method called")
        if self.running:
            self.num = 0
            self.running = False
 
    def _loop(self):
        if self.running:
            if self.end_value is not None and self.end_value <= self.num:
                self.running = False
                print("The End")
                self.num=0
                return
            print(self.value_List[self.num])
            self.num += 1
            self.root.after(self.delay, self._loop)
 
def CreateList(start, stop, numPts):
    SweepList=[]
    SweepList.append(start)
    stepInterval = (stop-start)/(numPts-1)
    for x in range(1,numPts):
        SweepList.append(start + x*stepInterval)
    return SweepList

    
win = Tk()

numList = CreateList(1000,10000,10)
sweep = Sweep(win, numList, 1000)
 
init_button = Button(win, text="Start", command=sweep.start)
init_button.grid(row=0, column=1, pady=20, padx=10)
 
pause_button = Button(win, text="Stop", command=sweep.stop)
pause_button.grid(row=0, column=2, pady=20, padx=10)
 
quit_button = Button(win, text="Quit", command=win.destroy)
quit_button.grid(row=1, column=0, columnspan=3, pady=20, padx=10)
 
win.mainloop()
In my real application I am running this sweep function within a separate function which is called after a button push. I'll better illustrate in a moment. However, working in baby steps. The code above modifies your original class to contain the list to be swept through, and the _loop function to step through self.value_List. In this case I understand the button's commands are well defined because the class is initialized in the main loop.
The code below puts the class definition and list creaction in a separate function outside the main loop, RunSweep. I have the pause button commented out because it returns an error, and rightfully so, that sweep isn't defined. Is there a way to modify that function call to include the class as one of the arguments instead of using sweep.Function? Perhaps have a new button pup up when that function is running and sweep is defined?
from tkinter import Button, Tk
  
class Sweep:
    def __init__(self, root, value_List, delay=1000):
        self.root = root
        self.value_List= value_List
        self.delay = delay
        self.running = False
        self.end_value = len(self.value_List)
        self.num = 0
 
    def start(self):
        print("Start method called")
        if not self.running:
            self.running = True
            self._loop()
 
    def stop(self):
        print("Stop method called")
        if self.running:
            self.num = 0
            self.running = False
 
    def _loop(self):
        if self.running:
            if self.end_value is not None and self.end_value <= self.num:
                self.running = False
                print("The End")
                self.num=0
                return
            print(self.value_List[self.num])
            self.num += 1
            self.root.after(self.delay, self._loop)
 
def CreateList(start, stop, numPts):
    SweepList=[]
    SweepList.append(start)
    stepInterval = (stop-start)/(numPts-1)
    for x in range(1,numPts):
        SweepList.append(start + x*stepInterval)
    return SweepList

def RunSweep(start,stop,numPts,delay):
    numList = CreateList(start,stop,numPts)
    sweep = Sweep(win, numList, delay)
    sweep.start()

    
win = Tk()

init_button = Button(win, text="Start", command=lambda:RunSweep(1000,10000,10,1000))
init_button.grid(row=0, column=1, pady=20, padx=10)
 
#pause_button = Button(win, text="Stop", command=sweep.stop)
#pause_button.grid(row=0, column=2, pady=20, padx=10)
 
quit_button = Button(win, text="Quit", command=win.destroy)
quit_button.grid(row=1, column=0, columnspan=3, pady=20, padx=10)
 
win.mainloop()
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  [Tkinter] TKinter Remove Button Frame Nu2Python 8 814 Jan-16-2024, 06:44 PM
Last Post: rob101
  tkinter - touchscreen, push the button like click the mouse John64 5 744 Jan-06-2024, 03:45 PM
Last Post: deanhystad
  Using Tkinter inside function not working Ensaimadeta 5 4,858 Dec-03-2023, 01:50 PM
Last Post: deanhystad
  Centering and adding a push button to a grid window, TKinter Edward_ 15 4,375 May-25-2023, 07:37 PM
Last Post: deanhystad
  Tkinter won't run my simple function AthertonH 6 3,740 May-03-2022, 02:33 PM
Last Post: deanhystad
  Can't get tkinter button to change color based on changes in data dford 4 3,362 Feb-13-2022, 01:57 PM
Last Post: dford
  [Tkinter] tkinter best way to pass parameters to a function Pedroski55 3 4,734 Nov-17-2021, 03:21 AM
Last Post: deanhystad
  [Tkinter] Have tkinter button toggle on and off a continuously running function AnotherSam 5 4,918 Oct-01-2021, 05:00 PM
Last Post: Yoriz
  [TKINTER] Problems creating directories in a selected path Vulera 2 2,727 Aug-10-2021, 06:38 PM
Last Post: Vulera
  tkinter showing image in button rwahdan 3 5,519 Jun-16-2021, 06:08 AM
Last Post: Yoriz

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020