Showing posts with label python. Show all posts
Showing posts with label python. Show all posts

Friday, January 9, 2009

BUI - refactoring, configuration, timers

During this week I spent significant amount of time restructuring and especially refactoring BUI. I also have initial implementation of timers and event manager ready. I just hooked up the event manager to current PyOpenGL frontend today so you can set up key events and assign handlers to them just today. Both parts probably could use at least some sort of rewrite though but at least they work minimally and can be easily extended.

In this and upcoming posts I aim to provide more "user friendly" information. I will try to be more verbose about the basic concepts and possibly reiterate some information that has been already mentioned earlier in the blog. Also to get concrete view on how to actually write scripts using BUI I will likely provide some sort of "recipes" or similar. Probably converting the essential information from this blog into some sort of manual would be a worthwhile effort later.

As a part of refactoring effort I extracted a concept of node from the architecture. A node is something that has references to other children and parent nodes. Analogously you can think them as inputs and outputs. I treat each part of the user interface as a node. A node can be either a layout, an element or even a window.

Layouts (used to be Containers) contain other layouts and elements and define the way elements are placed inside it. At the moment there are three layouts available: free, horizontal and vertical.

Free layout means that all nodes contained within have full freedom to determine their location, width and height. In case of horizontal and vertical layout location cannot be predefined. However hints about width and height can be given. The system figures out the missing bits in as sensible way as possible. Note that default settings defined globally in a configuration can be also overwritten at layouts also. This means that their children will use that local setting instead of globally defined one.

It is important to note that a node width/height has a mode (ie. width_mode), minimum (ie. min_width) and maximum (ie. max_width) attributes. By default width/height is set to auto(matic) mode unless width/height is explicitly given at the user interface definition. This means that it tries to match width/height to the default value (ie. default_node_width). It also takes parent width/height in count and finds minima of default value and parent's value. Note that during evaluation of an "auto" value, set value minimum and maximum are taken in count should they have been defined.

Other modes available are absolute and relative (should rename this as proportional for clarity?). If width/height is given a value explicitly in the user interface definition it's considered absolute by default. This value overrides the default value and is clamped to minimum, maximum and parent's value just like automatic option before.

Currently the remaining option left, relative, scales width/height in relation to its parent's value. At the moment it doesn't take minimum and maximum in count but it probably should.

Next I will cover the current configuration system briefly. To get straight to the business a configuration can look like something like this (taken from example clock.py):


configuration = '''
label: Clock test
width: 1000
height: 200
start_timers: True
hotkeys: hotkeys
structure: root_structure
#default_node_height: 20
'''


I started to use label attribute actively everywhere to signify the part of text that is displayed on the user interface. So if an element has some visible text in the user interface you can expect to use label attribute to access it. This convention is used in PyGTK also. Previously I used "name" attribute for the same purpose. Now however name refers to node's internal name that can be used to find certain node(s) for instance.

width and height refer to the default window's width and height. start_timers can be set True to determine that as the application is executed its timers will start at the same time too. hotkeys and structure refer to default hotkey map and user interface structure used (again candidates for renaming :) ). Those are defined in classes passed onto WindowManager. Other options, such as default_node_width or height, can be set as well.

At later time it should be possible to set up multiple windows and subwindows using configuration. In case of subwindow definition it might be interesting to reuse current layout system so that windows, being nodes themselves, just take place of elements. For time being it's limited to one only. In case of Blender I have to provide limited configuration as Blender's Scripts Window is the window itself and sets already certain attributes.

The last topic of this post, as mentioned in the title, is the timer system. Following snippet contains a simple definition of a timer (yet again from clock.py):


class Timers(AllMethodsStatic):
def update_clock(root_layout, timer, timers):
''' interval=0.5 '''
current_time = get_current_time_as_formatted_string()
clock = root_layout.find_child(name='current_time')
clock.label = current_time


As you can see a timer itself is quite simple. Once a timer is started it will run indefinitely till stopped. To create a timer you need to determine certain kind of function signature, set up interval and write some code. In function signature root_layout, timer itself and a collection of all timers are provided. Note that the signature is bound to change as new requirements arise. :)

root_layout can be used to find nodes of the user interface definition and alter them. In example above current time is set to a label. A timer can change its own status (interval for instance) and stop/pause itself if needed. A timer can also alter the status of other timers.

The pace of development work will probably slow down a bit in the coming weeks as I resume my studies. I hope to find nice excuses to work on the project though.

There are still some crucial aspects (finalize basic systems, make Blender frontend work again) to handle and bugs to fix before first release can be made. Also there are not that many user interface elements available for PyOpenGL frontend yet... I also want to write more test applications to properly test the limits of the library.

Friday, January 2, 2009

BUI - restructuring, work on PyOpenGL frontend

Two weeks have gone since the last blog post. I took a tiny break from coding during X-mas but apart from that things have been going quite nicely. To summarize major work done, see following list:
  • Restructured the source to reflect the architecture better. Now there are separate packages for frontend, backend, graphics, utils and tests. It should make more sense now even for people beyond me hopefully. :)
  • Implemented basics of PyOpenGL frontend including window management (only one window at the moment) and a couple of elements (label and separator) for it.
  • Implemented width: auto. This means that the object will scale its width to one of its parent automatically. If width is not set at all, it is considered auto. It is possible to constraint this setting using min_width and max_width. Note that auto setting has to be implemented for height still.
  • Got rid of LayoutManager and replaced it with property based solution. This means updates to object properties (width/height for instance) cascade properly. So for instance if you alter the width of a HorizontalContainer its contents operate accordingly. This also separates the logic a bit better to smaller chunks.
  • Converted elements to their own modules under element package. I am not still exactly convinced that this is the way to go (perhaps there needs to be alias for easier imports?) as in the end each module contains only one or a few classes. I might need to revert this decision at some point.
  • Split up unserialize and clarified its structure.
  • Got rid of Pyglet frontend. It might make sense to use pyglet in graphics package though as it has well optimized drawing methods available.
  • Renamed Containers as Layouts. So instead of VerticalContainer there is VerticalLayout now. This reflects better the purpose of that specific class.
  • Implemented FreeLayout. Children of FreeLayout have unconstrained coordinates. This means you can define the placement of the children freely using x, y (top left corner coordinates!), width and height.
  • Cleared up attribute hierarchy of elements. There should not be any redundant attributes now. I also got rid of x_offset, y_offset and variable as they were pretty useless. I replaced variable using name. Name can be used to refer to certain element(s) internally. This meant I had to specify a way to determine the part visible to the user (think label!). To do this I followed the way used in PyGTK Glade and determined a label attribute.
  • Implemented plenty of OpenGL based drawing functions in the graphics package. Probably quite a few have to be implemented still. :)
At the moment I am working on implementing timers for the PyOpenGL frontend. After this I will focus on the event system. It might take some sort of overhaul to make it work the way I want. GLUT seems to provide lot of needed stuff for me though.

Besides making event system work properly I have to further validate the current code, implement more elements for PyOpenGL frontend, add scrollbars, drag and drop, configuration system (determines default window size), serializer (save work), undo (session recording even?), proper layout system and perhaps something else I can't quite remember at the moment.

I hope to have the first test release done in a few weeks to get some initial user feedback. I am sure there are a lot of rough edges to polish.

I also found out that there is another project that uses the same acronym, BUI. Check it out at http://www.samskivert.com/code/jme-bui/ . Incidentally it seems to be pretty much Java equivalent of this project. Due to the same acronym I might have to rename BUI to something else.

Pytform is another interesting project I happened to stumble upon. Unlike BUI it's totally focused on game creation.

Friday, November 21, 2008

BUI - week's development + Introduction to Python doc

As mentioned in the last Friday's post the week indeed was pretty busy. I did manage to develop BUI a bit though. Nathan "jesterKing" Letwory tested BUI in a project of his. Feedback has been positive so far. He also contributed the first patch for the project. The patch added Number and IntNumber elements for Blender.

I also made it possible to assign events to key presses. Example:

structure_keys = '''
a: add_monkey # assigns event add_monkey to press of a
d: delete_all # same idea here
s:
press: do_something # assigns an event to press of s
release: do_something_else # assigns an event to release of s
'''


So by default events are mapped to press. Should you want to attach an event to key release, use the syntax described above. Note that it should work even without press portion.

I also fixed a bug related to hiding individual elements (it was possible to hide only containers before). I also added Fill element that will eventually replace both EmptyElement and EmptyContainer. Furthermore I restructured the code a bit and got rid of some nasty parts. There is still some cleaning up to do though.

As an interesting sidenote I found something quite similar to BUI, at least if you look at the way the user interface is defined. So check out IUP (http://www.tecgraf.puc-rio.br/iup/). The nice thing about this discovery is that I might be able to find use for some of their ideas in BUI (Z-container, more elements?). :)

Also during the week I finished a brief documentation about Python. It is available at http://bebraw.googlepages.com/introductiontopython.pdf . Basically Introduction to Python is a crash course to Python in an object oriented way. It helps if you happen to know something about the paradigm already. Even if you don't it might give you some clue about it. It would probably make sense to "bloggify" the whole thing at some point and make it more beginner friendly but we'll see about that.