Monday, September 28, 2009

Scocca - New event system

Long time, no post. Since the last post I have been busy writing programming languages, studying AI, programming PHP+jQuery (MediaWiki extensions) etc. but that doesn't mean Scocca or Cassopi have been forgotten. On the contrary.

I decided to change the design of Scocca. There are a couple of major changes:
  • The programming interface that Scocca provides is going to look literally the same for each frontend. This means that the same import statements work everywhere (just import scocca) and that the frontend used is chosen based on context (ie. it uses Blender frontend when ran inside Blender). It should be possible to choose frontend explicitly as well by either stating that in the script or by using a command line parameter.
  • The element coordinate system is going to be rewritten to use left/right/top/bottom + width/height combination system. Old x and y will still work, though. The idea is that needed layouts may be constructed based on this system. So for instance to produce a horizontal layout you might do something like "foo.left = bar.right" and "foo" object will be on the right side of "bar". It should be possible to provide offsets and combinations too. Furthermore the system should provide means to use relative values.
  • The event system has been redesigned to allow more natural way of binding, and invoking, events. I will cover this in the next code example:
from scocca import Application, keyboard, mouse, press, quit

app = Application()

@press(mouse, 'left')
def say_foo():
print 'foo'

# print 'hello you' when k is pressed. note parameter passing mechanism
@press(keyboard, 'k', parameters=('you', ))
def hello(name):
print 'hello ' + name

# it's possible to call handlers bound to some event in the following way
mouse.press('left') # prints 'foo'

keyboard.press['esc'] = quit # generic quit, implemented per frontend

app.run()
Note that the binding syntax looks identical to the syntax used in the case of nodes (ie. label.drag = handler). I still might drop that app prefix and stick to something like "keyboard.press" for the sake of clarity. I also need to add a way to use modifiers (ie. app.mouse.press('left', modifiers=('ctrl', 'alt', ))) and semantics for drop.

I already have a simple version of "drag" working for the Blender frontend. An element becomes draggable if handlers have been attached to its "drag" event. Simple as that. While the event is being dragged the handlers will be triggered of course. The cool thing about the current implementation is that it works even with native Blender user interface elements. So you could potentially build a user interface designer or something similar without any significant effort.

The next step is to implement semantics for "accept". The idea is that if an element is dragged on an element and dropped on it, the handlers attached to "accept" trigger and receive the dropped element. Then they may operate based on it.

I also have to figure out possible additional semantics for "drag". It might be useful to be able to constraint it somehow (ie. constraint it on some predefined axis, path, bounds etc.). Perhaps the handlers attached to "drag" should be able to mutate the coordinates of the element on the fly.

I just wrote an initial frontend of Scocca for PyQT. Some examples work already both in Blender and PyQT by using the same code. I will drop PyOpenGL frontend and focus on PyQT instead as this way I won't have to implement loads of user interface elements. PyQT has proven to be nice enough to work with so far.

In related news I have began to prototype an image manipulation library (or rather an adapter) to use in Scocca. The project site and some initial code may be found at https://launchpad.net/pyima . Once Scocca is ready enough I will concentrate hooking up pyima with it.

[edit]
Updated the code example to reflect the current situation. I removed the app prefix. Now devices may be accessed directly.
[/edit]

[edit2]
Changed the example to use neater decorator syntax. I prefer this to old one as in this solutions bindings happen in the order they are read.
[/edit2]