.. _mod_struct:
								   
Module Structure
================

PAGE generates two modules, The GUI module, and the Support
Module. The first contains all of the Python code necessary to plop
the GUI window onto the computer screen. In my vision of the PAGE
world, the GUI module is not to be edited. It contains all of the
required linkage to the Support module.  It is generally the main
module of the application.

The Support module is generated in skeletal form and is the framework
upon which the application is built. It is where the user written code
resides.  As such it is infrequently generated or replaced by PAGE. In
fact, the separation is the secret of rework; it allows changes
to the content and appearance of the GUI window while preserving the 
user's code. 

When the modules are generated they are displayed in the Python
Console but not saved automatically. This gives the user an
opportunity to inspect the code and decide whether to save. When PAGE
generates a module, it uses four spaces for indentation. When PAGE
saves a module, spaces are saved as spaces.

Project Directory
~~~~~~~~~~~~~~~~~

The directory containing the various files associated with PAGE is
called the project directory. It contains the project file, the
generate Python modules, any image files used with widgets, and any
application support modules. It should also be the current working
directory. 

Let me call the project directory "proj_dir", because it is precise
and corresponds to an internal PAGE variable.

.. proj_dir is set several
.. ways:
.. 
.. + If PAGE is executed without an arguments, proj_dir is set to the
..   current working directory.
.. 
.. + If PAGE is called with an argument, proj_dir is set to the current
..   working directory joined to the directory portion of the argument.
.. 
.. + If the GUI design created with PAGE is saved, the destination
..   directory becomes the proj_dir.
.. 
.. + If a project file is opened, the source directory becomes the
..   proj_dir.

This is important when widgets have image attributes. To keep such
cases reasonable sane, PAGE requires that all image files associated
with such widgets reside within the proj_dir or a subdirectory of it
prior to the specification of the widget. The generated code for
images contain file names and file locations. The file location are
expressed as relative to the current working directory.

I recommend that before executing PAGE, a directory be created and
that directory be made the current working directory. Image files need
not be included before beginning the GUI development, but need to
included before widgets specify non-blank image attributes. Again, you
can add a widget that might include an image but the image file must
be included before specified the image attribute.

GUI Module
~~~~~~~~~~

The main features of the GUI module are the class definitions, which
defines a GUI windows.  It defines the top level windows and all of the
contained widgets.  Note that it refers to all callback functions as
functions in the support module and that Tk variables such as
textvariables are referred to Tk variables in the support module.

A particularly simple GUI module, example.py, which has a popup menu
and two toplevel windows follows:

.. sourcecode:: python

    1 #! /usr/bin/env python
    2 #  -*- coding: utf-8 -*-
    3 #
    4 # GUI module generated by PAGE version 7.5d
    5 #  in conjunction with Tcl version 8.6
    6 #    May 26, 2022 07:05:58 AM PDT  platform: Linux
    7 
    8 import sys
    9 import tkinter as tk
   10 import tkinter.ttk as ttk
   11 from tkinter.constants import *
   12 import os.path
   13 
   14 _script = sys.argv[0]
   15 _location = os.path.dirname(_script)
   16 
   17 import b_support
   18 
   19 _bgcolor = 'wheat'  # X11 color: #f5deb3
   20 _fgcolor = '#000000'  # X11 color: 'black'
   21 _compcolor = '#b2c9f4' # Closest X11 color: 'SlateGray2'
   22 _ana1color = '#eaf4b2' # Closest X11 color: '{pale goldenrod}'
   23 _ana2color = 'beige' # X11 color: #f5f5dc
   24 _tabfg1 = 'black' 
   25 _tabfg2 = 'black' 
   26 _tabbg1 = 'grey75' 
   27 _tabbg2 = 'grey89' 
   28 _bgmode = 'light' 
   29 font12 = "-family {DejaVu Sans} -size 12"
   30 font15 = "-family {Nimbus Sans L} -size 14"
   31 
   32 _style_code_ran = 0
   33 def _style_code():
   34     global _style_code_ran
   35     if _style_code_ran:
   36        return
   37     style = ttk.Style()
   38     if sys.platform == "win32":
   39        style.theme_use('winnative')
   40     style.configure('.',background=_bgcolor)
   41     style.configure('.',foreground=_fgcolor)
   42     style.configure('.',font='-family {DejaVu Sans} -size 12')
   43     style.map('.',background =
   44        [('selected', _compcolor), ('active',_ana2color)])
   45     if _bgmode == 'black':
   46        style.map('.',foreground =
   47          [('selected', 'white'), ('active','white')])
   48     else:
   49        style.map('.',foreground =
   50          [('selected', 'black'), ('active','black')])
   51     style.map('TNotebook.Tab', background =
   52             [('selected', _bgcolor), ('active', _tabbg1),
   53             ('!active', _tabbg2)], foreground =
   54             [('selected', _fgcolor), ('active', _tabfg1), ('!active',  _tabfg2)])
   55 
   56     global _images
   57     _images = (
   58          tk.PhotoImage("img_close", data='''R0lGODlhDAAMAIQUADIyMjc3Nzk5OT09PT
   59                  8/P0JCQkVFRU1NTU5OTlFRUVZWVmBgYGF hYWlpaXt7e6CgoLm5ucLCwszMzNbW
   60                  1v//////////////////////////////////// ///////////yH5BAEKAB8ALA
   61                  AAAAAMAAwAAAUt4CeOZGmaA5mSyQCIwhCUSwEIxHHW+ fkxBgPiBDwshCWHQfc5
   62                   KkoNUtRHpYYAADs= '''),
   63          tk.PhotoImage("img_close_white", data='''R0lGODlhDAAMAPQfAM3NzcjI
   64                 yMbGxsLCwsDAwL29vbq6urKysrGxsa6urqmpqZ+fn56enpaWloSEhF9fX0ZGR
   65                 j09PTMzMykpKQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///yH
   66                 5BAEKAB8ALAAAAAAMAAwAAAUt4CeOZGmaA5mSyQCIwhCUSwEIxHHW+fkxBgPi
   67                 BDwshCWHQfc5KkoNUtRHpYYAADs='''),
   68          tk.PhotoImage("img_closeactive", data='''R0lGODlhDAAMAIQcALwuEtIzFL46
   69                  INY0Fdk2FsQ8IdhAI9pAIttCJNlKLtpLL9pMMMNTP cVTPdpZQOBbQd60rN+1rf
   70                  Czp+zLxPbMxPLX0vHY0/fY0/rm4vvx8Pvy8fzy8P//////// ///////yH5BAEK
   71                  AB8ALAAAAAAMAAwAAAVHYLQQZEkukWKuxEgg1EPCcilx24NcHGYWFhx P0zANBE
   72                  GOhhFYGSocTsax2imDOdNtiez9JszjpEg4EAaA5jlNUEASLFICEgIAOw== '''),
   73          tk.PhotoImage("img_closepressed", data='''R0lGODlhDAAMAIQeAJ8nD64qELE
   74                  rELMsEqIyG6cyG7U1HLY2HrY3HrhBKrlCK6pGM7lD LKtHM7pKNL5MNtiViNaon
   75                   +GqoNSyq9WzrNyyqtuzq+O0que/t+bIwubJw+vJw+vTz+zT z////////yH5BAE
   76                  KAB8ALAAAAAAMAAwAAAVJIMUMZEkylGKuwzgc0kPCcgl123NcHWYW Fs6Gp2mYB
   77                  IRgR7MIrAwVDifjWO2WwZzpxkxyfKVCpImMGAeIgQDgVLMHikmCRUpMQgA7 ''')
   78         )
   79     if _bgmode == "dark":
   80         style.element_create("close", "image", "img_close_white",
   81            ('active', 'pressed',  'img_closepressed'),
   82            ('active', 'alternate', 'img_closeactive'), border=8, sticky='')
   83     else:
   84         style.element_create("close", "image", "img_close",
   85            ('active', 'pressed',  'img_closepressed'),
   86            ('active', 'alternate', 'img_closeactive'), border=8, sticky='')
   87 
   88     style.layout("ClosetabNotebook", [("ClosetabNotebook.client",
   89                                  {"sticky": "nswe"})])
   90     style.layout("ClosetabNotebook.Tab", [
   91         ("ClosetabNotebook.tab",
   92           { "sticky": "nswe",
   93             "children": [
   94                 ("ClosetabNotebook.padding", {
   95                     "side": "top",
   96                     "sticky": "nswe",
   97                     "children": [
   98                         ("ClosetabNotebook.focus", {
   99                             "side": "top",
  100                             "sticky": "nswe",
  101                             "children": [
  102                                 ("ClosetabNotebook.label", {"side":
  103                                   "left", "sticky": ''}),
  104                                 ("ClosetabNotebook.close", {"side":
  105                                     "left", "sticky": ''}),]})]})]})])
  106 
  107     style.map('ClosetabNotebook.Tab', background =
  108         [('selected', _bgcolor), ('active', _tabbg1),
  109         ('!active', _tabbg2)], foreground =
  110         [('selected', _fgcolor), ('active', _tabfg1), ('!active', _tabfg2)])
  111     _style_code_ran = 1
  112 
  113 def popup1(event, *args, **kwargs):
  114         font15 = "-family {Nimbus Sans L} -size 14"
  115         Popupmenu1 = tk.Menu(b_support.root, tearoff=0)
  116         Popupmenu1.configure(activebackground="beige")
  117         Popupmenu1.configure(disabledforeground="#b8a786")
  118         Popupmenu1.configure(font="-family {Nimbus Sans L} -size 14")
  119         sub_menu = tk.Menu(Popupmenu1,
  120                 activebackground="beige",
  121                 disabledforeground="#b8a786",
  122                 font="-family {Nimbus Sans L} -size 14",
  123                 tearoff=0)
  124         Popupmenu1.add_cascade(menu=sub_menu,
  125                 compound="left",
  126                 label="File")
  127         sub_menu.add_command(
  128                 command=b_support.quit,
  129                 compound="left",
  130                 label="Quit")
  131         Popupmenu1.post(event.x_root, event.y_root)
  132 
  133 class Toplevel1:
  134     def __init__(self, top=None):
  135         '''This class configures and populates the toplevel window.
  136            top is the toplevel containing window.'''
  137 
  138         top.geometry("758x734+660+210")
  139         top.minsize(1, 1)
  140         top.maxsize(1905, 1050)
  141         top.resizable(0,  0)
  142         top.title("Toplevel 0")
  143         top.configure(background="wheat")
  144         top.configure(highlightbackground="wheat")
  145         top.configure(highlightcolor="black")
  146 
  147         self.top = top
  148 
  149         self.Button1 = tk.Button(self.top)
  150         self.Button1.place(x=190, y=200, height=48, width=126)
  151         self.Button1.configure(activebackground="beige")
  152         self.Button1.configure(background="wheat")
  153         self.Button1.configure(borderwidth="2")
  154         self.Button1.configure(compound='left')
  155         self.Button1.configure(disabledforeground="#b8a786")
  156         self.Button1.configure(font="-family {DejaVu Sans} -size 12")
  157         self.Button1.configure(highlightbackground="wheat")
  158         photo_location = os.path.join(_location,"./stop.png")
  159         global _img0
  160         _img0 = tk.PhotoImage(file=photo_location)
  161         self.Button1.configure(image=_img0)
  162         self.Button1.configure(text='''Button''')
  163 
  164         self.Button2 = tk.Button(self.top)
  165         self.Button2.place(x=310, y=110, height=35, width=83)
  166         self.Button2.configure(activebackground="beige")
  167         self.Button2.configure(background="wheat")
  168         self.Button2.configure(borderwidth="2")
  169         self.Button2.configure(compound='left')
  170         self.Button2.configure(disabledforeground="#b8a786")
  171         self.Button2.configure(font="-family {DejaVu Sans} -size 12")
  172         self.Button2.configure(highlightbackground="wheat")
  173         self.Button2.configure(text='''Button''')
  174 
  175         self.Button3 = tk.Button(self.top)
  176         self.Button3.place(x=280, y=310, height=35, width=83)
  177         self.Button3.configure(activebackground="beige")
  178         self.Button3.configure(background="wheat")
  179         self.Button3.configure(borderwidth="2")
  180         self.Button3.configure(compound='left')
  181         self.Button3.configure(disabledforeground="#b8a786")
  182         self.Button3.configure(font="-family {DejaVu Sans} -size 12")
  183         self.Button3.configure(highlightbackground="wheat")
  184         self.Button3.configure(text='''Button''')
  185 
  186         self.menubar = tk.Menu(top, font="-family {Nimbus Sans L} -size 14"
  187                 ,bg='#d9d9d9', fg=_fgcolor)
  188         top.configure(menu = self.menubar)
  189 
  190         self.sub_menu = tk.Menu(top,
  191                 activebackground="beige",
  192                 disabledforeground="#b8a786",
  193                 font="-family {Nimbus Sans L} -size 14",
  194                 tearoff=0)
  195         self.menubar.add_cascade(menu=self.sub_menu,
  196                 label="NewCascade")
  197         photo_location = os.path.join(_location,"./stop.png")
  198         global _img1
  199         _img1 = tk.PhotoImage(file=photo_location)
  200         self.sub_menu.add_command(
  201                 image=_img1,
  202                 label="NewCommand")
  203 
  204         _style_code()
  205         self.TNotebook1 = ttk.Notebook(self.top)
  206         self.TNotebook1.place(x=430, y=180, height=228, width=302)
  207         self.TNotebook1.configure(takefocus="")
  208         self.TNotebook1_t1 = tk.Frame(self.TNotebook1)
  209         self.TNotebook1.add(self.TNotebook1_t1, padding=3)
  210         self.TNotebook1.tab(0, text='''Page 1''', compound="left"
  211                 ,underline='''-1''', )
  212         self.TNotebook1_t1.configure(background="wheat")
  213         self.TNotebook1_t1.configure(highlightbackground="wheat")
  214         self.TNotebook1_t2 = tk.Frame(self.TNotebook1)
  215         self.TNotebook1.add(self.TNotebook1_t2, padding=3)
  216         self.TNotebook1.tab(1, text='''Page 2''', compound="left"
  217                 ,underline='''-1''', )
  218         self.TNotebook1_t2.configure(background="wheat")
  219         self.TNotebook1_t2.configure(highlightbackground="wheat")
  220 
  221         PNOTEBOOK="ClosetabNotebook"
  222         self.PNotebook1 = ttk.Notebook(self.top)
  223         self.PNotebook1.place(x=430, y=460, height=228, width=302)
  224         self.PNotebook1.configure(style=PNOTEBOOK)
  225         self.PNotebook1_t1 = tk.Frame(self.PNotebook1)
  226         self.PNotebook1.add(self.PNotebook1_t1, padding=3)
  227         self.PNotebook1.tab(0, text='''Page 1''', compound="left"
  228                 ,underline='''-1''', )
  229         self.PNotebook1_t1.configure(background="wheat")
  230         self.PNotebook1_t1.configure(highlightbackground="wheat")
  231         self.PNotebook1_t2 = tk.Frame(self.PNotebook1)
  232         self.PNotebook1.add(self.PNotebook1_t2, padding=3)
  233         self.PNotebook1.tab(1, text='''Page 2''', compound="left"
  234                 ,underline='''-1''', )
  235         self.PNotebook1_t2.configure(background="wheat")
  236         self.PNotebook1_t2.configure(highlightbackground="wheat")
  237         self.PNotebook1.bind('<Button-1>',_button_press)
  238         self.PNotebook1.bind('<ButtonRelease-1>',_button_release)
  239         self.PNotebook1.bind('<Motion>',_mouse_over)
  240 
  241 # The following code is add to handle mouse events with the close icons
  242 # in PNotebooks widgets.
  243 def _button_press(event):
  244     widget = event.widget
  245     element = widget.identify(event.x, event.y)
  246     if "close" in element:
  247         index = widget.index("@%d,%d" % (event.x, event.y))
  248         widget.state(['pressed'])
  249         widget._active = index
  250 
  251 def _button_release(event):
  252     widget = event.widget
  253     if not widget.instate(['pressed']):
  254             return
  255     element = widget.identify(event.x, event.y)
  256     try:
  257         index = widget.index("@%d,%d" % (event.x, event.y))
  258     except tk.TclError:
  259         pass
  260     if "close" in element and widget._active == index:
  261         widget.forget(index)
  262         widget.event_generate("<<NotebookTabClosed>>")
  263 
  264     widget.state(['!pressed'])
  265     widget._active = None
  266 
  267 def _mouse_over(event):
  268     widget = event.widget
  269     element = widget.identify(event.x, event.y)
  270     if "close" in element:
  271         widget.state(['alternate'])
  272     else:
  273         widget.state(['!alternate'])
  274 
  275 def start_up():
  276     b_support.main()
  277 
  278 if __name__ == '__main__':
  279     b_support.main()
  280 




			

Of course, this is a very simple example and does not show all the
possibilities.   

Lines 1 through 12 are imports needed to execute Python. There are
imports for  Python 3 but no longer are imports provided for Python 2.

.. sourcecode:: python

    1 #! /usr/bin/env python
    2 #  -*- coding: utf-8 -*-
    3 #
    4 # GUI module generated by PAGE version 7l
    5 #  in conjunction with Tcl version 8.6
    6 #    Jul 24, 2021 07:23:57 PM PDT  platform: Linux
    7 
    8 import sys
    9 import tkinter as tk
   10 import tkinter.ttk as ttk
   11 from tkinter.constants import *   
   12 import os.path

Lines 14 and 15 calculate the location of the program in the users
file system.

.. sourcecode:: python

   14 _script = sys.argv[0]
   15 _location = os.path.dirname(_script)
				
_location is used for the creation of tkinter images used
in the GUI module.  This was added to allow the application to run
from within an arbitrary directory. It is available to the support
module for the creation of tkinter images.

Line 17 is the linkage to the support module, m_support.exe.

.. sourcecode:: python

   17 import m_support				
				
Lines 19 through 30 defines various colors and fonts that may be used
in the class definitions.

.. sourcecode:: python

   15 _bgcolor = 'wheat'  # X11 color: #f5deb3
   16 _fgcolor = '#000000'  # X11 color: 'black'
   17 _compcolor = '#b2c9f4' # Closest X11 color: 'SlateGray2'
   18 _ana1color = '#eaf4b2' # Closest X11 color: '{pale goldenrod}'
   19 _ana2color = 'beige' # X11 color: #f5f5dc
   20 _tabfg1 = 'black' 
   21 _tabfg2 = 'black' 
   22 _tabbg1 = 'grey75' 
   23 _tabbg2 = 'grey89' 
   24 _bgmode = 'light' 
   25 font12 = "-family {DejaVu Sans} -size 12"


Lines 32 through 111 define the function "_style_code" which is
executed exactly once to define ttk styles as required for the ttk
widgets used in the class definitions. It is a function because the
code can only be executed after tkinter is initiated. The content of
"_style_code" depends depends on the repertory of ttk widgets actually
used in the GUI. In this case, considerable code is required to
satisfy the needs of the PNotebook widget used in this example.

   
.. sourcecode:: python				



   32 _style_code_ran = 0
   33 def _style_code():
   34     global _style_code_ran
   35     if _style_code_ran:
   36        return
   37     style = ttk.Style()
   38     if sys.platform == "win32":
   39        style.theme_use('winnative')
   40     style.configure('.',background=_bgcolor)
   41     style.configure('.',foreground=_fgcolor)
   42     style.configure('.',font='-family {DejaVu Sans} -size 12')
   43     style.map('.',background =
   44        [('selected', _compcolor), ('active',_ana2color)])
   45     if _bgmode == 'black':
   46        style.map('.',foreground =
   47          [('selected', 'white'), ('active','white')])
   48     else:
   49        style.map('.',foreground =
   50          [('selected', 'black'), ('active','black')])
   51     style.map('TNotebook.Tab', background =
   52             [('selected', _bgcolor), ('active', _tabbg1),
   53             ('!active', _tabbg2)], foreground =
   54             [('selected', _fgcolor), ('active', _tabfg1), ('!active',  _tabfg2)])
   55 
   56     global _images
   57     _images = (
   58          tk.PhotoImage("img_close", data='''R0lGODlhDAAMAIQUADIyMjc3Nzk5OT09PT
   59                  8/P0JCQkVFRU1NTU5OTlFRUVZWVmBgYGF hYWlpaXt7e6CgoLm5ucLCwszMzNbW
   60                  1v//////////////////////////////////// ///////////yH5BAEKAB8ALA
   61                  AAAAAMAAwAAAUt4CeOZGmaA5mSyQCIwhCUSwEIxHHW+ fkxBgPiBDwshCWHQfc5
   62                   KkoNUtRHpYYAADs= '''),
   63          tk.PhotoImage("img_close_white", data='''R0lGODlhDAAMAPQfAM3NzcjI
   64                 yMbGxsLCwsDAwL29vbq6urKysrGxsa6urqmpqZ+fn56enpaWloSEhF9fX0ZGR
   65                 j09PTMzMykpKQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///yH
   66                 5BAEKAB8ALAAAAAAMAAwAAAUt4CeOZGmaA5mSyQCIwhCUSwEIxHHW+fkxBgPi
   67                 BDwshCWHQfc5KkoNUtRHpYYAADs='''),
   68          tk.PhotoImage("img_closeactive", data='''R0lGODlhDAAMAIQcALwuEtIzFL46
   69                  INY0Fdk2FsQ8IdhAI9pAIttCJNlKLtpLL9pMMMNTP cVTPdpZQOBbQd60rN+1rf
   70                  Czp+zLxPbMxPLX0vHY0/fY0/rm4vvx8Pvy8fzy8P//////// ///////yH5BAEK
   71                  AB8ALAAAAAAMAAwAAAVHYLQQZEkukWKuxEgg1EPCcilx24NcHGYWFhx P0zANBE
   72                  GOhhFYGSocTsax2imDOdNtiez9JszjpEg4EAaA5jlNUEASLFICEgIAOw== '''),
   73          tk.PhotoImage("img_closepressed", data='''R0lGODlhDAAMAIQeAJ8nD64qELE
   74                  rELMsEqIyG6cyG7U1HLY2HrY3HrhBKrlCK6pGM7lD LKtHM7pKNL5MNtiViNaon
   75                   +GqoNSyq9WzrNyyqtuzq+O0que/t+bIwubJw+vJw+vTz+zT z////////yH5BAE
   76                  KAB8ALAAAAAAMAAwAAAVJIMUMZEkylGKuwzgc0kPCcgl123NcHWYW Fs6Gp2mYB
   77                  IRgR7MIrAwVDifjWO2WwZzpxkxyfKVCpImMGAeIgQDgVLMHikmCRUpMQgA7 ''')
   78         )
   79     if _bgmode == "dark":
   80         style.element_create("close", "image", "img_close_white",
   81            ('active', 'pressed',  'img_closepressed'),
   82            ('active', 'alternate', 'img_closeactive'), border=8, sticky='')
   83     else:
   84         style.element_create("close", "image", "img_close",
   85            ('active', 'pressed',  'img_closepressed'),
   86            ('active', 'alternate', 'img_closeactive'), border=8, sticky='')
   87 
   88     style.layout("ClosetabNotebook", [("ClosetabNotebook.client",
   89                                  {"sticky": "nswe"})])
   90     style.layout("ClosetabNotebook.Tab", [
   91         ("ClosetabNotebook.tab",
   92           { "sticky": "nswe",
   93             "children": [
   94                 ("ClosetabNotebook.padding", {
   95                     "side": "top",
   96                     "sticky": "nswe",
   97                     "children": [
   98                         ("ClosetabNotebook.focus", {
   99                             "side": "top",
  100                             "sticky": "nswe",
  101                             "children": [
  102                                 ("ClosetabNotebook.label", {"side":
  103                                   "left", "sticky": ''}),
  104                                 ("ClosetabNotebook.close", {"side":
  105                                     "left", "sticky": ''}),]})]})]})])
  106 
  107     style.map('ClosetabNotebook.Tab', background =
  108         [('selected', _bgcolor), ('active', _tabbg1),
  109         ('!active', _tabbg2)], foreground =
  110         [('selected', _fgcolor), ('active', _tabfg1), ('!active', _tabfg2)])
  111     _style_code_ran = 1

   
Lines 113 through 131 define the popup menu. 

.. sourcecode:: python

  113 def popup1(event, *args, **kwargs):
  114         font15 = "-family {Nimbus Sans L} -size 14"
  115         Popupmenu1 = tk.Menu(b_support.root, tearoff=0)
  116         Popupmenu1.configure(activebackground="beige")
  117         Popupmenu1.configure(disabledforeground="#b8a786")
  118         Popupmenu1.configure(font="-family {Nimbus Sans L} -size 14")
  119         sub_menu = tk.Menu(Popupmenu1,
  120                 activebackground="beige",
  121                 disabledforeground="#b8a786",
  122                 font="-family {Nimbus Sans L} -size 14",
  123                 tearoff=0)
  124         Popupmenu1.add_cascade(menu=sub_menu,
  125                 compound="left",
  126                 label="File")
  127         sub_menu.add_command(
  128                 command=b_support.quit,
  129                 compound="left",
  130                 label="Quit")
  131         Popupmenu1.post(event.x_root, event.y_root)
  132 


Lines 133 through 239 define the toplevel window as the class
Toplevel1.

Lines 241 through 273 is boiler plate code generated for the PNotebook
widget. 

Finally lines 275 through 279 launch the application.

.. sourcecode:: python

  275 def start_up():
  276     b_support.main()
  277 
  278 if __name__ == '__main__':
  279     b_support.main()
  
Support Module
~~~~~~~~~~~~~~

Below is the support module, example_support.py, created by PAGE and
corresponds to the GUI module, example.py, above. It is a skeleton
module for the for the application that you are building.

.. sourcecode:: python

    1 #! /usr/bin/env python
    2 #  -*- coding: utf-8 -*-
    3 #
    4 # Support module generated by PAGE version 7.5d
    5 #  in conjunction with Tcl version 8.6
    6 #    May 26, 2022 07:30:34 AM PDT  platform: Linux
    7 
    8 import sys
    9 import tkinter as tk
   10 import tkinter.ttk as ttk
   11 from tkinter.constants import *
   12 
   13 import b
   14 
   15 def main(*args):
   16     '''Main entry point for the application.'''
   17     global root
   18     root = tk.Tk()
   19     root.protocol( 'WM_DELETE_WINDOW' , root.destroy)
   20     # Creates a toplevel widget.
   21     global _top1, _w1
   22     _top1 = root
   23     _w1 = b.Toplevel1(_top1)
   24     root.mainloop()
   25 
   26 def quit(*args):
   27     print('b_support.quit')
   28     for arg in args:
   29         print ('	another arg:', arg)
   30     sys.stdout.flush()
   31 
   32 if __name__ == '__main__':
   33     b.start_up()
   34 


				



This module will be the home of the user coded portion of the
application.  Obviously, PAGE can only prepare a framework for the
application. What PAGE knows about are, (1) the linkage between the
GUI module and the support module, (2) the callback functions to be
located in the support module,  and (3) the class names of the
toplevel windows.

The purpose of the support module skeleton is to allow the GUI to
execute and demonstrate the generated GUI. Let me discuss the
generated code.

Lines 1 through 11 are imports needed to execute Python.

.. sourcecode:: python

    1 #! /usr/bin/env python
    2 #  -*- coding: utf-8 -*-
    3 #
    4 # Support module generated by PAGE version 7l
    5 #  in conjunction with Tcl version 8.6
    6 #    Jul 24, 2021 07:24:31 PM PDT  platform: Linux
    7 
    8 import sys
    9 import tkinter as tk
   10 import tkinter.ttk as ttk
   11 from tkinter.constants import *

Line 11 was added because the Tk constants, such as END and NW as
frequently used and this import statement makes it a bit easier.

Line 13 imports the class definitions of GUI classes defining the
toplevel windows. In this case b.py, the corresponding GUI file
is imported.

Lines 15 through 28 is the function main which is main entry point for
the application.

.. sourcecode:: python

   15 def main(*args):
   16     '''Main entry point for the application.'''
   17     global root
   18     root = tk.Tk()
   19     root.protocol( 'WM_DELETE_WINDOW' , root.destroy)
   20     # Creates a toplevel widget.
   21     global _top1, _w1
   22     _top1 = root
   23     _w1 = b.Toplevel1(_top1)
   24     root.mainloop()
				
Lines 17 through 20 and line 24 initialize Tkinter. Any application
code required before the GUI is created should placed before line 17.
Line 18 creates the toplevel window which may be thought of as the
root window of the application.


Line 19

.. sourcecode:: python
				 
   19     root.protocol( 'WM_DELETE_WINDOW' , root.destroy)

causes the function named as the second parameter to be called when
the 'x' in the corner of the main window title bar is selected. PAGE
specifies "root.destroy" which terminates Tkinter. You should change
it to any termination or cleanup function your application requires
upon GUI termination.  Alternatively, cleanup code could be placed
after line 24.  Other application windows will not have a similar
protocol statement; hence selecting 'x' in one of those windows merely
destroys that window. Line 19 is present because I did not want the
application user to be able to close all the GUI windows and still
have the program running with no obvious way to stop it.

Lines 18 and 22 through 24 actually creates the root toplevel window
with the first class defined in the GUI module at the same time as
initiating Tkinter. It is treated as the main window of the GUI. As
such the main window cannot be destroyed without terminating
Tkinter. Terminating Tkinter does not terminate the application. That
occurs when main is finished. Therefore, required termination code can
be placed after in main after line

.. sourcecode:: python

   24     root.mainloop()


If you need initialization code after the GUI is created but before the
application user interacts with the GUI, place it before line 24 which
completes the GUI creation and the application execution is then
controlled by the GUI. 
   
   
The generated code for a skeletal callback functions is very simple:

.. sourcecode:: python

   30 def quit(*args):				
   31     print('m_support.quit')
   32     for arg in args:
   33         print ('another arg:', arg)
   34     sys.stdout.flush()

The code generated as above is so generated that the GUI module and
the support module will be an executable pair.  That is, you can
execute the GUI module and see what it will look like even though you
have put in no additional application code. If the GUI invokes a
callback, say by a button select, the program will tell you that it
was invoked and will print out any arguments passed to the
routine. Now you have a leg up, go program.


As a convenience, the following code is added at the bottom of the
support module to aid in debugging.  If you are debugging the support
module and want to test its execution, you can just execute it.  I
found it particularly useful since I do my development in emacs where
I have a key binding which will invoke python to execute the current
buffer. Often that is the support module.

.. sourcecode:: python

   if __name__ == '__main__':
        example.start_up()				


Some of these features are further explored in :ref:`Applications with
Multiple Top-Level Windows`.

.. _update7:

Updating the Support Module
~~~~~~~~~~~~~~~~~~~~~~~~~~~

You have written a substantial body of application code,
and discover that you need an additional widget in the GUI module;
what to do?

First invoke PAGE with the project name, add the widget with all its
configuration including callbacks and Tk variables. Then generate
Python code for the GUI module just like before.  You sure do not want
to rebuild the support module anew and erase all of your hand code.
So when you tell PAGE to generate your support module, it gives you
the option of updating the existing support module. If selected, PAGE
will merely add skeletons for the new callbacks and add the new Tk
variables.  In addition, it will backup the previous version of the
modules in case of failure or PAGE bugs. etc..  PAGE will keep backups
to a depth specified in the preferences.  See :ref:`backup preferences <backup>`.

For version 7, the automatic update function has been expanded to
consider the addition and deletions of toplevel windows except for the
root window, and the changing of the class names of the toplevel
windows.

The update function no longer has anything to do with Tk variables
other than those required for popup (context) menus. There is no
longer a function named "set_Tk_var".

Skeletal callback functions are simply added near the bottom of the support
module. Do with them what you will.

Updating the support module prior to version 7 only added code to the
module. Doing so did not change any existing code. With version 7 and the
capability of adding, deleting, and renaming of toplevel windows it
becomes necessary to change code in the support module when
executing the update function. Remember that a goal of that function is to
allow the support module to execute showing the layout and content of
the GUI windows.

Creation of a new window is easy, updating just adds a little code
near the bottom of main.

Changing the name of the toplevel window class
leads to changing
the code that creates the object to a comment and following it with a
new creation line of code like:

.. sourcecode:: python

   _top3 = tk.Toplevel(root)
   # _w3 = unknown.Toplevel3(_top3)
   _w3 = unknown.Hello(_top3)

In this example the alias of the toplevel widget was changed from
Toplevel3 to Hello. If this change were not made then the module would
not execute because the toplevel class "Toplevel3" no longer
exists. Note this substitution only applies to toplevel creation code
that adheres to exact format of the automatically generated code. If
you manually insert creation code in the support module you will have
to fix it manually.

With deleting toplevel windows, things are a little stickier.  A
general rule I follow is to never remove code from the support module
when applying updates. My first thought was to comment out the
statement in the support module which creates the toplevel
window. However, that statement also creates the variable _w<int>
which may occur throughout the module and I am really unable to deal
which that. There are just too many possibilities to handle. So I have
decided that it is best if I try doing nothing. That is further
justified by the expectation that deleting an existing top level will
be a rare event.  However, if you attempt to execute the application
from a Python console by selecting the run button, you will may get a
window indicating that manual changes to the support module are
needed.

The toplevel window changes above rely on finding and modifying
specific code patterns as generated by PAGE. It is possible that user
code cleverly uses modified code sequences for manipulating toplevel
windows. In such cases, all bets are off. The "clone" example is an
example where a toplevel window is created by a non-standard code
sequence. 

.. _compat7:

Major Compatibility Issues
~~~~~~~~~~~~~~~~~~~~~~~~~~

Version 7 of PAGE is compatible with respect to project files, mostly. This
version does accept and can modify existing project files with only
one problem observed. When the project file contains popup menus, it
is necessary to run PAGE on the project file, select the popup file
from the Widget Tree, and add the name of the popup function to the
"proc" attribute in the Attribute Editor. With a legacy project those
names are likely to be popup1, popup2, etc..

Generated GUI modules are quite different from the old format but that
should not be a problem because they are generated automatically and
should not be changed by the user. The major difference is that the
GUI modules may now contain more than one class definition each of
which defines a separate toplevel widget allowing a simpler way to
have multiple toplevel GUI's. Note also that all of the Tk variables
required by a toplevel widget are defined as instance variables inside
the class statement.

As seen above, there are significant changes to the support module which
will affect the user. Let me elaborate.

First, the support module begins execution in the "main" function of
the support module. This allows the user to call application code
before GUI toplevel windows are created. That is, the application can
now execute initialization code before doing anything with the GUI or
Tkinter. 
A lack of this has been a shortcoming
of earlier versions. Make such calls before Tkinter is initialized
at the line:

.. sourcecode:: python

   root = tk.TK()

Similarly, application code can be executed after Tkinter exits at
the line:

.. sourcecode:: python

	root.mainloop()

Of course, that would require Tkinter be ended with "root.destroy()"
rather than "sys.exit().

Next, you will notice that the support module does not contain a skeletal
init function. In earlier versions of PAGE, execution began in the GUI
module and a call to the init function passed control to user
generated code in the support module as well as reference variables to the
toplevel widget and the class object which populate the toplevel
widget. Since execution begins in "main" in the support module and
references to toplevels and populating classes are already there,
there is no point to build init functions.

Earlier versions used the variables "top_level" and "w". To warn users
that the PAGE variables should be used with caution, version 7
prepends "_" and appends an integer to these variables, so "top" and
"w" became "_top1" and "_w1" for example. The integer is derived from
the naming scheme used in the project module. GUI's created with
Version 7 will have numbers like 1, 2, 3, etc., earlier versions used
a less transparent naming scheme.

The first toplevel class encountered in the GUI becomes "root" and is
special in the sense that the creation of the root window initiates
Tkinter and its destruction terminates Tkinter.

Earlier versions of PAGE generated a "destroy_window" function but
version 7 does not. The reasons include

   
#. Destroying the the root window would terminate the Tkinter,
   destroying the others would not.

#. Toplevel windows already have a destroy method. So coding
   "_top3.destroy()" is as simple as "destroy_window3()".

#. Toplevel windows have a number of useful methods such as withdraw,
   deiconify, and a dozen more. Clearly one does not want a host of
   such functions gratuitously added.   
   
#. Consistency would require functions for each toplevel and again,
   that seems excessive.

The final compatibility issue: the facility for upgrading the support
module will not convert an earlier support module to a version 7
support module.

The support module no longer contains the function "set_Tk_var". Its
former contents have been placed in the class function of the toplevel
needing the variable. This has two benefits:
+ one does not have name collisions from using the same names across
  toplevel widgets.
+ the Tk variables are instance variables meaning that it is possible
  to multiple copies of the same toplevel widget each with its own
  value of the Tk variables.  
   
Migrating Existing Projects
~~~~~~~~~~~~~~~~~~~~~~~~~~~

To approach the subject of migration, let me discuss my migration of
some of the examples. I will start with the simplest examples where the
project contained just one toplevel window and then a multi-widget example.

With an example like "calendar", I first opened the project file
"cal.tcl" in PAGE and generated and saved the GUI module and the
support module. Next I opened both the support module and the previous
support module in my favorite editor and copied the various hand
modified routines from the old support module into the new support
module, placing them just above the line:

.. sourcecode:: python

	if __name__ == '__main__':

Since I had used the variable "w" to reference the top level window,
I included the init function as well. To link between the "main"
function and the init function, I added the function call:

.. sourcecode:: python

	init(_top1, _w1)

as the penultimate line of "main".
After that I only had to copy a couple of import statements:

.. sourcecode:: python

	import datetime
	import calendar				

and the application ran correctly. I used the generated skeleton to
tell me which functions to copy from the old support
module. Obviously, I had to include the whole call tree of function
referred to by the newly included functions. To serve as a guide,
I left in the generated skeleton functions. They are replaced by later
functions of the same name. 

Consider next the vrex example.
Here I opened 'vrex.tcl' in PAGE and then used the borrow facility to
bring in 'help.tcl' from which I did a copy and paste of the help
toplevel unto the vrex toplevel. I then saved the vrex project, quit
PAGE, and then reopened 'vrex.tcl' with PAGE and then generated and
saved the GUI module and support module.

Opening the support module in an editor, I then added the functions
from the old support module including init which does significant
post-creation initialization and so I added the line of code

.. sourcecode:: python

   init(root, _w1)

to "main" as in the previous example.

Since I do not wish to have the help window to open until the help
function is invoked, I moved the three creation statements for the
help window from the main function to the help function. I then
copied the support functions from help_support.py to
vrex_support.py. The init function in help_support.py has only one
operative statement:

.. sourcecode:: python

	load_vrex_help(w.TScrolledtext1)

I placed that line of code at the bottom of the help function changing
'w' to '_w2'. Similarly, I changed 'top' to '_top2' in the close
function.

One of the results of executing the init function is to establish global
variables 'w' and 'top' as references to '_w1' and '_top1'. This works
fine for the vrex support routines and saved me from having search and
change those variables in the vrex support routines.
In the help support routines the variables 'w' and 'top' occurred only
once so they were manually changed.

Of course, I needed to add the following import statement:

.. sourcecode:: python

	from tkinter import filedialog
	
In addition to the considerations above, it may be that one encounters
a call back function which starts with the line:

.. sourcecode:: python

	def startup(p1, p2):

in an earlier support module where the new callback would be:

.. sourcecode:: python

	def startup(*args):

So modify the code to be of the form:

.. sourcecode:

   def startup(*args):
       p1 = args[0]
	   p2 = args[1]
	   ...