Incomplete and outdated parts

Caution

The remaining content in this section has not been updated since July 2002. It's likely be very outdated in some parts.

Startup

The program starts from the function main in the module main: this creates an instance of wxGlade (a subclass of wxApp), which in turn creates a wxGladeFrame: this is the main window of the app, i.e. the one with the palette of buttons. The initialization of wxGladeFrame consists of three steps:

  • Creation of the three frames of the app, the palette itself, the tree and the property window

  • Loading of the code generator modules. The codegen/ subdirectory is scanned to find the available code generators: when a python module is found, the app tries to load it and to access its 'writer' attribute: if this is successfully accomplished, such 'writer' object is considered a valid code generator, and is inserted into the 'common.code_writers' dictionary (the key used is the 'language' attribute of the writer itself)

  • Loading of the widget and sizer modules. To load the widgets, the file widgets/widgets.txt is read, and the app tries to import every widget module listed on such file. For every module successfully imported, the initialize function is then called: this function sets up the builder and code generator functions for a particular widget (explained later), and returns a wxBitmapButton instance to be added to the main palette. The loading of the sizers is more or less the same, except that all the sizers are in the same module, edit_sizers, and the initialization function (called init_gui) returns a list of wxBitmapButton objects

Adding a top-level Widget

When the user clicks on a button of a top-level widget (a Frame or a Dialog), the method add_toplevel_object of wxGladeFrame is called: this is responsible for the addition of the widget to the application. This happens in this way:

  • the name of the class of the widget to add is obtained: this is done with the use of the common.refs dictionary, which maps the ids of the buttons of the palette to the class names of the widgets.

  • with the name just obtained, the appropriate factory function for the widget to add is got from the common.widgets dictionary. This function must accept three parameters: a reference to the parent widget (None in this case), a reference to the sizer to which the widget will be added (again None for top-level windows) and the zero-based position inside the sizer (once again, this is unused for top-level windows)

  • the call of the factory function actually builds the widgets and inserts it in the common.app_tree tree with a call to its method insert. The __init__ method of the widget also builds all the Properties of the object and stores them in the 'self.properties' dictionary

Adding a top-level Sizer

This is similar to the addition of a top-level widget, but the action is performed in two steps:

  • when the user clicks on the button in the palette, the method add_object of wxGladeFrame is called: this sets the global variables common.adding_widget and common.adding_sizer to True, and stores the class name of the sizer to add in the global common.widget_to_add (the name is obtained from the common.refs dictionary as described above)

  • when the user left-clicks the mouse inside the previously added top-level widget, its drop_sizer method is called, which is responsible of the addition of the sizer: it calls the factory function for the sizer (passing self as the first argument), which will build the object and add it to the tree

Adding a Normal Widget/Sizer

This step is more or less the same as step 3:

  • wxGladeFrame.add_object is called in response to a button click

  • when the user ``drops'' the widget inside a slot in a sizer, the method on_drop_widget of edit_sizers.SizerSlot is called, which in turn calls the appropriate factory function with arguments self.parent, self.sizer and self.pos (i.e. the parent, sizer and position inside the sizer of the slot that will be replaced). Factory functions of non-top-level objects call, apart from common.app_tree.insert to insert the object in the tree, the method add_item of edit_sizers.SizerBase, to add the object to the sizer and to remove the slot. For managed widgets/sizers, the __init__ method also builds the Properties which control the layout of the object inside a sizer, and stores them in the self.sizer_properties dictionary.

Changing the Value of a Property

When the user selects a widget the property window changes to display the properties of the selected object: this is done by the functions show_properties of edit_windows.EditBase and edit_sizers.SizerBase, which are called inside two event handlers for focus and tree selection events.

When the value of a Property is changed, its setter function is called to update the aspect/layout of the widget the Property belongs to: such function is obtained from a call to the widget's __getitem__ method, which must return a 2-tuple (getter, setter) for the Property

Saving the Design

This operation is performed by the common.app_tree Tree: for every Node of the tree, an object XML element is generated, with the following attributes: name, class, base (class). Each object contains an element for each Property (generated by the write method of Property) and then an object element for all its sub-widgets and/or sizers. Properties in the sizer_properties dictionary are treated in a different way, as well as the children of a sizer, which are sub-elements of sizeritem objects: see the source code for details.

Loading an App from a XML file

This is done by xml_parse.XmlWidgetBuilder, a subclass of xml.sax.handler.ContentHandler.

Basically, the steps involved are the following:

  • when the start of an object element is reached, a XMLWidgetObject instance is created and pushed onto a stack of the objects created: such object in turn calls the appropriate ``XML builder'' function (got from the common.widgets_from_xml dictionary) that creates the widget: this function is similar to the factory function used to build the widget during an interactive session, see the code for details and differences

  • when the end of an object element is reached, the object at the top of the stack is removed, and its widget (see the source of XmlWidgetObject) is laid out

  • when the end of a Property element is reached, the appropriate setter function of the owner of the Property is called. This is the default behaviour, suitable for simple properties. For more complex properties, whose XML representation consists of more sub-elements, each widget can define a particular handler: see for example FontHandler in edit_windows.WindowBase

Generating the Source Code

This section is the result of a cut & paste of the comment at the beginning of codegen/py_codegen.py. It is *VERY* incomplete. The ContentHandler subclass which drives the code generation is xml_parse.CodeWriter.

How the code is generated: every time the end of an object is reached during the parsing of the XML tree, either the function add_object or the function add_class is called: the latter when the object is a top-level one, the former when it is not. In the last case, add_object calls the appropriate ``writer'' function for the specific object, found in the obj_builders dictionary. Such function accepts one argument, the CodeObject representing the object for which the code has to be written, and returns 3 lists of strings, representing the lines to add to the __init__, __set_properties and __do_layout methods of the parent object.

Note

The lines in the __init__ list will be added in reverse order.