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

No comments: