File: tech_notes.txt

package info (click to toggle)
wxglade 0.6.5-2
  • links: PTS
  • area: main
  • in suites: wheezy
  • size: 3,348 kB
  • sloc: python: 24,679; xml: 1,022; makefile: 135; sh: 4
file content (185 lines) | stat: -rw-r--r-- 9,268 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
	wxGlade technical notes
	-----------------------
Last update: 2003-06-26, but only section 9. The rest has not been updated
             since 2002-07-22, and it's likely be very outdated in some parts


This is an informal overview of wxGlade internals, made through a sample
session of use. Each action of the hypotetical user will be described from the
point of view of the application, to (hopefully) understand what's happening
behind the scenes.
These notes are *absolutely* incomplete and in some cases they might be
outdated or not completely correct: the best reference is always the source
code.

1. 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/' subdir 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
   succesfully accomplished, such 'writer' object is considered a valid code
   generator, and is inserted into the 'common.code_writers' dict (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 succesfully 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_all') returns a list of wxBitmapButton objects


2. Adding a toplevel widget
---------------------------
When the user clicks on a button of a toplevel 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 app.
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' dict, 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' dict. 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 toplevel
  windows) and the zero-based position inside the sizer (once again, this is
  unused for toplevel 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' dict


3. Adding a toplevel sizer
--------------------------
This is similar to the addition of a toplevel 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' dict as described above)

- when the user left-clicks the mouse inside the previously added toplevel
  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


4. 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
  '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-toplevel 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' dict


5. 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


6. Saving the app
-----------------
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' dict
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.


7. Loading an app from an 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, an 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' dict) 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


8. 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 toplevel 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' dict. 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


9. For contributors
-------------------
You are, of course, free to make any chages/additions you want to wxGlade, in
whatever way you like. If you decide to contribute them back, however, here are
some simple (stylistic) rules to follow: note that these are only general
indications, if you think they don't fit somewhere, feel free to ignore them.

 - class names are usually CamelCase
 - variables, functions and method names are lower_case_with_unserscores
 - ``constants'' are UPPER_CASE
 - source lines are at most 79 characters long
 - class bodies are usually ended by a # end of class ClassName comment
 - source files use Unix EOL conventions (LF) if possible. In any case, please
   don't mix Unix and Windows EOLs
 - put your copyright info whenever appropriate

 - that's all folks!!