Showing posts with label scocca. Show all posts
Showing posts with label scocca. Show all posts

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]

Friday, January 30, 2009

Scocca - Blender frontend testable, more examples

One of the best ways to drive development of an user interface library is of course to do something concrete with it. Last weekend I implemented a simple version of Pong just to see how the system scales for it. This resulted in quite a few improvements. I also spent some time working on Blender frontend and porting examples to it. Note that most of those don't work as expected as the layout and style systems need to be revised. Using absolute coordinates to determine user interface node locations should not be a problem, however. Also position attribute works. Using position you can quickly float nodes to different parts of its parent using combinations of "left", "right", "top", "bottom" and "center" keywords (ie. "bottomcenter" would align the node just above the bottom center border of parent).

This morning I ported an example found at Blender's Python API documentation to use Scocca instead. The original code looks like this:

import Blender
from Blender import Draw, BGL

mystring = ""
mymsg = ""
toggle = 0

def event(evt, val): # the function to handle input events
global mystring, mymsg

if not val: # val = 0: it's a key/mbutton release
if evt in [Draw.LEFTMOUSE, Draw.MIDDLEMOUSE, Draw.RIGHTMOUSE]:
mymsg = "You released a mouse button."
Draw.Redraw(1)
return

if evt == Draw.ESCKEY:
Draw.Exit() # exit when user presses ESC
return

elif Draw.AKEY <= evt <= Draw.ZKEY: mystring += chr(evt)
elif evt == Draw.SPACEKEY: mystring += ' '
elif evt == Draw.BACKSPACEKEY and len(mystring):
mystring = mystring[:-1]
else: return # no need to redraw if nothing changed

Draw.Redraw(1)

def button_event(evt): # the function to handle Draw Button events
global mymsg, toggle
if evt == 1:
mymsg = "You pressed the toggle button."
toggle = 1 - toggle
Draw.Redraw(1)

def gui(): # the function to draw the screen
global mystring, mymsg, toggle
if len(mystring) > 90: mystring = ""
BGL.glClearColor(0,0,1,1)
BGL.glClear(BGL.GL_COLOR_BUFFER_BIT)
BGL.glColor3f(1,1,1)
Draw.Toggle("Toggle", 1, 10, 10, 55, 20, toggle,"A toggle button")
BGL.glRasterPos2i(72, 16)
if toggle: toggle_state = "down"
else: toggle_state = "up"
Draw.Text("The toggle button is %s." % toggle_state, "small")
BGL.glRasterPos2i(10, 230)
Draw.Text("Type letters from a to z, ESC to leave.")
BGL.glRasterPos2i(20, 200)
Draw.Text(mystring)
BGL.glColor3f(1,0.4,0.3)
BGL.glRasterPos2i(340, 70)
Draw.Text(mymsg, "tiny")

Draw.Register(gui, event, button_event) # registering the 3 callbacks



After porting it to use my system it looks like this (note that I did not care about the layout, just functionality):

# -*- coding: utf-8 -*-
from Blender import Draw
from scocca.frontend.blender.elements import Label, ToggleButton
from scocca.frontend.blender.window import Window

# TODO: add size for Label (at the best case it would be totally dynamic!) and use PyFTGL for rendering?

window = Window(bg_color='blue')

toggle_status_label = Label(parent=window,
x=72, y=0, color='orange')

mouse_status_label = Label(parent=window,
x=340, y=70, color='white')

help_text = Label(parent=window, x=10, y=200, color='white')
help_text.label = 'Type letters from a to z, ESC to leave'

my_string_text = Label(parent=window, x=20, y=230, color='white')

toggle = ToggleButton(parent=window, label='Toggle',
x=10, y=10, width=55, height=20,
tooltip='A toggle button')

def toggle_pressed():
toggle_state = 'down' if toggle.value else 'up'
toggle_status_label.label = 'The toggle button is %s.' % toggle_state
toggle.press(toggle_pressed)

def add_to_my_string():
my_string_text.label += window.event_manager.last_pressed_key # XXX: ok???
window.bind_hotkey('any_character_key', 'press', add_to_my_string)

def add_space():
my_string_text.label += ' '
window.bind_hotkey('space', 'press', add_space)

def remove_from_my_string():
my_string_text.label = my_string_text.label[:-1]
window.bind_hotkey('backspace', 'press', remove_from_my_string)

def released_mouse_button():
mouse_status_label.label = 'You released a mouse button.'
window.bind_hotkey('any_mouse_button', 'release', released_mouse_button)

def pressed_mouse_button():
mouse_status_label.label = 'You pressed a mouse button.'
window.bind_hotkey('any_mouse_button', 'press', pressed_mouse_button)

window.bind_hotkey('esc', 'press', Draw.Exit)

window.run()



There are still some issues to fix in my version but even as it is it should give some idea of where the differences lie in. Take a good look at the abstraction level and code flow. If you have any ideas how to tweak it further, let me know.

The Blender frontend is pretty much testable. It has some rough spots (font rendering does not scale, missing some wrapping (all menus ie.), layout/style system isn't nice) but that should not stop you from building something simple with it.

EDIT!
Note that I got rid of a couple of now redundant lines (color definitions) in above example. This is because I integrated GrapeFruit color module in Scocca.

Friday, January 23, 2009

Scocca - a new way to define user interface, event system refresh

It can be useful to define user interface just by providing a description just like I have done so far. It can be cumbersome to create one for each script, however. An alternative way to define a user interface is to do it purely in code form. This is what I have been working on since the last post. The idea was that this would make it easier to prototype the style system but I haven't gotten that far yet. Besides this I pretty much rewrote the event system. It's still work in progress and there are some details to finish before it is good enough for basic use.

Next I will provide two of examples to show how the new system can be used. First example will show how to code a simple "Hello world!" application. Latter shows how to create a color picker application.

The code for simplistic "Hello world!" may look like this:

# -*- coding: utf-8 -*-
from scocca.frontend.pyopengl.elements import Label
from scocca.frontend.pyopengl.window import Window

window = Window(width=400, height=200, label='Hello test')

label = Label(parent=window, label='Hello, world!',
width=100, height=20, position='center')

window.run()


There are two important things going on at the code. First of all a Window is created. It is possible to provide it a variety of attributes including the width, height and label of the window. Label, as mentioned in an earlier post, means the visible text part of the window that appears on the top of it. Another important thing to note is how the Label element displaying the "Hello, world!" text has been defined. The most important part of to note is that window is provided as parent for the label so it can be bound to the window and hence displayed. It can also be positioned simply by using position attribute or explicit x and y coordinates (x=20, y=50 for example).

Color picker is slightly more complicated as shown by the code below:

# -*- coding: utf-8 -*-
import sys
from scocca.frontend.pyopengl.elements import Label
from scocca.frontend.pyopengl.window import Window
from scocca.utils.color import pick_color_at_current_cursor_location

window = Window(width=800, height=100, position='topleft',
label='Press "k" to pick color at current location of cursor')

label = Label(parent=window, label='', width=200, height=20,
position='centerleft')

def pick_color(window):
window.bg_color = pick_color_at_current_cursor_location()
label.label = str(window.bg_color)
label.color = map(lambda x: 1.0 - x, window.bg_color)
window.bind_hotkey('k', 'press', pick_color)

def quit_script(window):
sys.exit()
window.bind_hotkey('q', 'press', quit_script)

window.run()


The same ideas as in "Hello, world!" can be seen in this example also. As an addition it contains hotkey bindings. In this case the color picker has been designed so that the user presses 'k' key to capture color under the current location of the mouse cursor. After hitting k the color will be assigned as the background color of the window. Furthermore the color code is displayed in a label. Hopefully the code speaks for itself. Note that the idea of the map function is to invert the color of the label text. There should be a specific function to do this. I have not written an architecture to manipulate colors properly yet though.

The binding idea works more generally as well. Basically it is possible to bind events to nodes in following ways for instance: node.mouse_drag(drag_handler), node.press(press_handler). The central ideas in node events are based on two simple loops of events that go as follows: press-drag-release, enter-over-exit. Drag and latter loop deal with location of the mouse cursor. The difference between drag and over is that in case of drag it expects that certain mouse button is pressed (left mouse button by default). Also drag and over events get triggered each time mouse cursor moves (perhaps I will change this to redraw or add a timer to handle cases in which mouse stays still). This particular part of the system is still under the flux a bit but I think it's the way to go. At least the syntax seems simple enough.

In addition to work made above I also fixed the Blender frontend to work with a simple "Hello, world!" example. Fortunately it wasn't much work. The code of "Hello, world!" is pretty much the same as in case of PyOpenGL frontend except for the imports. In theory it should be possible to port applications written in different frontends just by changing import statements a bit as long as all needed user interface elements and parts of the frontend have been implemented. :) I hope to restore Blender frontend fully along with the old way of defining user interface as soon as possible (perhaps during this weekend even).

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. :)