Button it up (Buttons and event handlers)
July 27, 2011 9 Comments
Cut to the gift department. A large lady is standing by counter holding a large cylinder with a rose attachment.
Lady Yes this looks the sort of thing. May I just try it?
Assistant Certainly, madam.
The lady presses button and a sheet of flame shoots out across the hall.
Lady Oh! Sorry! So sorry! (she is happy though) Yes that’s fine.
We’re having a look at another kind of widget today, as well as seeing some stuff about “event handling”. The widget we’re looking at is the Button – it’s what has “Ok” on it when you get a pop up dialog. The process is much the same as what we did last time for labels:
>>> from Tkinter import * >>> b1 = Button(None, text="This is a button. Can you see it has some edges that the label didn't have?") >>> b1.pack()
This should give you a button that looks like this:
Now let’s do something zany and add another one:
>>> b2 = Button(None, text="Click me!") >>> b2.pack()
Here’s the pic:
Can you see that the second button has appeared below the first button? It is also in the middle, rather than on either of the sides. Notice now that the pack() method has brackets (which indicates it’s a function?). Have a look at the function’s help info to see more details about the parameters that the pack() method accepts:
Let’s try one of them (make sure you can see the window with the buttons in it when you hit enter for this line):
Did you see the Button move?
You can try side=RIGHT on your own.
You can change the text of these buttons if you want:
Each of these buttons has associated with it a dictionary – which you should have noticed because of the use of the square brackets  with an index which is not a number. What we did above was change the value of the key ‘text’ in b2′s dictionary. To see all of the keys we in the dictionary use the standard .keys() method of dictionaries:
>>> b2.keys() ['activebackground', 'activeforeground', 'anchor', 'background', 'bd', 'bg', 'bitmap', 'borderwidth', 'command', 'compound', 'cursor', 'default', 'disabledforeground', 'fg', 'font', 'foreground', 'height', 'highlightbackground', 'highlightcolor', 'highlightthickness', 'image', 'justify', 'overrelief', 'padx', 'pady', 'relief', 'repeatdelay', 'repeatinterval', 'state', 'takefocus', 'text', 'textvariable', 'underline', 'width', 'wraplength']
Changing the values of these keys will change various characteristics of the Button. Let’s put the text back the way it was:
>>> b2['text']="Click me!"
Now, if you actually click one of these buttons you’ll see that it changes visually (more or less as you would expect a button to – it may look a little different from the buttons you’re used to because its styling comes from Tkinter not your operating system). However, you’ll also notice that it doesn’t actually do anything. This should not come as a surprise, given that we have just created a button but haven’t told Python what to do when the button is pressed.
So, let’s do that. In the list of keys above, there is a key called ‘command’. It’s currently empty:
>>> b2['command'] ''
This is where we tell Python what to do when the button is pressed. The ‘command’ key expects to receive the name of a function, so we need to create a function, then set the ‘command’ key as the name of that function. I start by setting up two constants in order to avoid spelling errors:
>>> B2BASETEXT="Click me!" >>> B2ALT_TEXT="Click me again!" >>> def b2Click(): ... if b2['text']== B2BASETEXT: ... b2['text']=B2ALT_TEXT ... b2.pack(side=RIGHT) ... else: ... b2['text']=B2BASETEXT ... b2.pack(side=LEFT)
Once that is done, we set the command:
Now clicking the button should bounce it from one side of the window to the other, changing the text each time*[see note below]:
However, we could have put anything at all in that function b2Click(), and that’s a really powerful idea. The only problem is that Tkinter won’t let you pass arguments to the function. However, not only is this not actually a problem for us at the moment (it’s not something we need to do), there’s also a way around it so it’s not really a problem at all.
So, let’s add quit button:
>>> b3 = Button(None,text="Click here to quit (without further warning)", command=quit) >>> b3.pack()
If you click this new button, Python’s quit() function is run. That will close the windows – and, indeed, exit Python as well. This buggers up my console (new lines don’t work properly). If you have this problem the reset command from the console should work (:> reset). Normally if you have a “quit” command it may result in users losing data they haven’t saved, so you will usually give users a warning to confirm they really want to quit. You would probably “wrap” this call to quit() inside a separate function which asks the user to confirm that they want to quit.
Clicking a Button (or, indeed any other part of the window) is called an “event”. The function which does something when you click the button is called a “handler” (also “event handler” or “callback handler”).
Another thing to notice here is where the third button ended up (since we didn’t specify a side to pack it on). All I’m going to say about this is that laying out a user interface with Tkinter can be a little difficult to understand. We’ll talk more about layout later.
* the proper way to do this is to start up Tkinter’s mainloop() method. We will do this later (and if your clicks don’t seem to work let me know!) This method does the job of listening for events (clicking the mouse on a button is an “event”) generating messages and communicating them to callback handlers (in this case the function b2Click is a callback handler).