The PySimpleGUI Architecture - Under The Hood
If you want to get a little deeper understanding of how PySimpleGUI differs from "traditional GUI libraries", this is the place.
First, understand that this section is a summary, a simplification, an overview of some other framework architectures, as well as an overview of the PySimpleGUI architecture. It's been simplified and there are generalizations, so there is no need to shout and point at your screen.
Let's get some terms understood to make the descriptions go smoother.
Event - Something that happens. Can be a button click, an item chosen from a drop-down menu, a menu item chosen, a character typed... LOTS of events are possible in a GUI.
Callback - When a function you've written is called due to an event occurring.
Queue - A data structure in computer science. Like a list where items are added on the "input end" and taken out on the "output end". The result is the when a queue is "read", the items are taken out of the output end in the order they were put into the input end.
Message - "Some data". Could be a variable, a tuple, a string... what type it is doesn't matter."
User/Application Code - Python code that you write that's in your .py file(s).
Object Oriented GUI Development / "Event Driven" (Push Modal)
This is THE topic that trips up programmers trying to learn GUI development. The "problem" is that human being don't think in a highly "distributed" or fragmented way. This style of architecture consists of many small functions containing the code that will handle your "event".
Let's call this the "Push Model" because when an event happens, it's actively "pushed" at you. With these architectures, the application usually creates the GUI layout, and then as the last statement in the program calls a function like
mainloop contained in the "window" class. In tkinter it's called
mainloop. You'll often find tkinter examples that specifically read
root.mainloop(). A search for
root.mainloop on GitHub returned 486,000 results. This function does not return until the window is closed.
That last statement is important. Your code, is written linearly in your .py file, top to bottom, and at the end, you call a function that never returns until the window is closed.
What is this
mainloop() function? I'm glad you asked! It's the "GUI Dispatcher". When you created your window, you created each widget such as Buttons. When you created each button, you told tkinter the name of the function to call for that button. This function is a "callback function".
The result is that you've got lots of these little functions that you wrote for each widget. Typically these functions are actually "methods" in a class. You'll make a class based on a tkinter Button, and in that class you'll have a method that you tell tkinter to call when that button is clicked.
Your code may look something like this for a GUI with 4 widgets - A Button, a Slider, an Entry (text input ), and another Button.
You'll have 4 classes and the code for those callback functions are going to be scattered inside one file or could be split across several files.
Stepping Through a Run
OK, we've got the stage set, let's run your code.
You've written the code for your classes, you've written the callback functions (usually as methods in the classes) and you've just called the
At this point, the "GUI Library Dispatcher" is running. This is not code you ever see. It's not code in your program. The Dispatcher waits until something happens.
Your user clicks a button!
Ok, something's happened! In the "Push Model" architecture diagram the "Button Click" block at the top represents the button click happening. This causes some code in the blue "Dispatcher" box to run. That code determines which of your "Callback Functions" should be called when that button is clicked.
The Dispatcher then calls your callback function for that button.
Now your code is called. In the Push Model diagram, the code is shown as 3 boxes
Button 1 Code,
Button 2 Code,
Button 3 Code.
If you look at the diagram with the classes you write, then you'll get a better idea of where this code is in your project. One of those green rectangles labeled with "Callback" is executed. Once you are finished doing whatever it is you need to do when the button is clicked, your function returns. When your function returned, the "GUI Dispatcher" takes over running again, waiting around for the next thing to happen.
"Callbacks" Can Be Confusing
Some programmers have no problem with their code being strewn all over the place. They're able to keep it in their heads, write their code in a specific way, organize their files in a way that adds some order to the chaos. This kind of a architecture is sometimes called "Event Driven".
The requirement to write classes for these various widgets or the application and placing callbacks inside them is one of the reasons beginners struggle to get over that GUI bar. Writing Python classes is not generally covered until the end of a course. It's not beginner stuff.
Widgets Manipulating Widgets
If classes and callbacks don't explode your head, let's toss in one more challenge. What if your button wants to do something to your slider? Maybe your button disables your slider. Now your button class needs to know something about your slider class.
The PySimpleGUI Message Passing Architecture (Pull Model)
Before going through the PySimpleGUI architecture, let's look at one of Python's Standard Library constructs, the Queue. With a Python Queue, some message "arrives" and when it does, it's added to the end of the queue.
In the User's Code, the queue is "read", by calling the queue's
get() method. The code would look something like this.
Don't worry about there being nothing added to the queue. The point is that the user asks for an item from the queue. The user calls
get() and if there's an item in the queue, the item is returned.
You can think of the PySimpleGUI interface in exactly the same way. Your user code asks for an event. You are "pulling" items to processing out of the PySimpleGUI library. Contrast this to the "Callback" architecture where your code is directly called when an event happens.
In Computer Science, this type of interface is sometimes call "Message Passing". Instead of your code being directly invoked (called) you get a message passed to you by asking for one.
Asking For Events
Recall in the previous section that the code to get an event in PySimpleGUI is:
Your call to
window.read() is asking PySimpleGUI for the next event for you to process.
If you are going to keep reading events until the window is closed, then you put your
window.read() call into a while loop:
This loop in PySimpleGUI is called the "Event Loop". The benefit to structuring code like this is that your processing of the events can be done linearly. Instead of small functions being scatters throughout your project, you've got your code that handles the events all in 1 place, and in a nice straight linear piece of code. For most people, this linear format is an easier way to visualize the problem than the distributed callback model.