Python coding – Part 8: Making GUI apps
This month, Darren Yates begins a gentle look at how to create Graphics User Interface (GUI) apps in Windows using the Python programming language.
If you’re interested in tech, learning to code is one of the best things you’ll ever do, giving you skills that not only make you more employable, but also allow you to ‘open the bonnet’ and see how stuff works. So far in this series, we’ve covered the basics you need to create a range of Python apps, from variables to file operations and even classes. This month, we make the leap from command-line to GUI apps that will run on any Windows computer.
COMMAND-PROMPT VS GUI
All the apps we’ve written so far in this series run in the command-prompt window and despite what some may consider a boring façade, the command-prompt is a great way to start coding because the ‘user interface’ is taken care of for you. All you need are print() and input() functions and you’re away.
But when you start writing GUI apps, you get to decide where everything goes — the buttons, the textboxes, radio buttons and more. It also means you now have two coding streams to consider — the app’s functional code or ‘algorithm’ and the user interface design. Which do you code first — the app algorithm or the user interface? It depends, but if you’re designing software professionally, you’d more likely start by consulting stakeholders — the people who’ll have to use your software — to find out the features and functions they require, then build your software around that, take it back to stakeholders for checking and so on. Still, rather than get bogged on the ‘systems design and analysis’ road, we’ll just cover the nuts and bolts of creating Python GUI apps.
TRY-IT-AND-SEE
If you play around with Microsoft’s Visual Studio or Google/JetBrains’ Android Studio, you’ll find these integrated development environments (IDEs) come with layout editors or ‘GUI builders’ that are a bit like desktop publishing for apps — you can drag and drop controls onto the app window as you want them to appear in the real thing. However, Python’s IDLE development environment doesn’t have this, but that’s not a bad thing when you first start — because you’re forced to code manually and see what
“You now have two coding streams to consider — the app’s functional code or ‘algorithm’ and the user interface design.”
works. This way, you get to understand what the GUI code actually does. Sure, once you’re more familiar with things, move onto other more feature-rich IDEs (as most people do). But to begin with, the more you understand how your code works, the easier you’ll find coding in the future.
CREATING A WINDOW
Python, on its own, doesn’t know anything about GUI apps, but the power of Python is in its modules or ‘libraries’ that enable you to bolt into your code almost any functionality you can think of. GUI apps are no exception to this and there are at least a dozen different libraries you can add that handle GUI development alone. However, since the IDLE environment itself is written in Python, it comes with a GUI library bundled called ‘tkinter’. It’s actually a programming language of its own called ‘tcl’ with an extension called ‘tk’ for creating GUI controls or ‘widgets’. Combined, they’re known as ‘tcl/tk’ and come preinstalled in many Linux/Unix distros, including Mac OS X (although, you need to be careful here — tinyurl.com/lvrb835).
To create a simple ‘hello world’ Python GUI app, we need to import tkinter (short for ‘tk interface’) into our app and in IDLE on Windows, we do that with: import tkinter
Last month, we introduced the concept of ‘object-oriented programming’ (OOP) and ‘classes’, so let’s use one here: import tkinter class GuiApp: def __init__(self): self.window = tkinter.Tk() self.window. geometry(‘400x300’) tkinter.mainloop() my_ gui_ app = GuiApp()
The class, called ‘GuiApp’, defines what the interface will look like, which here, is just a blank window. We define it inside the initialisation (“__init__”) or ‘constructor’ function and fire up tkinter’s mainloop() function to display it. After that, we create a copy or ‘instance’ of the GuiApp class and call it ‘my_ gui_app’, which automatically creates the window and displays it. The size of the window in pixels is set by the geometry() function and the character array parameter ‘400x300’.
TEXT LABELS
An empty window isn’t particularly useful, so open up ‘helloworld.py’ in the IDLE editor from this month’s source code pack and we’ll start adding widgets, two of the most common being labels and buttons. Labels let you add text to a window, buttons add user-control commands for further actions. To add a label, you use tkinter’s ‘Label’ function — it has a number of parameters, most of which are optional, except the first, which identifies where the label belongs.
Labels, like other tk widgets, have to belong somewhere, they have to have a ‘parent’ widget. Here, we assign the label to self.window, which is the root tk widget for this class. You also must specify the text you want the label to display. You have two options for this – you can use a fixed string or ‘literal’, such as “hello world”, or you can assign a variable.
Remember us saying previously that tkinter uses the tcl language? To pass text to the label, we need to create an instance of a tk variable class called ‘StringVar’ (String Variable):
self.labelText = tkinter. StringVar()
self.labelText.set(“Hello world”)
self.label = tkinter. Label(self.window, textvariable=self.labelText, font=”Arial 24 bold”)
self.label.pack()
We then assign text to the variable using StringVar’s set() function. Lastly, we call the pack() function to add the
label to the window, which invokes the layout manager. Tkinter has three main layout managers — pack, grid and place.
‘Pack’ brings in the various widgets you code and automatically places them within the window, using only the space needed. However, the alignment may change depending on how the user changes with window size. ‘Grid’ uses a table-like arrangement, aligning widgets into rows and columns. It’s simple enough, but fairly rigid. ‘Place’, on the other hand, lets you decide exactly where a widget sits in the window, down to the pixel. It sounds great, but if users can resize your app window, those fixed-position widgets won’t neatly realign of their own accord. So while it’s versatile, ‘place’ requires more work to always look right (unless you lock your app window size).
COMMAND BUTTONS
Command buttons are the most common app control and adding a button is similar to adding a Label. In our ‘helloworld.py’ example, we create an instance of tkinter’s Button object and assign it to a new object ‘self.buttonOK’. The first parameter is, again, the parent widget, followed by the text you want the button to show. After that, you indicate the function you want launched when the button is pressed (in this case, it’s a function we create called ‘changeLabel’). Again, we ‘pack’ the buttonOK object into the self.window widget and finally, run the main tkinter loop.
We haven’t created the ‘changeLabel’ function yet, so let’s do it now. We start by defining it as a separate classmember function using the ‘def’ keyword and here, just to keep things simple, we’ll get it to just change the above label text to read: ‘Hello yourself!’ So the link between the command button and the function is the ‘command’ parameter in the Button() function initialisation (remember the ‘self’ prefix).
TEXT ENTRY
The other common widget is the Entry widget, which provides the user a single-line text entry area. Open up ‘entername.py’ and you’ll see the only mandatory parameter is, again, the parent widget. Now to get hold of the text the user enters, use the get() function, which returns the text as a string object — in this example, we’re changing the label text by incorporating the contents of this string object as a personal greeting.
CENTRING THE APP WINDOW
One thing you might want to adjust straight away is the location of your app on the computer screen. At the moment, our apps are all located near the top-left corner, by default. Python and tkinter don’t have a function to auto-centre app windows, but you can code your own using built-in parameters. First, decide on the app window frame size you want by choosing a ‘width’ and ‘height’, in pixels. Next, get tkinter to return the current screen width and height, again, both in pixels, using the functions winfo_screenwidth() and winfo_screenheight(). Now, halve the screen width and subtract half the app window width, then halve the screen height and subtract half the app window height and you’ll have the top-left corner coordinates that guarantee your app will appear front-and-centre.
KNOW YOUR TIMES TABLES
OK. We’ve now got some basics under our belt — we can create a window with a label, command button and user text-entry widget. Now let’s use some of the other Python knowledge we’ve gathered over the last few issues and create a GUI app. Nothing too complicated, just something to get started. And remember, this is just one way to do it — there’s always more than one way to code an app.
Our new ‘quiz.py’ app is a basic times-table quiz that asks you to answer random times-table questions and keeps track of the number of questions you get right, along with the total number asked. It builds on what we’ve just learned, but also adds
in some extra tweaks. For example, adding a title to the app window. You add it to your tk() object using the title() function — just give it a string of text and that’s it.
The app also has three labels — labelQuestion, labelCheck and labelScore. Note also the new font parameter — you can change the font type and size with the ‘font’ string, starting with the font name, its point-size and any font additions (bold, italics and so on).
PACKING ORDER MATTERS
One other thing to remember — the order in which you enter the ‘pack()’ statements into your code is the order they’ll appear in your app. You can put these statements wherever you want (within reason), but the order matters. The same also occurs for ‘grid()’. However, since ‘place()’ allows you to set the exact positioning, widget order doesn’t matter in that instance.
CLASS FUNCTIONS
At the start of this masterclass, we mentioned that GUI apps require you to consider two streams — the GUI design code and your app functionality. The quiz app functionality here mostly takes place in the two class functions, checkAnswer() and newQuestion().
The app begins by running newQuestion() to generate a new times-table question. This consists of generating two random integers between zero and 12 and assigning them each to a variable, ‘arg1’ or ‘arg2’. We then use these to update the textQuestion variable, which appears in the labelQuestion label. We also delete any text inside the entryAnswer user-entry textbox.
When the user enters an answer and clicks on the OK button, it fires up the checkAnswer() function, which checks whether or not the answer is correct. If it is, it notifies the user and adds one to the ‘totalCorrect’ variable. If it’s not, it notifies the user the answer is wrong.
Now note the source code indentation here — as we’ve mentioned many times previously, code indentation is of primary importance in Python. The last three statements of the checkAnswer() function are in the same indent alignment as the if-else statement, meaning these statements are separate to the if-else statement.
In these last three statements, we add one to the ‘totalQ’ tally, update the score and launch the newQuestion() function again to come up with the next question. As a result, the app cycles around this loop until the user gets bored and shuts down the app.
BINDING KEY EVENTS
Pressing the OK button with the mouse every time is a bit of a pain. What about being able to also use the enter/return key? You can do that by attaching or ‘ binding’ this event of pressing the enter key to the entry widget. A couple of things, though.
First, tkinter calls the enter key ‘<Return>’ rather than ‘<Enter>’. The function we want to launch when the enter key is pressed is checkAnswer(), as before, but you must also include the ‘event’ parameter in the checkAnswer function’s parameter list, otherwise, logic error.
EVENT-DRIVEN PROGRAMMING
If you’ve been following this series, you might have seen a bit of a difference in the way the apps this month have been coded compared with previous episodes. GUI-based apps are all about events — a user pressing a button or a key, checking a box or entering text. This ‘eventdriven programming’ concept parallels object-oriented programming.
Here, you’re not directing the user to perform a task by following a specific code path. Instead, you allow the user to ‘drive the bus’ and your code simply responds to actions they select. Think about the way you use your phone, PC or notebook — your productivity apps, for example, let you choose when you want to change a font or save a file or whatever. Using an event-driven programming paradigm is how that’s achieved.
As you can imagine, we’ve barely scratched the surface of what Python GUI programming is all about. See you again next time.