1

I am trying to create a button in Python (using Tkinter), that changes color every time I click it. I have this so far:

from Tkinter import *

class Application(Frame):

    def __init__(self, master):
        Frame.__init__(self, master)
        self.grid()
            #initialize frame
        self.create_widgets()
        self.bttn_clicks = 0
            #initiates button clicks

    def create_widgets(self):
        self.bttn1 = Button(self)
        self.bttn1["text"] = "Click Me!"
        self.bttn1["command"] = self.update_count
        self.bttn1.grid()
            #creates button

    def update_count(self):
        if self.bttn_clicks + 1:
            self.bttn1.configure(background = "blue")
        if self.bttn_clicks + 2:
            self.bttn1.configure(background = "green")
        if self.bttn_clicks + 3:
            self.bttn1.configure(background = "orange")
        if self.bttn_clicks + 4:
            self.bttn1.configure(background = "red")
        if self.bttn_clicks + 5:
            self.bttn1.configure(background = "yellow")
            #changes colors from blue, to green, orange, red, then yellow

root = Tk()
root.title("Color Button")
root.geometry("200x85")
app = Application(root)
root.mainloop()

If anyone could help, I would greatly appreciate it. Also, I am new to programming and Python, and I just started using Tkinter 3 days ago - so keep that in mind.

  • possible duplicate of [How to change button color with tkinter](http://stackoverflow.com/questions/5543815/how-to-change-button-color-with-tkinter) – unutbu Nov 15 '11 at 23:28
  • 2
    @unutbu: nope, this is slightly different. The title is roughly the same. That other question was just a bug in some code, this is more of a "how do I do it?" -type question. Though, the solution to the other one can certainly act as an example for this one. – Bryan Oakley Nov 16 '11 at 00:10
  • @unutbu I already looked at that before I posted this question, and it did not help me. – Katie Roberson Nov 16 '11 at 01:26

2 Answers2

4

Python has iterators. They are similar to lists, but the items in the iterator are generated as they are needed, rather than enumerated in their entirety at definition-time. Here is an example of an infinite iterator, made by wrapping a tuple with itertools.cycle:

In [119]: import itertools

In [120]: color=itertools.cycle(('blue', 'green', 'orange', 'red', 'yellow'))

In [121]: color
Out[121]: <itertools.cycle object at 0x9a1846c>

You can pull out values from the iterator by applying the next function to it:

In [122]: next(color)
Out[122]: 'blue'

In [123]: next(color)
Out[123]: 'green'

If we were to keep calling next enough times, the iterator would reach yellow, then cycle back around to yield blue again. Thus it is an infinite iterator, and it describes perfectly what colors we want for the button without having to mess with any counter variable.

So instead of counting with self.bttn_clicks, you could use this iterator this way:

import Tkinter as tk
import itertools

class App(tk.Frame):
    def __init__(self, master):
        tk.Frame.__init__(self,master)
        self.master=master
        self.grid()
        self.colors=itertools.cycle(('blue', 'green', 'orange', 'red', 'yellow'))
        self.bttn1=tk.Button(self, text='Click Me!', bg=next(self.colors),
                             command=self.change_color)
        self.bttn1.grid()
    def change_color(self):
        color=next(self.colors)
        self.bttn1.configure(background=color)

root=tk.Tk()
root.title('Color Button')
root.geometry('200x85')
app=App(root)
root.mainloop()

Note that the button is highlighted when the mouse is over it. So when you click the button it looks grey. You have to move the mouse off the button to see the colors.


The code

if self.bttn_clicks + 1:

tells Python to evaluate the expression self.bttn_clicks + 1. Since self.bttn_clicks starts out at 0, this expression equals 1. In Python 0 is considered Boolean false, while all other numbers are Boolean true (even float('nan') and float('inf')!). So the if self.bttn_clicks + 1 evaluates to True and the if-block is executed.

Thus the button is set to blue:

self.bttn1.configure(background = "blue")

Next the conditional

if self.bttn_clicks + 2:

is evaluated to 0+2 which is equivalent to 2 which is again True, so the next if-block is also executed. Now the button is set to green. And so on.

Obviously, that's not what we want. Instead, increment the value of self.bttn_clicks by one, but have it wrap back to zero when it exceeds the index corresponding to the last color (see how laborious this is compared to using an iterator?):

self.bttn_clicks=(self.bttn_clicks+1)%5

and test which value self.bttn_clicks now equals:

   if self.bttn_clicks == 0:
        self.bttn1.configure(background = "blue")
   elif self.bttn_clicks == 1:
        self.bttn1.configure(background = "green")
   ...
unutbu
  • 842,883
  • 184
  • 1,785
  • 1,677
  • Okay, I see what you are saying, thank you! What if I want to change the color by counting the clicks using self.bttn_clicks - mainly because I am now curious about how to do it. – Katie Roberson Nov 16 '11 at 03:30
  • Wow, okay, I finally got it to work! Thanks so much! I actually understand what my program is doing now. – Katie Roberson Nov 16 '11 at 04:58
1

Before you edited your question, you were not saving the reference to the button, and thus had no way to later modify it. To be able to modify a widget you need to save a reference to it. Then, later you can modify it using the configure method to change its color:

self.bttn1 = Button(...)
...
self.bttn1.configure(background = "red"
Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
  • What do you mean save a reference to the button? I actually just updated my work, because I have accomplished some things since I last posted this question. – Katie Roberson Nov 16 '11 at 01:32
  • "save a reference" means to assign the instance of a widget to an attribute of the class (eg: `self.bttn1 = ...`) rather than assigning it to a temporary variable (eg: `buttn1 = ...`). – Bryan Oakley Nov 16 '11 at 18:04