WxGUI Programming Howto
The basic rules how to write wxPython-based wxGUI are in the file SUBMITTING_WXGUI in GRASS source codes (TODO: create this file). This page contains more advanced tips how to write wxGUI source code.
Isolating different behavior by using wx-like styles
Both wxWidgets and wxPython use styles to set appearance and behavior of widgets and windows.
Different behavior or different sub-widgets of one widget can be distinguished by wx-like styles.
Using events to reduce dependencies
An event tells us that something happened
If you are writing code where two classes (class A and B) interact with each other, you may tend to write code into an inappropriate class. For example, class A (ModuleSearchWidget, in this case) contains this code:
if self.cmdPrompt: self.cmdPrompt.SetText(text) self.cmdPrompt.SetSelectionStart(pos) self.cmdPrompt.SetCurrentPos(pos) self.cmdPrompt.SetFocus()
This code is specific to class B (GPrompt). Other users of class A do not specify cmdPrompt variable and do not need this code. However, when changing the class B you must modify also A. Similarly, when reusing the class A for the class C, you need to add a C specific code into the class A. The solution is to replace the code depending on other classes by emitting an event. Here is the example code (the event name tell us what is actually happening):
moduleEvent = gModuleSelected(name = cmd) wx.PostEvent(self, moduleEvent)
Now, when using class A with B or C, you don't need to modify class A. You just connect A with B or C by binding a handler in B or C to an A's event.
Do not call event handler directly
If you are calling event handler directly (without an event), you should create a method which does the actual work, has proper name and does not require an event. The following caller's code:
would be replaced by:
Event handler can be a lambda function
If you are using event handlers which only call a function to do the actual work, you will probably realize that your event handlers are very short, probably only one line. To avoid creating of these short functions, you can use lambda functions. The following code:
self.Bind(EVT_TEXT_CHANGED, self.OnTextChanged) # ... def OnTextChanged(self, event): UpdateTextLabel(event.text) def UpdateTextLabel(text): # ... actual work here
can be replaced by:
self.Bind(EVT_TEXT_CHANGED, lambda event: UpdateTextLabel(event.text)) # ... def UpdateTextLabel(text): # ... actual work here
Events emitted by a class are part of its interface
Events are part of class' interface. If defining new events, define them before the class definition.
Newly defined events should be before a class definition (not at the file beginning or in separate file). Note that this does not apply to CommandEvents.