Intro — What We’re Doing
Today, we’ll start looking at widgets that populate windows (and frames) and what better way to start than with a button or two so we’ve got something to click on besides the window’s close gadget. We’ll also look at:
- rolling our own super-class Buttons, and
- deriving child classes from that super-class.
A Button with a Callback
This demo will show—not just a button, but a button with a callback. Because… why create a button that does nothing?
Here’s what we’ve added to the simple code from previous OOP demos:
class HelloButton(ttk.Button): def __init__(self, frame): super().__init__(frame) # object attributes self.text = "Say Hello" self.message = "Hello, tkinter World!" # configure self.grid() self.config(text = self.text, command = self.say_hello) def say_hello(self): print(self.message)
The first thing you’ll notice about this demo is that it won’t win any awards for design. It does, however, illustrate the basics… plop down a button, hook up a callback, and show a result. (Check the terminal/Command Prompt/shell/what-have-you to see output.)
A new class named
HelloButton. It could also have been called simply Button, but it’s specialized to do that
‘Hello, World’ thing we’ve all come to love when exploring a new language or GUI toolkit. Here’s how that specialization breaks down:
self.text: text to be slapped onto the button face,
self.message: text to be dumped to the terminal when the callback is triggered, and
self.config(): configure the button using the previous variables.
Like all Python callbacks, the first (and in this case, only) argument must be self-referential. I won’t attempt to explain why and it doesn’t matter anyway. It’s just the way things are done in Python.
The only statement here is straightforward: dump some text to the terminal. And this is why we used the
self. prefix when we set up the object attribute in the initialization method. If we hadn’t, we’d have to pass the
message attribute as an argument. We’ll look at doing that down the road, but for now, we’re keeping it dead simple.
So, that’s what a simple
Button looks like, but most of the time, we’ll need more than a simple
Buttons of a Feather
Buttons all have a few things in common, so much so that we can set up our own super-class
Button to configure these things, then override them when we have to in the derived
Buttons have in common:
- a label (might be text, might be an image, but for now, let’s stick with text),
- an action to be carried out when the user clicks it, and
- GUI placement.
And here’s the code for our super-class:
class Button(ttk.Button): def __init__(self, parent): super().__init__(parent) # object attributes self.text = "*******" self.message = "no message" # configure self.config(command = self.do_something) self.grid() def do_something(self): print(self.message)
The object attributes are rather generic as is the
do_something() method, but they’re fine for a demo. They define:
- the placeholder button name as a row of asterisks (in case I forget to name one of the derived buttons I write later, this should stand out enough to catch my eye), and
- the placeholder message as being
no messageat all.
do_something(), all it does is spit the message to the terminal.
First, another HelloButton:
class HelloButton(Button): def __init__(self, parent): super().__init__(parent) # object attributes self.text = "Say Hello" self.message = "Hello, tkinter World!" # configure self.config(text = self.text)
Here we do an override on
self.text (the Button label) as well as
The other thing we do—and we didn’t do this in the super-class—is make a call to
self.config() to set the Button’s label text.
In fact, it doesn’t even make sense to make this call in a super-class. If we did do it there instead of in the derived class, all our buttons would have the generic label
******* despite the fact that we change this attribute for each derived class. Since that’s the way things work in Python, we might as well just make the call here in the derived class and be done with it.
Second, we have a
GoodbyeButton. The only thing of note here is that we don’t override the value of
self.message and so clicking the
GoodbyeButton spits out the generic message defined in our
And that’s all for today.
Buttons, callbacks, and derivation of classes. Good stuff to keep in mind when building UIs.
Next time, we’ll dig into sizing a
Window in a couple of different ways, one of which can be overridden by sizing demands made by child widgets.
Until then, keep your windows clean and stay safe.
Please feel free to accept this invitation to become our newest sponsor.
And have a great day!
Comments? Questions? Observations?
Did we miss a tidbit of information that would make this post even more informative? Let's talk about it in the comments.
- You can also click the link below to email us,
- follow us on the Tkooper Facebook page, or
- You can subscribe via RSS so you won't miss anything.
Thank you very much for dropping by!
© Copyright 2021 Ronald V. Tarrant