Package fife :: Package extensions :: Package pychan
[hide private]
[frames] | no frames]

Package pychan

source code

Pythonic Guichan Wrapper - PyChan

Pythonic GUI API.

Features

TODO

BUGS

Problems

How to use

At its core you only need a few functions. After setting up FIFE you need to initalize pychan. After that you can load a GUI from an XML file. Please see the documentation of loadXML for the details of the XML format :

  import pychan
  pychan.init(fifeEngine)
  guiElement = pychan.loadXML("contents/gui/myform.xml")

The resulting guiElement can be shown and hidden with the obvious widgets.Widget.show and widgets.Widget.hide methods.

To get a specific widget you have to give it a name in the XML definition and use that to extract the widget from the returned GUI element. :

  okButton = guiElement.findChild(name="okButton")
  myInput = guiElement.findChild(name="myInput")

The data is extracted and set via direct attribute access. These are using the python property technique to hide behind the scenes manipulations. Please keep in mind that the Layout engine and the exact way the widgets are displayed is somewhat limited. :

  myInput.text = "Blahblah"
  myList.items = ["1","2"]
  guiElement.position = (80,90)

A dialog without an OK button would be futile - so here's how you hook widget events to function calls. Every widget has a widgets.Widget.capture method, which will directly call the passed function when an widget event occurs. As a convenience a widgets.Widget.mapEvents function will batch the widgets.Widget.findChild and widgets.Widget.capture calls in an obvious way. :

  myButton.capture( application.quit )
  guiElement.mapEvents({
     'okButton' : self.applyAndClose,
     'closeButton':  guiElement.hide
  })

Other important places to look for information:

Initialization, data distribution and collection

Very often a dialogs text fields, labels and listboxes have to be filled with data after the creation of the dialog. This can be a tiresome process. After a dialog has executed, other attributes have to be read out again, this to can be tiresome. PyChan simplifies both processes. But it treats them as three processes. One is setting the data that will never be read out again - called initial data - the text of a checkbox or the list of a listBox are good examples. The second is setting the data that is mutable by the user and may be read out again - for example the state of a checkbox or the selected index in a list. The third and final process is collection of the user-mutable data:

 guiElement.distributeInitialData({
       'myListBox' : choices,
       'myLabel' : map.name,
 })
 guiElement.distributeData({
       'myTextField' : map.description
 })
 # ... process dialog.
 map.description, choice = guiElement.collectData('myListBox','myTextField')
 print "You selected:",choice,", good choice!"

See widgets.Widget.distributeData,widgets.Widget.distributeInitialData, widgets.Widget.collectData and widgets.Widget.collectDataAsDict.

Styling and font handling

Note: These features are work in progress and likely to change.

A style is a set of rules for matching widgets and a set of attributes applied to them after creation. The attributes can be any of the given attributes. Matching happens currently only by the widget class name itself.

As an example the following style - written as a python data structure - will set the border size of all labels to 10:

 style = {
     'ListBox' : { # Matches all listboxes
          'border_size : 10 # After creation call  lisbox.border_size = 10
     }
 }

As a convenience you can use the string default to match all widget classes and thus - for example apply a common font:

 style = {
    'default' : {
         'font' : 'console_small'
    }
 }

A new style is added to pychan with internal.Manager.addStyle. You can set a new default style by adding a style with the name 'default'.

The font is set via a string identifier pulled from a font definition in a PyChan configuration file. You have to load these by calling loadFonts in your startup code:

  import pychan
  pychan.init( fifeEngine )
  pychan.loadFonts( "content/fonts/console.fontdef" )

The font definition files are in the following format:

       [Font/FIRST_FONT_NAME]

       type: truetype
       source: path/to/font.ttf
       # The font size in point
       size: 30

       [Font/SECOND_FONT_NAME]

       type: truetype
       source: content/fonts/samanata.ttf
       size: 8

       # And so on.

I hope the example is clear enough ... Other options you can set:

Unicode and internationalisation

All text that is visible and editable by the player has to be a unicode object. All text that is used internally, e.g. widget names, have to be normal strings.

While PyChan will not raise an exception, if you do not follow this guideline, you are encouraged to so.

You can change the way unicode encoding errors are handled by using the function setUnicodePolicy.

Widget hierachy

Every widget can be contained in another container widget like Window, VBox, HBox, Container or ScrollArea. Container widgets can contain any number of widgets. Thus we have a tree like structure of the widgets - which finally makes up the window or frame that is placed on the screen.

In PyChan widgets are supposed to be manipulated via the root of this hierachy, so that the actual layout can be changed in the XML files without hassle. It can be compared to how HTML works.

These bits and pieces connect things up:

-  name - A (hopefully) unique name in the widget hierachy
-  findChildren - The accessor method to find widgets by name or any other attribute.
-  _parent - The parent widget in the widget hierachy
-  deepApply - The method used to walk over the widget hierachy. You have to reimplement
  this in case you want to provide custom widgets.

Wrapping machinery

The wrapping mechanism works be redirecting attribute access to the Widget derived classes to a real_widget member variable which in turn is an instance of the SWIG wrapped Guichan widget.

To ensure the real widget has already been constructed, when the wrapping machinery is already in use, this has to be the first attribute to set in the constructors. This leads to a reversed construction sequence as the super classes constructor has to be invoked after the subclass specific construction has taken place.

Submodules [hide private]

Classes [hide private]
  _GuiLoader
Functions [hide private]
 
init(engine, debug=False, compat_layout=False)
This has to be called before any other pychan methods can be used.
source code
 
traced(f)
Simple decorator that prints tracebacks for any exceptions occuring in a function.
source code
 
loadXML(filename_or_stream)
Loads a PyChan XML file and generates a widget from it.
source code
 
setupModalExecution(mainLoop, breakFromMainLoop)
Setup the synchronous dialog execution feature.
source code
 
setUnicodePolicy(*policy)
Set the unicode error handling policy.
source code
Variables [hide private]
  loadFonts
  manager = None
hash(x)
Function Details [hide private]

init(engine, debug=False, compat_layout=False)

source code 

This has to be called before any other pychan methods can be used. It sets up a manager object which is available under pychan.manager.

Parameters:
  • engine - The FIFE engine object.
  • debug - bool - Enables and disables debugging output. Default is False.
  • compat_layout - bool - Enables and disables compat layout. Default is False.

traced(f)

source code 

Simple decorator that prints tracebacks for any exceptions occuring in a function. Useful to avoid the infamous 'finally pops bad exception' that shadows the real cause of the error ...

loadXML(filename_or_stream)

source code 

Loads a PyChan XML file and generates a widget from it.

Parameters:
  • filename_or_stream - A filename or a file-like object (for example using StringIO).

    The XML format is very dynamic, in the sense, that the actual allowed tags and attributes depend on the PyChan code.

    So when a tag Button is encountered, an instance of class Button will be generated, and added to the parent object. All attributes will then be parsed and then set in the following way:

    • position,size,min_size,max_size,margins - These are assumed to be comma separated tuples of integers.
    • foreground_color,base_color,background_color - These are assumed to be triples or quadruples of comma separated integers. (triples: r,g,b; quadruples: r,g,b,a)
    • opaque,border_size,padding - These are assumed to be simple integers.

    All other attributes are set verbatim as strings on the generated instance. In case a Widget does not accept an attribute to be set or the attribute can not be parsed correctly, the function will raise a GuiXMLError.

    In short:

           <VBox>
                   <Button text="X" min_size="20,20" base_color="255,0,0" border_size="2" />
           </VBox>
    

    This result in the following code executed:

           vbox = VBox(parent=None)
           button = Button(parent=vbox)
           button.text = "X"
           button.min_size = (20,20)
           button.base_color = (255,0,0)
           button.border_size = 2
           vbox.add( button )
    

setupModalExecution(mainLoop, breakFromMainLoop)

source code 

Setup the synchronous dialog execution feature.

You can enable synchronous dialog execution by passing to functions to this function.

Parameters:
  • mainLoop - Function - This is regarded as the applications main loop, which should be able to be called recursively. It should not take no arguments and return the argument passed to the second function (breakFromMainLoop).
  • breakFromMainLoop - Function -This function should cause the first function to finish and return the passed argument.

    With these to functions dialogs can be executed synchronously. See widgets.Widget.execute.

setUnicodePolicy(*policy)

source code 

Set the unicode error handling policy.

Possible options are:

  • 'strict' meaning that encoding errors raise a UnicodeEncodeError.
  • 'ignore' all encoding errors will be silently ignored.
  • 'replace' all errors are replaced by the next argument.

For further information look at the python documentation, especially codecs.register_error.

Example:

       pychan.setUnicodePolicy('replace','?')