Going for Qt

You have probably read the post on the lua rewrite (Going for a Lua(jit) rewrite). Since everything is going to change, I took the opportunity to see if there was a framework out there that would be easier to use with Mimas.

What I need is an open source portable GUI framework that has scripting support so that remote devices can “publish” there interfaces with the scripting language.

I want the flexibility of html+css+js with a C++ backend (hard to meet needs) but I found the perfect framework.

The framework is Qt because:

  • it is LGPL
  • it works on mac/linux/windows
  • it’s a great C++ framework with solid support for signals and slots (easy to connect widgets to proxy)
  • and finally, it has QML, a Javascript variant that can be used to create interpreted user interfaces.

<edit> After some work on the new lua based Rubyk, I realize that I do not want to use too many languages to define interfaces and such, so I’ll probably use Lua for this too. It has the added advantage to make the interface code easily portable to other frameworks or devices (iPad with Wax for example). </edit>

Learning Qt

I started learning the framework and I am very impressed by the overall quality. The documentation is good, the examples are easy to understand, the template/macro usage is well worked out (I know it’s hard to keep this simple).

Things just work like you expect, with some nice surprises related to layout (scaling with windows and such).

GUI redesign

Now that the tools are in place, I need to redesign how all of this gets connected together. The “things” involved in this game are:

  • planet: process executing Rubyk (Lua)
  • satellite: graphical interface (Qt)
  • patch: stores a processing definition with parameters (Lua)
  • prototype: stores a prototype for a processing node (Lua)
  • view: a view contains widget organization and behavior to control a planet from a satellite (QML).

Rubyk files

prototype file

A prototype file contains Lua code to define a “class”. The file contains method definitions, class variables and default values.

Example:

node:outlet('output')

node:inlet('input').call = function(self, val)
  if val then
    print(string.format("[%s] %s <--- '%s'", self.name, 'input', val))
    -- receiving value
    self.val = val
    -- can we notify without explicitly adding this line ?
    self:notify('input', val)
    -- prepend name and send to outlet
    self:send('output', self.name .. ': ' .. val)
  end
  return self.val
end

-- other way to declare the same (without the send)
node:inlet('input2')

Note that all data needs to be stored in “self” since the file definition is shared among all instances of this “class”. Any global variable is shared with all instances.

patch file (processing definition/settings)

A patch contains code to instanciate prototypes, setup links and change parameters.

Example:

rk.set('a', {class='proto.lua', name='A'})
rk.set('b', {class='proto.lua', name='B'})
-- links
rk.connect('a', 'output', 'b', 'input')

Note that the “set” operation updates a node if it exists or creates a new node if the given name does not exist. With some tricks in the _G environment table, a setup file can be used without changes as a new prototype (this enables sub-patch abstraction).

reload

When running a patch or prototype file, we need to “update” existing objects instead of recreating them so that links and state are preserved. Adding new objects or updating existing ones is easy, but how do we delete links/objects/methods ?

A solution could be to mark these elements as dirty before the script reload and remove them if they are still dirty (not touched) after script execution. In order to do this, we could use a new environment table on each reload and simply move objects from the old table to the new one on “create/update”. What is left in the old table should be deleted.

But how do we delete a node in Lua ? Since Lua uses garbage collection, we need to remove all references to the node. This means removing:

  1. links pointing to the node
  2. references in env tables (parent node, prototype)

And how do we update sub-patch links (sub-patch definitions do not have this problem since they are “live” through the metatable) ? For now, we consider that these elements are not updatable.

runtime Lua

prototype

In a script, the ‘node’ is a prototype created by Node.new. The prototype stores methods
and outlet/inlet definitions. The script is run in its own private environment. This means
that it is possible to create private methods but all state information should live inside
‘self’ or it will be shared across objects.

instance

An instance is made of an empty table with the prototype as metamethod. The instance also
contains an empty ‘outlets’ list with a special index method to create outlets as needed. In
order to create an outlet, the list searches the prototype’s outlet definitions.

Gaspard Bucher

comments

  1. leave a comment