WxGUI Programming Howto

From GRASS-Wiki
Revision as of 11:00, 2 February 2013 by ⚠️Wenzeslaus (talk | contribs) (wx-styles, events)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.

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:

self.OnTextChanged(event=None)

would be replaced by:

self.UpdateTextLabel()

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.