# This is meant to draw Start and Stop buttons and a label
# The Start button should start a loop in which the label
# is configured to change colour and text.
# At each pass through the loop the variable self.stop is checked:
# if True the loop should terminate.
# The Stop button should terminate the loop by setting the
# variable self.stop to True.
# I have two problems:
# 1. the Stop button does not interrupt the loop
# 2. the label only shows its reconfigured state at the end of the loop

# Please, what are my misconceptions about how this works, and what
# do I need to do to make it do what I expected?
# What I am really trying to do is show an animation but have a button
# to enable users to stop it.
# Thanks in advance for any advice.

from Tkinter import *
import time

class SGWidget(Frame):
    def __init__(self, parent=None):
        Frame.__init__(self, parent)
        self.top_frame = Frame(bg='green')
        self.top_frame.grid()
        self.makeToolbar()
	self.label = Label(self.top_frame,
			text = 'Text',bg='orange')
	self.label.grid()

    def makeToolbar(self):
        self.toolbar_text = ['Start','Stop']
        self.toolbar_length = len(self.toolbar_text)
        self.toolbar_buttons = [None] * self.toolbar_length

        for toolbar_index in range(self.toolbar_length):
            text = self.toolbar_text[toolbar_index]
            bg = 'yellow'
            button_id = Button(self.top_frame, 
                               text=text,
                               background=bg)

            button_id.grid(row=0, column=toolbar_index)
            self.toolbar_buttons[toolbar_index] = button_id

            def toolbar_button_handler(event, self=self, button=toolbar_index):
                return self.service_toolbar(button)
            button_id.bind("<Button-1>", toolbar_button_handler)
                
    def service_toolbar(self, toolbar_index):
        if toolbar_index == 0:
	    self.stop = False
	    print self.stop
            self.blink()
        if toolbar_index == 1:
            self.stop = True
	    print self.stop

    def blink(self):
	for i in range(10):
	    print 'looping',self.stop
	    self.label.configure(bg = 'black',text='black')
            self.label.update_idletasks()
	    time.sleep(1)
	    self.label.configure(bg = 'white',text='white')
            self.label.update_idletasks()
	    if self.stop == True: break

if __name__ == '__main__':
    SGWidget().mainloop()

Your initial problems stem from the fact that you are mixing tabs and spaces in your indentations. It really loused up the code on my editor. Stick with spaces (4) only!

Also, when you click the stop button during sleep(1), it most likely will not respond. You may need to use Tkinter's after().

Sorry about the tabs - new version below if anyone else wants a copy. Also I've replaced the sleep by a loop to slow things down. As will be seen it makes no difference (except being machine speed dependent), so I'm doing something pretty basic wrong.

# This is meant to draw Start and Stop buttons and a label
# The Start button should start a loop in which the label
# is configured to change colour and text.
# At each pass through the loop the variable self.stop is checked:
# if True the loop should terminate.
# The Stop button should terminate the loop by setting the
# variable self.stop to True.
# I have two problems:
# 1. the Stop button does not interrupt the loop
# 2. the label only shows its reconfigured state at the end of the loop

# Please, what are my misconceptions about how this works, and what
# do I need to do to make it do what I expected?
# What I am really trying to do is show an animation but have a button
# to enable users to stop it.

from Tkinter import *
import time

class SGWidget(Frame):
    def __init__(self, parent=None):
        Frame.__init__(self, parent)
        self.top_frame = Frame(bg='green')
        self.top_frame.grid()
        self.top_frame.update_idletasks()
        self.makeToolbar()
        self.label = Label(self.top_frame,
                           text = 'Text',bg='orange')
        self.label.grid()

    def makeToolbar(self):
        self.toolbar_text = ['Start','Stop']
        self.toolbar_length = len(self.toolbar_text)
        self.toolbar_buttons = [None] * self.toolbar_length

        for toolbar_index in range(self.toolbar_length):
            text = self.toolbar_text[toolbar_index]
            bg = 'yellow'
            button_id = Button(self.top_frame, 
                               text=text,
                               background=bg)

            button_id.grid(row=0, column=toolbar_index)
            self.toolbar_buttons[toolbar_index] = button_id

            def toolbar_button_handler(event, self=self, button=toolbar_index):
                return self.service_toolbar(button)
            button_id.bind("<Button-1>", toolbar_button_handler)
                
    def service_toolbar(self, toolbar_index):
        if toolbar_index == 0:
            self.stop = False
            print self.stop
            self.blink()
        if toolbar_index == 1:
            self.stop = True
            print self.stop

    def blink(self):
        for i in range(10):
            print 'looping',self.stop
            self.label.configure(bg = 'black',text='black')
            self.label.update_idletasks()
            #time.sleep(1)
            j = 0
            for k in range(10000000):
                j += 1
            self.label.configure(bg = 'white',text='white')
            self.label.update_idletasks()
            if self.stop == True: break

if __name__ == '__main__':
    SGWidget().mainloop()

You should definitely use tkinters .after() function, it enters a local event loop which means it doesn't block your program.

def blink(self):
        if not self.stop: # check self.stop is false before proceeding
            print 'looping',self.stop
            self.label.configure(bg=choice(COLORS))
            self.label.update_idletasks()

            self.after(100, self.blink) # after 100 ms, call function self.blink

choice is a function imported from random,
COLORS is a list containing colors, eg: ['green','red','blue'] etc

Hope that helps

Thanks for the suggestion. I still cannot get it to work. Please supply a working solution embedded in my code.

# This is meant to draw Start and Stop buttons and a label
# The Start button should start a loop in which the label
# is configured to change colour and text.
# At each pass through the loop the variable self.stop is checked:
# if True the loop should terminate.
# The Stop button should terminate the loop by setting the
# variable self.stop to True.
# I have two problems:
# 1. the Stop button does not interrupt the loop
# 2. the label only shows its reconfigured state at the end of the loop

# Please, what are my misconceptions about how this works, and what
# do I need to do to make it do what I expected?
# What I am really trying to do is show an animation but have a button
# to enable users to stop it.

from Tkinter import *
import time
from random import choice

COLORS = ['red','green','blue','orange','brown','black','white','purple','violet']

class SGWidget(Frame):
    def __init__(self, parent=None):
        Frame.__init__(self, parent)
        self.top_frame = Frame(bg='green')
        self.top_frame.grid()
        self.top_frame.update_idletasks()
        self.makeToolbar()
        self.label = Label(self.top_frame,
                           text = 'Text',bg='orange')
        self.label.grid()

    def makeToolbar(self):
        self.toolbar_text = ['Start','Stop']
        self.toolbar_length = len(self.toolbar_text)
        self.toolbar_buttons = [None] * self.toolbar_length

        for toolbar_index in range(self.toolbar_length):
            text = self.toolbar_text[toolbar_index]
            bg = 'yellow'
            button_id = Button(self.top_frame, 
                               text=text,
                               background=bg)

            button_id.grid(row=0, column=toolbar_index)
            self.toolbar_buttons[toolbar_index] = button_id

            def toolbar_button_handler(event, self=self, button=toolbar_index):
                return self.service_toolbar(button)
            button_id.bind("<Button-1>", toolbar_button_handler)
                
    def service_toolbar(self, toolbar_index):
        if toolbar_index == 0:
            self.stop = False
            print self.stop
            self.blink()
        if toolbar_index == 1:
            self.stop = True
            print self.stop

    def blink(self):
        if not self.stop:
            print 'looping',self.stop
            self.label.configure(bg=choice(COLORS))
            self.label.update_idletasks()

            self.after(100, self.blink)

if __name__ == '__main__':
    SGWidget().mainloop()

a1eio, Thank you so much! I clearly need to find out more about the difference between
self.after(100, self.blink)
and
self.after(100, self.blink())
Thanks again.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.