Friday, December 19, 2008

BUI - LayoutManager and much more

Last weekend I progressed on restructuring the code. I managed to get rid of some cruft. Of course there is some new cruft to fix. :)

Here are the major changes made summarized:
  • Implemented LayoutManager to handle calculating all layout related things for all user interface containers/elements. This made it possible to clean up the mess caused by different coordinate systems used by the backend and OpenGL based frontends. In BUI origin of a window is imagined to be at the top left corner. Also the y axis is inverted (value of y is inverse of y in Cartesian coordinate system).
  • Cleaned up the way render is defined in AbstractObject. This leads cleaner frontend code as coordinate doesn't have to be passed on anymore. Also super is no longer needed.
  • Implemented Separator element. It just draws a line based on the container inside which it has been placed. Note that name attribute works only in case it has been placed inside VerticalContainer (vertical flow leads to horizontal separator and vice versa). In this case the name is rendered in the middle of the separator. Also line is not drawn on that specific location. To implement the missing case I probably have to look into Pango and use Cairo to render Pango text into an OpenGL surface or just a raw image which isn't as nice.
  • I also began to separate drawing code into it's own modules. In case of OpenGL the idea is to make the code generic so that the actual import can be handled on the frontend side. This means the same functions can be reused on multiple frontends.
  • Converted Fill to element. It did not make sense to define Fill as a container as it logically cannot contain other elements. Therefore it is an element.
  • Cleaned up the way Blender WindowManager calculates mouse coordinates. It should work alright now. There was a tiny glitch before.
There are still plenty of things to implement before the first official release but at least it's progressing. :) In the next weeks I hope to implement features more visible to the user (CustomContainer with free layout (element coordinates as relative?), event mapper, scrollbars, auto width/height, StyleManager?, ???).

Friday, December 12, 2008

BUI - Image Filter tweaks, AllMethodsStatic, background color for containers/elements

I did not get much done since the last post. The week was pretty busy. After next weeks I will have holiday of three weeks so I expect to be more productive then. Anyway I got something done:
  • I added some minor tweaks to the Image Filter example. For instance it shows the image names as labels now.
  • A new base class for container and event classes, AllMethodsStatic, is available. I used some metaprogramming to define this class to mimic namespaces. Essentially inheriting from this class makes all methods of the class static. This is nice as you don't have to write @staticmethod decorators in front of each constraint/event. The solution is not particularly pythonic but I think it works in this case, at least for now.
  • I made it possible to determine background color for a container or an element. This should be expanded later to make it possible to define gradient/image/whatever as a background. As a side effect implementing this clarified the code a bit. The rendering parts still need solid tests though.

Friday, December 5, 2008

BUI - state events and minor tweaks

Since the last blog post I have implemented following features for BUI:
  • Initial, very crude Pyglet frontend. Note that it does not do anything smart yet.
  • State events. This means that it is possible to check if mouse cursor is over some given element or container and then trigger an event handler. There is a new event hook known as on_mouse_over to activate this feature. See image_state_event.py example file for more details.
  • YAML definitions embedded in Python files work with tabs now. I added this feature as some Python coders prefer to use tabs instead of spaces favored by PEP-8. Now it should not cause as much confusion for the coder. Note that mixing spaces and tabs may cause issues still as expected.
  • I also did some miscellaneous clean up here and there. See commit logs for more details if you are interested.
  • Implemented simple image browser (with wildcards) for Blender. See http://code.google.com/p/bui/source/browse/trunk/examples/blender/imagefilter.py .
I also began to sketch my first totally custom user interface element for Blender. This element, vertical slider, should be usable for scrollbars as well. I implemented initial drawing code (very crude!) and came to realize my event mapping system needs a refresh. Basically the issue I encountered was that sometimes it is beneficial to be able to catch multiple events at once and then trigger some handler(s) based on that. For instance in this case the mouse cursor has to be over the thing which you use to control the value of the slider (does anyone know what this thing should be called?) and to adjust the value the user has to click mouse button (usually left one) and then move it.

After this specific case I noticed that this is something that should be doable generally. It is actually a part of design that should be separated altogether. Designwise this means that I have to implement an event to event handler mapper that handles the relations I need. In other words it should be possible to map multiple events (state or regular) to multiple handlers.

I think I end up implementing something like this in Python:

event_mapping = '''
- events: s_press, k_press # triggers at once when both s and k are pressed
event_handlers: walk_the_dog, feed_the_monkey
- events: left_mouse_pressed, cursor_over_element(some_element)
event_handlers: paint_on_cursor_location
- events: s_release, p_released, shift_press
event_handlers: write_to_blog
'''

...

# remember to pass the mapping to Application


Note that in the first case it should trigger also when the user releases the key for a while and then presses it again!

In the second part I used cursor_over_element instead of mouse_over_element because I think cursor is more generic than mouse. It is possible to use other input devices than mouse after all. Also note the pressed notation. In this case press is actually converted into a state using -ed. Hence the handlers get triggered all the time both given states are true.

In the third case state and regular events are actually mixed. The event triggers only if p has been released (ie. it is not pressed at the moment) and then s is released and shift pressed down simultaneously. This is an example of a case that just won't work in the system at it is nonsensible this way. If either s or shift case was removed or converted to a state, it would be just fine. It might be nice to be able to warn the developer about situations like this.

BEGIN EDIT:
I realized that the third case works. All the user has to do to trigger the handlers is to make sure p has not been pressed, shift is pressed (activates "press shift" and s is released (activates "s release"). When all those cases are true, the handlers are executed.
END EDIT:

It may be that I will retain at least parts of the old way of assigning handlers as the current way is quite adequate for some quick hacking but we shall see...

I also had a nice chat with the author of bpyGUI. To understand what you can do with bpyGUI, check out http://www.youtube.com/watch?v=QhDQRFLv3K0 . His approach is quite different from mine. Probably the neatest thing about his system is the way he implemented drag and drop. There are probably a couple of things to pick up there. :)

I realized that I might be able to get rid of those pesky @staticmethod decorators simply by creating a metaclass containing the behavior. This is something that must be done before the official release of course as it simplified the coding process quite a bit and makes code more readable.

As probably most Python people noticed, Python 3.0 was released! This was a really big thing for the Python community and a long awaited one. It will probably take a while till I begin to port bui to Python 3.0 however. Before migration would make any sense, all my dependencies current dependencies (Blender, pyglet, pyyaml) would have to be available in Python 3.0 code.