Showing posts with label bui. Show all posts
Showing posts with label bui. Show all posts

Friday, January 16, 2009

BUI - Renaming to Scocca, wiki

As I mentioned earlier, there is already a project named BUI. The project has been renamed as Scocca. Scocca is Italian as pretty much means supporting structure, which this Scocca essentially is. The new project site can be found at https://launchpad.net/scocca . Renaming gave me a chance to migrate to Launchpad. Of course it's bit of a tradeoff.

Google Code and Launchpad have several important differences. Probably the main one for me was the version control system available. Google Code offers SVN while Launchpad offers bzr. SVN is a fine example of a centralized version control system. bzr is a distributed version control system. I have plenty of experience with distributed systems while I have not used distributed systems before. They offer certain advantages over centralized ones however.

The main advantage is that it is extremely simple to branch. This is extremely valuable in open source environment as it allows anyone to easily maintain his own branch of the application. Furthermore the changes can be merged back to the trunk version should that be desired.

The main disadvantage of distributed version control systems is that they are a relatively new phenomenon. This means that the tool support is weaker than in case of SVN for instance. Most of IDEs support SVN in a form or other. Finding an IDE that supports bzr is much harder task to handle.

Of course I still have plenty of things to learn about bzr (how to handle bugs etc. properly) but I believe it's a good time to bite the bullet now than later.

As Google Code, so does Launchpad offer code browser and an issue tracker. Furthermore Launchpad has specific support for blueprints, translations (gettext) and dynamic FAQ (answers). Interestingly it supports cross-project bug reports. This is an important feature for a library. Applications that happen to use it and have their project site at Launchpad can point bug reports dealing with the library directly to it.

Of course it's not all happy, happy, joy, joy. Google Code offers a really nice wiki. This is something that Launchpad does not support. Yet, anyway. Also while blueprints are nice as they allow the users and developers alike to specify features and discuss on them, the page describing the blueprint has to be placed on some external place (say, wiki again?). This is not a problem, just an annoyance. I opened a wiki for the project at http://scocca.wik.is/ .

For time being the idea is that the site will contain the descriptions of blueprints. There is not much information yet though. The motivation behind writing these blueprints is simple. It should be easy to transform these into real documentation. Furthermore they may help to see oversights in design (it's hard to see certain issues while you are delving in the code). And as a bonus the whole process becomes more transparent meaning that the ideas are not just in my head or in the code but in actual, readable documentation. :)

Note that I am not saying Google Code is bad and you should not use it. On the contrary. I think it is an excellent service you should take advantage of particularly if you don't mind using SVN and don't care about the specific infrastructure Launchpad provides.

If you want to get started with Launchpad, check out https://wiki.ubuntu.com/LaunchpadStepByStepInstructions. It may seem a bit strange first with whoamis, push and pull but it actually slowly begins to make sense I hope. :)

My coding time has been severely limited by other duties. I hope to sketch out a style system (think CSS) during the weekend to the wiki and perhaps in code form too. The idea is that this system separates the width/height aspect out of the user interface structure definition. I think this clarifies the design a bit while the user has to provide one extra definition. At minimum the definition gives default style settings such as minimum widths and heights, background colors and such. In that way it will clean up configuration part as well.

There is plenty to do before I dare to release first public version but it's getting there each small step at a time. After all I need to have something to write for the next blog post at least. :)

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, 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.

Friday, November 28, 2008

BUI - week's development

Since last blog post I made following major changes to BUI:

  • EmptyContainer and EmptyElement are gone. Fill container has replaced them.
  • I got rid of most of globals() in the source. There is still one place left to repair though (I have some kind of idea how to fix this).
  • Events and Constraints are stored in classes (or in separate modules. I have not tested this option but it should work) and passed on to Application. See examples folder in the source repository to see how this mechanism works.
  • Added Image element for Blender. Note that you can use vector (SVG) images with Image element. It scales the image automatically based on given width and height. If no dimensions are given, it will use dimensions defined in the image itself.
  • Added Icon element for Blender. Icon makes it possible to render icons (default set) provided with Blender.
  • Added ColorPicker element for Blender. ColorPicker is just the basic color picker provided with Blender. I tried to get Normal element to work too but for some reason the callback function required for value updates of the element did not appear to work. This might be a bug in Blender and I will investigate this issue further.
  • Extended bones.py example file. Now it is possible to modify Z location of an object named the same as the slider created based on bones of the armature. Check out the example .blend provided to see and understand how this really work. Essentially it is just a handy replacement for Blender's driver system.

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.

Friday, November 14, 2008

BUI - development site

I modularized BUI and opened a development site for it. The site can be found at http://bui.googlecode.com/ . At the moment I am in process of implementing World (parent of containers). The plan is to have World to contain general rules (world size, element height of children without height of their own). Furthermore a World should be able to contain many children. This would make it possible to mimic panels of Blender. I have some other ideas as well but I will discuss them in more detail after I have the basic concept done.

If you look at the root of the source code repository, you can find three example files (bones.py, filter_layers.py and simple.py) that demonstrate the usage of BUI in context of Blender. To make those examples to work, set Python path of Blender so that it can find the place in which you checked out the repository. Or just put the repository in your scripts directory should you have one.

After I have finished implementing World, I still need to add basic way to map keys and mouse events (move, press buttons) to events. This is something that is actually pretty easy to do.

The next two weeks are going to be a bit busy so the development work will be probably a bit slower than usual. After that it should return back to normal.

Friday, November 7, 2008

BUI - Basic UI framework

Last weekend I began to develop my own user interface abstraction using Blender's Python API. The API offers basic access to Blender's drawing (Blender widgets) and event system. Based on this I built my own little system.

To drive development forward I used my "filter layers" concept. I find Blender's layer system a bit restricting. Filter layers offer the user a way to define what objects belong to a given layer by defining a filter. In other words you can have all lamps on one layer, all cameras on one and so on.

My approach is quite simple. I abstracted the structure of the user interface using YAML. It should be possible to change this to support other formats, such as XML, without too much effort. Here's a snippet of what user interface definition looks like at the moment:


ui_structure = '''
VerticalContainer:
width: 400
children:
- HorizontalContainer:
children:
- Label:
name: Filter layers v0.9
- PushButton:
name: X
tooltip: Quit script
event_handler: quit_script
width: 20
- EmptyContainer:
height: 10
- VerticalContainer:
name: layers
children:
- UIStructure:
name: layer_structure
- HorizontalContainer:
children:
- PushButton:
name: Add layer
tooltip: Add new layer
width: 100
'''


The treelike structure consists of containers and elements. Containers handle the order in which the elements inside it are rendered. So if you use HorizontalContainer, elements are laid out horizontally. In case of VerticalContainer, as you might expect, the elements are laid vertically. EmptyContainers can be used to add empty space to the layout. I originally used specific padding property put decided to remove it as EmptyContainers proved to be more simple and nicer to handle.

If you look closely, you can find a special element known as UIStructure. This actually represents a link to another tree. The reason why I implemented it this way is because I needed to duplicate certain parts of tree in the user interface code for instance when I am adding a layer or a filter.

The whole structure is converted in object format when the application is run. I wrote simple traversing functions that can be used to find elements in it. Also the user interface tree can be modified on runtime.

It is possible to attach an event handler to each element. The names of the event handlers are defined implicitly by default following Convention over Configuration principle. So if you have a PushButton named "Add layer", you can expect that it uses an event handler known as add_layer. Of course this is not desirable always so I made it possible to define event handlers explicitly.

At one point during the development I noticed that I need something more than just user interface definition and events. I realised that there are certain constraints that apply to the user interface all the time. For instance layers have to be numbered starting from one and increasing by one till nineteen. Or "Show filter" must be renamed to "Show filters" should a layer contain more than just one filter and vice versa.

To solve this issue I implemented a constraint system. Constraints are named as some_descriptive_name_here_constraint. They are evaluated each time the script window of Blender is redrawn. I suppose there are some optimizations that could be done but I am not too worried about the performance at this point as the constraints defined are pretty simple anyway.

At the moment I am in progress of generalizing and restructuring this whole system on proper modules. The idea is that there is a specific abstract part. Based on this common part drawing and event handling implementations can be made for wanted platform such as Blender or Pyglet. In other words should you want to use the basic system, all you need to do is to subclass event manager, write elements and hope it works. :)