.. _sec-graphical-user-interfaces: =========================== Graphical user interfaces =========================== [status: somewhat fleshed out, but not complete] .. rubric:: Prerequisites * The 10-hour "serious programming" course. * A GNU/Linux system with python3 and python3-tk installed (on an Ubuntu 16.04 system this can be done with ``sudo apt install python3-tk``) A chat about sources of input in a GUI ====================================== The programs we first write with text-based user interfaces, like the tic-tac-toe program in the basic course, have a very simple flow: #. You get input from the keyboard (and *only* from the keyboard). #. You do some processing based on that input. #. You write some output to the terminal. Later, in :numref:`chap-data-files-and-first-plots`, we learned to write programs which read data from a file. Still, even in this kind of program you are always only reading input from one place at a time. At this point I talk to the class about how there are two types of programs that do things quite differently: **network** programs and **GUI** programs. In network programs you can have connections open to several different hosts, and you might be reading input from several of them at once. In A GUI program you can have input from the keyboard, you can also have mouse movement and mouse clicks. The program also has to track other events that can affect its behavior, like the movement of a window so that a different portion of it is exposed. GUI programs often use what's called an :index:`*event loop*`: you set up your programs layout with windows and :index:`widgets` and then go in to an infinite loop where you call a function that checks if any events (mouse, keyboard, ...) have occurred. In discussing this I would then to go the whiteboard and draw an example of a simple GUI with a couple of buttons. With that up I would discuss *what happens when the user clicks on something?* This leads to the discussion of a :index:`*callback*` or :index:`*callbacks function*`. Widgets and widget sets ======================= Discuss what are widgets? Little self-contained user interface bits (like buttons and text entry fields and canvases) which you can place in your program's graphical interface. As is often the case in the free software world, there is a dizzying array of different :index:`*widget sets*` available for programming in Python. Tkinter, wxPython, PyQt, PyGTK, PySimpleGui, Kivy, ... The most basic widget set, Tkinter, is included as a standard part of Python. It allows you to develop reasonable comprehensive graphical interfaces, so I will use that as our starting point. Another which has gained adoption since 2018 is PySimpleGUI, and we will give it a quick glance at the end of this chapter. The simplest programs ===================== The programs ------------ Simple single OK button program. .. _listing-ok-simplest-py: .. literalinclude:: ok-simplest.py :language: python :caption: ok-simplest.py - program with an OK button. Sure, but that does nothing. Let's make it do something when you press the button: .. _listing-ok-callback-py: .. literalinclude:: ok-callback.py :language: python :caption: ok-callback.py - program with an OK button that does something. Now let's make it able to quit with a "Quit" button: .. _listing-ok-callback-quit-py: .. literalinclude:: ok-callback-quit.py :language: python :caption: ok-callback-quit.py - program with an OK button that does something simple, and a Quit button that exits. Observing this simple program raises a couple of questions about things we saw in it: Packers: more than just one button ---------------------------------- One concept in GUI programs is :index:`*geometry management*`. How does the program lay out all the widgets? A programmer could say: put this widget at coordinages (12, 74), and this other one at coordinates (740, 210), and so forth. This would be *terrible* style. It would do the wrong thing if the window gets resized, and it would become impossible to maintain when there are more widgets. Widget systems introduce the idea of "geometry management" to deal with this. The calls to ``button.pack()`` that you saw in ``ok-callback-quit.py`` are an example. We told the widgets to pack themselves inside their parent window, and much was taken care of automatically by doing that. We did not, for example, have to specify the position of the buttons in the window. If you resize you will notice that the buttons are kept in somewhat reasonable positions. As we go on our tutorial tour of widgets let us pay attention to what happens with the packing of widgets inside windows. A tour of widgets ----------------- We only saw button widgets, but this is a chance to point out what other widgets there are. It's hard to get an exhaustive list since one can write custom widgets, but here are some to mention: * button * canvas * checkbutton * combobox * entry * frame * label * labelframe * listbox * menu * menubutton * message * notebook * tk_optionMenu * panedwindow * progressbar * radiobutton * scale * scrollbar * separator * sizegrip * spinbox * text * treeview Following a tutorial ==================== We will now follow this tutorial: https://www.tutorialspoint.com/python3/python_gui_programming.htm This tutorial has links to most of the basic Tkinter widgets, with examples of how to use each one. In the course I have the students bring them up one by one, pasting them in to the python3 interpreter to see them at work. The one that might be most interesting is the *Scale* widget: it shows three interacting widgets where the slider sets a value, the button then reads that value and causes a label to be updated. I would go through that example in greater detail. Cellular automata on a canvas ============================= Students might want to read this before going through this chapter: https://en.wikipedia.org/wiki/Elementary_cellular_automaton A simply drawing of the CA -------------------------- We have an example of a cellular automaton elsewhere in the book (:numref:`chap-emergent-behavior`). We can use the routines in that program to compute the cellular automaton, and just add the graphical portion in this program. Download the simple_ca.py program from :numref:`sec-write-the-simple-ca-py` and save it in a file called ``simple_ca.py``. Then take a look at the ``draw_ca.py`` program in :numref:`listing-draw-caca-py` and try running it. .. _listing-draw-caca-py: .. literalinclude:: draw_ca.py :language: python :caption: draw_ca.py - draws a cellular automaton graphically. Change the initial conditions, the size of the automaton, experiment with it. .. exercise:: Modify draw_ca.py to have a *first row editor*: a small canvas which allows you to modify the cells in the first row so that you can run any configuration you choose. .. exercise:: Modify draw_ca.py to have a *rule editor*: a small canvas which allows you to modify the rule for the cellular automaton evolution. You should also have widgets to allow a different number of cells, a different number of rows, and more or less states or neighbors. .. exercise:: You might have noticed that the drawing of the canvas gets slower and slower as you have more and more squares. You can get around this by commenting out the ``w.update()`` call so that the cellular automaton only gets drawn at the end of the run, but then you miss out on seeing the progression of the cellular automaton. Modify ``draw_ca.py`` to be more efficient in drawing the canvas. You can use the information at this stack overflow link as a guide: https://stackoverflow.com/questions/10515720/tkinter-canvas-updating-speed-reduces-during-the-course-of-a-program Adding controls to the program ------------------------------ Then save :download:`draw_ca_with_controls.py` and study it and run it. At this time the program is not yet too clean (FIXME: update this text when I update the program), but we can discuss the new mechanisms that appear in this program: * The canvas does not just run automatically: we control it with the buttons. * We use the canvas's ``after()`` function to make the canvas draw "in the background" while the controls are still active. This means we can pause, for example. .. exercise:: Introduce a "first row editor" widget between the controls and the canvas. This recognizes mouse clicks and lets you set the initial cell values so you don't have to set them with code in the program. .. exercise:: introduce a "rule editor" widget, possibly on the same row as the control widgets. You could start by just taking a number between 0 and 255. Them move on to 8 squares or checkboxes, where you would click on them to activate the binary cells that end up in the rule string. But the coolest might be to make a widget that shows the 3 cells and their child, with the 8 possible 3-cell configurations, and picking if they turn in to 1 or 0. Conway's game of life ===================== Goal: create a canvas which allows you to click to select initial cell values. Then kick off Conway's game of life rule. I don't have much explanatory text yet, but for now let's discuss the program we have. Download the ``conway_life.py`` program from :numref:`sec-write-the-simple-ca-py` and save it in a file called ``conway_life.py``. Then save :download:`draw_conway_life.py`. We study the programs together and see how the GUI version works, and then run it. Tic-tac-toe with buttons ======================== .. _listing-ttt-gui-py: .. literalinclude:: ttt-gui.py :language: python :caption: ttt-gui.py - GUI for the tic-tac-toe program we wrote in the basic Python course. You can use that GUI program with the underlying text-based program we wrote in the basic Python course: :download:`tic_tac_toe.py` A glance at PySimpleGUI ======================= Start with: .. code-block:: console $ pip3 install PySimpleGUI Following the tutorial at https://realpython.com/pysimplegui-python/ (archived at https://web.archive.org/web/20231225142347/https://realpython.com/pysimplegui-python/ ) let us create ``ok_psg.py``: .. _listing-ok_psg-py: .. literalinclude:: ok_psg.py :language: python :caption: ok_psg.py - simple introductory program for the PySimpleGUI widgets. Other resources =============== https://tkdocs.com/tutorial/ (does many languages side-by-side) http://zetcode.com/gui/tkinter/ (object oriented; this is too soon to use OOP in this course) https://likegeeks.com/python-gui-examples-tkinter-tutorial/ (maybe good) https://dzone.com/articles/python-gui-examples-tkinter-tutorial-like-geeks (maybe good) https://www.python-course.eu/tkinter_labels.php (maybe good because it has modern preferences like "import tkinter as tk", but maybe too lengthy in the early examples) https://www.tutorialspoint.com/python3/python_gui_programming.htm