Graphical user interfaces
A graphical interface inverts control: the program no longer commands, it waits. That change of mindset —from the sequential program to the event loop— is what you have to understand first.
01The program stops being in charge
A console program is in charge: it asks for a value, calculates, prints, ends. A graphical interface reverses that relationship. The program starts, draws the window and waits; the one who decides what happens is the user.
| Sequential program | Event-driven program |
|---|---|
| The code controls the order | The user controls the order |
scanf blocks until there is data | Nothing blocks: you are notified when something happens |
| Starts and ends | Lives in a loop until the window is closed |
| A single line of execution | Many handlers, each one short |
Underneath every graphical interface there is the same loop, whatever it is called:
while the window is open:
event = get_from_queue() # waits if there is none
handler = find_handler_for(event)
handler(event)
redraw_what_changed()
The operating system deposits every click, every key press and every mouse movement into that queue. The program takes them out one at a time and calls the corresponding handler.
Use the controls below: each action queues an event. Process them one at a time and see which handler runs.
02Widgets and containers
An interface is built from widgets —the controls— placed inside containers that decide how they are arranged. The container tree is what lets the window adapt when it is resized, instead of having everything in fixed coordinates.
| Widget | What it is for | Event it generates |
|---|---|---|
| Button | Triggering an action | click |
| Text field | Typing a value | change, key, focus |
| Checkbox and radio button | Choosing between alternatives | state change |
| Slider | A continuous value | change, many times per second |
| List and table | Showing and choosing records | selection |
| Canvas | Free drawing | redraw, mouse |
There are two approaches to arranging them: absolute positions —quick to do, but they break as soon as the size or the font changes— and layout managers by rows, columns or grid, which is always the better choice.
03Separating the interface from the logic
The rule that prevents the most problems: a button’s handler does not do the work, it only calls whatever does. If the calculation, the measurement or the access to the device is written inside the handler, it cannot be tested without opening the window, nor reused from a script.
def on_press_measure(): # handler: three lines
v = instrument.measure_voltage() # the logic, separate
label.config(text=f"{v:.3f} V")
chart.add(v)
That is the core of the patterns studied under their own names —model-view-controller and its variants—: the model holds the data and the rules, the view displays them and the controller translates events into calls to the model.
If a handler takes two seconds, the window is frozen for two seconds: it does not redraw, does not respond and the system marks it as “not responding”. Anything that takes time —reading a port, downloading, heavy calculation— goes in another thread, and when it finishes it notifies the interface. The rule is strict: only the interface thread may touch the widgets; the others send it the result through a queue or through the mechanism the library provides.
04An instrumentation interface
In electronics, the interface almost always accompanies a piece of equipment: a measurement bench, a process control, a recorder. What is expected of it:
- Visible status. Connected or not, on which port, since when.
- Continuous reading without blocking. One thread reads the instrument; the interface refreshes every 100 or 200 ms, not at 60 frames per second.
- Units and significant figures. Show 3.7 V, not 3.700000001.
- Logging and export. Saving the series as CSV is the first thing anyone using it will ask for.
- Errors in plain sight. If the equipment was disconnected, say so; do not leave the last value frozen.
- Confirmation of anything dangerous. A power output is not turned on with a careless click.
| Toolkit | Language | Where it fits |
|---|---|---|
| Tkinter | Python | Comes with Python; ideal for getting started and for internal tools |
| Qt (PyQt / Qt in C++) | Python or C++ | Large, cross-platform applications with graphics |
| Dear ImGui | C++ | Debugging panels and lab tools |
| Local web interface | Any + browser | The device serves a page; it is used from any machine |
05In the lab
Build a window with a button, a field and a slider, and make each handler print to the console which event arrived. Move the slider and count how many events a single drag generates.
Put a sleep(3) inside a button’s handler and check that the interface goes
dead. Then move that work to a thread and verify that the window stays alive.
Using the protocol from the previous page, build an interface that turns an output on and off, sets a PWM with a slider and shows the sensor reading on a scrolling chart.
06Common mistakes
- Doing heavy work in the handler and freezing the window.
- Touching widgets from another thread. It is the cause of the hardest crashes to reproduce.
- Mixing the logic with the interface and being left with no way to test anything.
- Positioning everything in absolute coordinates and having it break when the size changes.
- Redrawing the entire chart at 60 frames per second when the data changes ten times per second.
- Not reporting the error and leaving the last value on screen as if it were current.
07Self-assessment
What is the event loop?
The cycle that takes events from the queue, calls the corresponding handler and redraws. Every graphical interface has one.
Why does a window freeze?
Because a handler is taking a long time and the loop cannot keep processing events or redrawing.
Can a worker thread update a label directly?
No: only the interface thread may touch the widgets. The worker thread sends it the result through a queue or through the mechanism the library provides.
How many events does dragging a slider generate?
Many, one for each change of value. That is why a slider’s handler has to be cheap, or the real work has to be spaced out.
What advantage does a layout manager have over fixed coordinates?
The window adapts when the size, resolution or font changes, without the controls overlapping.
What does the model-view-controller pattern separate?
The data and rules (model), what is displayed (view) and the translation of events into actions (controller). It allows testing the logic without opening the window.
08Further reading
- Mark Summerfield. Rapid GUI Programming with Python and Qt. Prentice Hall, 2007. Events, threads and interface design with a real-application mindset.
- Official Python documentation. tkinter — Interface to Tcl/Tk. Python Software Foundation. The library that comes already installed: enough for most lab tools.
- Jakob Nielsen. Usability Engineering. Morgan Kaufmann, 1993. The classic heuristics: visibility of status, error prevention, user control.