Rework

Rework

I have tried to facilitate rework in PAGE. That is, how do you use PAGE to tweak the GUI without loosing the supporting code that you have written? At the same time I want to allow one to view the changes with the run command button in the Python console.

Here is the situation. You design a GUI and generate the Python code using PAGE, which creates an executable skeleton Python program. You fleshed out the skeleton program to support the GUI and then decided to modify the GUI. If then you again generate the Python code for the GUI, you do not want to loose the application code which you have written to realize the application. This is the rework problem: How to keep your hard fought code when reworking the GUI?

My approach is to create the generated GUI code in one Python module, called the GUI module and the supporting code in a separate Python support module. That is, PAGE generates two Python modules, one for the GUI and one for the supporting code complete with the necessary linkage.

PAGE implements this schema as follows. PAGE works with three files:

  • “<name>.tcl” which is the design description of the GUI suitable for input to PAGE. It can either be created in PAGE, passed to PAGE as a parameter, or opened from the File submenu of the main menu.

  • “<name>.py” which is the main python module generated by PAGE which contains the automatically code to create the GUI on the screen. It is meant to be completely generated by PAGE. This will be referred to as the GUI module.

  • “<name>_support.py” which contains the code supporting the GUI module. PAGE creates a skeleton of this module that contains all of the necessary boiler plate code for linkage to the GUI module as well as the definitions of the Tkinter variables and skeleton versions of callback routines. I refer to this as the support module.

In the above, “<name>” is the application name (it was called the project name in the original Visual Tcl program) selected by the user when the system saves the “.tcl” or extracted from the file name which may be supplied as a parameter when invoking PAGE.

It is expected that the user will generate a new version of the GUI module whenever he modifies the GUI but will need PAGE to modify the support module only when new toplevel windows or callback functions are defined. The first will be much more frequent than the latter. PAGE includes a mechanism for automatically updating the support module. This mechanism will be discussed in the next section.

In earlier versions of PAGE, it was only necessary to add new skeletal callback functions and new Tkinter variables. I adopted the rule that the update will never remove code from the support module. PAGE version 7 with the support of multiple toplevel windows, updating is more complicated. First, the Tkinter variables are no longer defined or created in the support module. Further, changes to the GUI may add or delete toplevel windows. These cases are discussed below.

Let me emphasize two points:

  • You should never modify the GUI module because modifying the GUI design and regenerating the GUI code will loose the modifications.

  • Let me repeat: You should only generate the support module once for an application; otherwise any code which you manually added will be lost.

Automatically Updating the Support Module

This facility was introduced in version 4.3. The basic idea is that reworking the GUI design may introduce new callback functions, Tkinter variables, or custom widgets which need to incorporated into the support module. PAGE can now add those entities to the support module. That is, new skeletal functions are added and new entries are added to the set_Tk_var function, if necessary the set_Tk_var function is added. Similarly, new rename statements for added custom widgets are added. All this while leaving the rest of the existing support module unchanged. It was the case that only additions were required and so updating never involved removing code from the support module.

If Control-U was selected to generate the support module, then the user will be given the options of:

  • generating the support module anew,

  • using an existing support module, thereby utilizing the existing handwritten code; the old support module will be displayed.

  • updating an existing support module to preserve the existing code and adding new Tk Variables or skeleton callback functions.

To rephrase the above, when the user selects Gen_Python->Generate Support Module from the main menu or selects Control-U, PAGE will analyze an existing support module and compare the callback functions and Tkinter variables in it with those utilized in the reworked design. If new entities are required the user will be given the option of automatically adding them. Existing code is not removed or modified. It is up to the user to remove or otherwise deal with code no longer needed.

The first step in this process is to analyze the existing support module to determine the required new code. Next,the new code is added to the existing support module. Following inspection in the Python console, the user may elect to save the updated module.

There can be an issue with tab characters. In step 2, tab characters are not expanded while the added code will use spaces for indentation and that could cause syntax errors through misalignment. You should remove any tabs in support modules before attempting updates.

With version 7, the automatic update feature has been expanded to handle the adding of toplevel windows, and the changing of toplevel class names which are aliases of the toplevel widget. If a toplevel is added then create statements are added to the function “main”. If a toplevel is deleted, then the automatic update feature will comment out the create statements. Finally, changing the alias of a toplevel window will cause one create statement to be commented out and replaced by another.

If the user adds a toplevel window with the alias “Login” then the following code will be added to the function “main” in the support module.

# Creates a toplevel widget.
global _top2, _w2
_top2 = tk.Toplevel(root)
_w2 = unknown.Login(_top2)

If the user changes the alias of a toplevel window from “Toplevel2” to “Second”, them the following create statements.

global _top2, _w2
_top2 = tk.Toplevel(root)
_w2 = unknown.Toplevel2(_top2)

might be replaced with

global _top2, _w2
_top2 = tk.Toplevel(root)
#_w2 = unknown.Toplevel2(_top2)
_w2 = unknown.Second(_top2)

In the case where the user deletes a toplevel window the update process does nothing. This is because the analysis of the support module cannot determine all references to a toplevel widget. It is left to the user to remove them. Hopefully deleting a toplevel window is a are occurrence.

To mitigate cases where the update process damages an existing support module, PAGE keeps several backups.

Reuse

By reuse, I mean the using of widget definitions used and defined in previous PAGE projects. The two aspects of reuse are:

  • Using existing projects as templates.

  • Borrowing widget definitions from existing projects.

Templates

Basically, one can use an existing project as a template for a new project. The file submenu of the main menu has two variations for loading existing projects - Open as Project, and Open as Template. The first, Open as Project, is the one to use when you wish to continue work on an existing project because the opening reestablishes the default color scheme and default fonts as they existed when the project was last stored. That means that if the project will look the same as when last viewed. It also means that if someone gives you a project files and you open it will appear to you as it appeared to him. Of course, you must have the same fonts as he used in the project to preserve the appearance.

Opening a project file as a template abandons the default colors and fonts in favor of the preferences in the profile or rc file.

Very simple then, just load the project you want to use as a template and do a “Save As…” from the main menu providing the name of the new project. Of course, you will have to copy any images used in the template to the directory of the new project.

Borrowing Widgets from Existing Projects

With borrowing one retrieves and opens an existing project, which I will refer as the Lender GUI or Brorrowee, at the same time as the current project is open and then may copy-and-paste elements from that preexisting GUI onto the current GUI. For instance one might have a frame with and array of buttons in one GUI and would like to replicate it in another project.

Since PAGE version 7 supports multiple toplevel widgets, one can also copy-and-paste entire toplevel widgets from the Lender GUI to the current GUI. For instance, one can borrow a toplevel window designed for logging in from a Lender GUI. Another example would be borrowing a toplevel widget containing a progress bar.

The Lender GUI is opened via File->Borrow menu entry in the Main menu. When it is opened, the background for each toplevel widget is set to Plum so that the user will recognize it as a Lender GUI. I chose Plum because I doubt that many users will choose Plum as a toplevel background color. Also its position is offset from the source for visibility. It is possible even likely that a toplevel window of the Lender GUI will overlap or obscure the current GUI under construction; in that case just move it to the side to expose the current GUI.

The Lender GUI is read only in the sense that the save functions will ignore it, leaving it unchanged on disk. So don’t worry about changes you might make to the Lender GUI such as moving a toplevel window. By the same token you cannot make changes to the Lender GUI that you would like to save.

This feature required a minor change in the format of the project tcl file. That means that a project file created before version 4.10 will need to be opened and saved before it can be a lender. Remember to use “Save as” instead of “Save” which only saves if the main GUI has been changed.

A peculiar side effect of borrowing is that if the Lender GUI happens to have context (popup) menus, they will be incorporated in the main GUI. That is because every bit of documentation that I could find on context menus used root as the parent rather than a Toplevel window; so I did too. This may not be too bad because if you don’t bind to them they will never be invoked. Further, if you borrow a widget which is bound to a popup you will likely want to pull over the menu as well as the widget. If not wanted context widgets are easily deleted from the Widget Tree. Comments solicited.

The Menu Bar from the Lender GUI may be copied and pasted into the current GUI. To do that

  • Select the Menu Bar entry in the Widget Tree with Button-3 and then the Copy entry of the popupmenu, select the toplevel window with Button-3.

  • Select the Copy entry in the popup window.

  • Select the Toplevel GUI window with Button-3.

  • Select the Paste entry in the popup window.

  • Press Button-1 anywhere in the destination toplevel GUI window.

If any of the borrowed widgets have images, then the image files must be copied to the new project directory before the paste operation and they must be in the same relative position in the new project directory. That is, if the image path was, for instance “./images/image.png” then the image path in the new directory should also be “./images/image.png”. If the image files are not in place before the paste operation, the past operation will fail. In many cases PAGE will volunteer to do that for you.

When finished with a Lender GUI, one may close the Lender GUI with the Close Borrowed button in the Widget Tree. If one does a save while a Lender GUI is open, the Lender GUI is ignored; it is not saved.

Care must be exercised in borrowing widgets with images. After the borrow is completed, any image reference must be updated to refer to images in the destination project directory.

Borrowing is an unusual function, so please report any problems encountered.