It’s not only for Raspberry Pi’s of course, but python is somewhat of a default language when it comes to the Pi. My son started of coding on his Pi in Scratch, but got frustrated by its limitations so moved on to python. When I started working with raspberry pis, pretty well a year ago exactly now, I used python because there were so many libraries available already. However pretty well everything I have done with the pi has been to drive a robot, and any screen output that I’ve used has just been text-based. But graphical user interfaces (GUIs) are nice, so I decided that it was time to learn how to do them with python.
Having made that decision, the question was “A graphical user interface for what?” I guess I could have done some sort of real-time monitoring of one of my robots, but I wanted to start with something simple. Well, I got some inspiration when I spotted this article yesterday. Pi Wars was on in Cambridge about a month before the new North Cambridge station opened, and the friend I visited while there was just a short walk from it – so naturally I was interested in it for future visits. When it opened, I saw lots of pictures of its clad exterior, and references to Conway’s game of life. I didn’t study it too carefully, because I was in the midst of a mountain of work, but every time I saw one of these pictures, I thought to myself “That doesn’t look like any game of life pattern I recognise!” Anyway, the article above confirmed my hunch, that it’s not game of life at all. (Indeed, it’s linked – horror horror – to an Oxford mathematician, Stephen Wolfram, instead.) Anyway, I decided that I would implement a GUI to run this game. (Conway’s game of life, nothing to do with Stephen Wolfram’s rule 135/30.)
Want to learn about the Conway’s game of life? Wikipedia is an easy starting point. Essentially, there is a 2D grid of cells, and each cell’s behaviour is governed by the following rules:
- Any live cell with fewer than two or more than three live neighbours dies.
- Any live cell with two or three live neighbours stays alive.
- Any dead cell with exactly three live neighbours becomes a live cell.
- All other cells stay dead.
Nice and simple. In fact, I seem to recall doing a text-based implementation of this as a second year undergraduate. (Which I guess was implemented in C.) But the point of this exercise was not the implementation of the rules (these are nice and easy), but the GUI to display the results. So I had a bit of a look about, and the first thing I discovered is that there are a lot of different ways of implementing GUIs in python. For better or worse, I chose to do mine using Tkinter. And I have to add a disclaimer here: although I haven’t written GUIs in python before, I have done lots of GUI programming before, in C, C++, and Java. So I started with knowing what it was that I was looking for… and I’m glad that I did, because even with that knowledge it was difficult to find good online resources. I’d love to be able to point you to a definitive resource, but I didn’t find one. The best I found was this, but it didn’t answer all my problems, many of which I solved by cross-referencing multiple web pages.
The basic idea of GUIs is that you have a series of objects (frames, labels, buttons, canvases and more) that fit within a window, or sometimes within a nested object within a window. I started out by sketching roughly the components that I wanted, like this:
Once I had this basic model, I set about composing it. The canvas is the bit for drawing, so within that I had a grid, in which I could have living or dead cells. Below that, the first frame was the control panel, with a slider to control the speed, and buttons for load, save and run. Then the final frame contained instructions, which I simply inserted as a series of labels (one label per line of instructions). Then at the very bottom was the all-important “quit” button. (Always include a way to quit!)
The most difficult thing (as with most GUI systems) was deciding how to specify the layout. I used a combination of two layout types: pack, which is fairly basic, and grid, which gives a bit more control. The only place I used the grid layout was in the control frame, where the slider spans three columns on one row, and the buttons have one column each in the row below.
Once the window was displaying happily, the next step was to add functionality – callbacks for the buttons and slider, and updates to the canvas. Callbacks are the functions that get called when you perform an action (such as click) on an object within a window. So for example, the callback attached to the quit button causes the window to close and the application to terminate.
I’m not going to provide a step-by-step tutorial, but if you’d like to see what this looks like in my code, here you go: