One of the challenges of GUI programming is keeping track of which things happen while the GUI is being built and which things happen later in response to user events.
For example, when you are setting up a callback, it is a common error to call the function rather than passing a reference to it:
def the_callback(): print 'Called.' g.bu(text='This is wrong!', command=the_callback())
If you run this code, you will see that it calls the_callback immediately, and then creates the button. When you press the button, it does nothing because the return value from the_callback is None. Usually you do not want to invoke a callback while you are setting up the GUI; it should only be invoked later in response to a user event.
Another challenge of GUI programming is that you don’t have control of the flow of execution. Which parts of the program execute and their order are determined by user actions. That means that you have to design your program to work correctly for any possible sequence of events.
For example, the GUI in Exercise 19.3 in More widgets has two widgets: one creates a Circle item and the other changes the color of the Circle. If the user creates the circle and then changes its color, there’s no problem. But what if the user changes the color of a circle that doesn’t exist yet? Or creates more than one circle?
As the number of widgets grows, it is increasingly difficult to imagine all possible sequences of events. One way to manage this complexity is to encapsulate the state of the system in an object and then consider:
- What are the possible states? In the Circle example, we might consider two states: before and after the user creates the first circle.
- In each state, what events can occur? In the example, the user can press either of the buttons, or quit.
- For each state-event pair, what is the desired outcome? Since there are two states and two buttons, there are four state-event pairs to consider.
- What can cause a transition from one state to another? In this case, there is a transition when the user creates the first circle.
You might also find it useful to define, and check, invariants that should hold regardless of the sequence of events.
This approach to GUI programming can help you write correct code without taking the time to test every possible sequence of user events!