11.6. Reloading Callback Handlers Dynamically
Our next GUI-programming technique is all about changing a GUI while it is runningthe ultimate in customization. The Python reload function lets you dynamically change and reload a program's modules without stopping the program. For instance, you can bring up a text editor window to change the source code of selected parts of a system while it is running and see those changes show up immediately after reloading the changed module.
This is a powerful feature, especially for developing programs that take a long time to restart. Programs that connect to databases or network servers, initialize large objects, or travel through a long series of steps to retrigger a callback are prime candidates for reload. It can shave substantial time from the development cycle.
The catch for GUIs, though, is that because callback handlers are registered as object references rather than module and object names, reloads of callback handler functions are ineffective after the callback has been registered. The Python reload operation works by changing a module object's contents in place. Because Tkinter stores a pointer to the registered handler object directly, though, it is oblivious to any reloads of the module that the handler came from. That is, Tkinter will still reference a module's old objects even after the module is reloaded and changed.
This is a subtle thing, but you really only need to remember that you must do something special to reload callback handler functions dynamically. Not only do you need to explicitly request reloading of the modules that you change, but you must also generally provide an indirection layer that routes callbacks from registered objects to modules so that reloads have impact.
For example, the script in Example 11-11 goes the extra mile to indirectly dispatch callbacks to functions in an explicitly reloaded module. The callback handlers registered with Tkinter are method objects that do nothing but reload and dispatch again. Because the true callback handler functions are fetched through a module object, reloading that module makes the latest versions of the functions accessible.
Example 11-11. PP3E\Gui\Tools\Reload\rad.py
When run, this script makes a two-button window that triggers the message1 and message2 methods. Example 11-12 contains the actual callback handler code. Its functions receive a self argument that gives access back to the Hello class object, as though these were real methods. You can change this file any number of times while the rad script's GUI is active; each time you do so, you'll change the behavior of the GUI when a button press occurs.
Example 11-12. PP3E\Gui\Tools\Reload\actions.py
Try running rad and editing the messages printed by actions in another window; you should see your new messages printed in the stdout console window each time the GUI's buttons are pressed. This example is deliberately simple to illustrate the concept, but the actions reloaded like this in practice might build pop-up dialogs, new top-level windows, and so on. Reloading the code that creates such windows would also let us dynamically change their appearances.
There are other ways to change a GUI while it's running. For instance, we saw in Chapter 10 that appearances can be altered at any time by calling the widget config method, and widgets can be added and deleted from a display dynamically with methods such as pack_forget and pack (and their grid manager relatives). Furthermore, passing a new command=action option setting to a widget's config method might reset a callback handler to a new action object on the fly; with enough support code, this may be a viable alternative to the indirection scheme used earlier to make reloads more effective in GUIs.