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.

No comments: