Skip to content
Chris Oelmueller edited this page Feb 16, 2013 · 1 revision

This page lists all implicit conventions enforced or used somewhere in the Unknown Horizons gui code and xml files.

The second part then explains how to avoid some of the more obvious pychan design issues and bugs.

Implicit pychan conventions

TabBG

Our tabwidget tabs use the custom <TabBG amount="N" /> widget to show the (tiling) background. When you change a tab size, modify the amount= attribute (how many tiles we will display) and remember to adapt the parent Container size= as well. Corresponding sizes:

<Container  size="250,300"> <TabBG amount="3" />
<Container  size="250,350"> <TabBG amount="4" />
...
<Container  size="260,550"> <TabBG amount="8" />

3 and 8 tiles are the suggested minimum/maximum, respectively.

ImageButtons

  • we provide these predefined buttons: <OkButton>, <CancelButton>, and <DeleteButton>
  • they represent the actions "confirm/acknowledge", "cancel/abort" and "tear/remove/trash" where we want to always use the same icon throughout UH to reduce confusion potential
  • use them like regular buttons. the preset attributes which can be overwritten are: name='okButton'/'cancelButton'/'deleteButton', path=, is_focusable=False
  • the widgets are defined in horizons/gui/widgets/imagebutton.py

styling can depend on widget names

  • check horizons/util/gui.py which defines that if a widget name is "name" or starts with "headline", then the headline style in horizons/gui/style.py is applied to the widget and all subwidgets. Also, if a widget name or comment start with "uni_", the widget font will be the Unifont font.
for w in widget.findChildren():
	if w.name.startswith("headline") or w.name == "name":
		w.stylize('headline')
	elif w.name.startswith("uni_") or w.comment.startswithith("uni_"):
		w.font = 'unifont'

icon names

  • for an icon which is named 001.png, we expect the file 001_h.png to be present in the same directory. It is the image that gets displayed on hovering an icon that uses 001.png.
  • resource and build menu icons are looked up by their respective ID in 3 digit format, padded by zeros (e.g. 001.png and 017.png)

translation tweaks

  • by default, all text= and helptext= attributes are translated.
  • if you would like a certain widget to not be translated (e.g. links), you have two options
    • either remove the entire gui definition file from the index (exclusion list files_to_skip in development/extract_strings_from_xml.py
    • or use comment="noi18n" as keyword in the xml file. If you want to use the comment attribute for something else, prepend 'noi18n' to the widget name instead (example: name="noi18n_img_label")

Non-trivial techniques to make pychan do what you want

DROP DOWNS crash the game

Workaround: We don't use drop down widgets at all. Most were replaced by ListBoxes contained in ScrollAreas.

Receive event when scolling through a list with the mouse wheel

You would usually do this, but then you only get events on clicks

self.widget.findChild(name="mylist").capture(cb)

capture() is somewhat equivalent to "action", so just replace the stuff from above with this:

self.widget.findChild(name="mylist").mapEvents({
    'mylist/action'              : cb,
    'mylist/mouseWheelMovedUp'   : cb,
    'mylist/mouseWheelMovedDown' : cb
})

You will usually also want to support moving with arrow keys:

self.widget.findChild(name="mylist").capture(cb, event_name="keyPressed")

Layouting is off

Try it like this:

for i in xrange(3):
    yourwidget.resizeToContent()
    yourwidget.adaptLayout()
    yourwidget.show()

This can usually be reduced to a single call, but try this block to be certain. If it still doesn't get layouted, try it again next tick, it is possible that a frame changes changes some values.

Attributes of my custom xml widget are not recognized

You are probably trying to access them in __init__ of your new widget. For some reason this is impossible for all widgets built from xml.

As a workaround, make your new attributes be python properties and move the actually interesting code into its setter. That way it is called even after __init__ does not know about the correct value yet.

Clone this wiki locally