Download and run luban examples
Convenient scripts come with luban installation so you can easily download and run luban examples.
For example, if you want to install and run luban example project “aokuang”, run
$ cd <somewhere-in-your-home-directory>
$ download-luban-project.py aokuang
$ start-luban-project.py aokuang
A browser window will be opened to show the web application aokuang.
Available example projects are:
To stop a luban project from running:
$ stop-luban-project.py <path-to-the-project>
Create my first luban application from command line
Note
You could create and manage your luban project visually using application
Gongshuzi, which is still experimental,
however.
Luban project creator script
You can create a template luban project by using the script “create-luban-project.py”.
To create your own luban application, do the following
$ cd /anywhere/you/like
$ create-luban-project.py
This will create a project named “helloworld”. To create a luban project with a name
of your choice, do
$ create-luban-project.py <project name>
Now you can start your project,
$ start-luban-project.py helloworld
or
$ start-luban-project.py <project name>
Now a browser window should be open with your
first luban web application
.
You should see a page that is titled “Hello world”.
You can also see the wx application running by
$ cd /path/to/helloworld/bin
$ wxmain
Directory structure
Now let us take a look at the structure of our luban project.
There are a few subdirectories in directory “helloworld”.
- config: configuration files. Most of them are xml files (ending with .pml). They are useful for fine tune the behavior of various components that constitute your luban application.
- bin: executables specific for this application. For example, some applications need intialization scripts or services (daemons) to be run before the application can work correctly.
- log: logging files. You will look for debugging information here.
- html, cgi-bin: specific for web application. You can fine tune your css style sheets to improve the visual appearance of your interface here.
- helloworld: the python source tree for the helloworld project. This will, for example, contain data models for your application.
- content: this is the main directory that gives your luban user interface functionalities.
At your first attempt to build a luban application, only the directory “content”
need to be touched.
In directory “content”, there are following subdirectories:
- images: images to be used in your user interface
- components: the user interface components
- actors: components that handles user interactions
- visuals: components that build the ui elements/hierarchies
Actor
Actor is the component that responds to user requests. In web applications, a method of an actor can be
accessed through a url that looks like
http://web.site/main.py?actor=<actor_name>&routine=<routine_name>
For example,
http://web.site/main.py?actor=helloworld&routine=default
will call the method “default” of actor “helloworld”.
In WX applications, a command line
$ wxmain.py --actor=helloworld --routine=default
will call the same method.
Now let us look at our helloworld actor (which can be found in
helloworld/content/components/actors/helloworld.odb)
def actor():
from luban.components.Actor import Actor as base
class Actor(base):
def default(self, director):
return director.retrieveVisual('helloworld')
return Actor('helloworld')
An actor file must have extension ”.odb”, and it is a python source file.
It must define a method “actor”, which takes no arguments and returns
an instance of a class derived from luban.component.Actor.Actor.
Any method of the derived Actor class that takes a “director” argument
can be invoked through a web link or a python command line.
In this helloworld example, the default method just simply returns a
visual:
return director.retrieveVisual('helloworld')
Please refer to retrieveVisual.
Visual
This the code for the “helloworld” visual
(helloworld/content/components/visuals/helloworld.odb):
def visual(director):
import luban.content
frame = luban.content.frame(title='test frame')
doc = frame.document(title='Hello world!')
doc.paragraph().text = ['This is a test.']
return frame
It defines a method “visual” which takes an argument “director”.
The body of the “visual method creates a “frame”, and
then adds a “document” inside the frame, and then adds
a “paragraph” inside the document.
Create a simple function plotter web service in few minutes
The goal of this tutorial is to create a web service to plot
a function y=f(x) configurable by user.
start up
Let us start by create a new luban project:
$ cd <somewhere>
$ create-luban-project.py plotfx
This creates a directory “plotfx” and several sub directories.
You could see it running by
$ start-luban-project.py plotfx
A browser should pop up with url http://localhost:8801/cgi-bin/main.py
Sin Function
Now let us create a data object represent the function y=sin(a*x+b).
Please use your favorite editor to create python source
plotfx/plotfx/Sin.py:
import numpy
class Sin(object):
a = 1.0
b = 0.0
def __call__(self, x):
a = self.a
b = self.b
return numpy.sin(a*x+b)
It has two parameters a and b, and a method __call__ to compute
orm for sin function
Here, we use the magic of orm in luban to turn the data object to a
form. Please run
mkdir -p plotfx/content/components/actors/orm
and create a file
plotfx/content/components/actors/orm/sin.odb:
from plotfx.Sin import Sin
import luban.orm
Actor = luban.orm.object2actor(Sin, needauthorization=False)
def actor(): return Actor('orm/sin')
Please point your browser to
http://localhost:8801/cgi-bin/main.py?actor=orm/sin&routine=debug_edit
, and you will see a form was created for the Sin data object.
And the form already knows that the input value has to be floating
point numbers: if you type in something other than that and submit,
an alert will show up.
CurveComputation
In order to plot a curve of the user specified function, we need a
curve computation that takes a function and a specification of the x
axis, and generates y points for each x points.
Please create file plotfx/plotfx/CurveComputation.py:
import numpy
from Sin import Sin
class CurveComputation(object):
function = Sin()
xmin = 0.0
xmax = 10
xstep = 0.1
def __call__(self):
xmin = self.xmin
xmax = self.xmax
xstep = self.xstep
x = numpy.arange(xmin, xmax, xstep)
f = self.function
return f(x)
and file
plotfx/content/components/actors/orm/curvecomputation.odb:
from plotfx.CurveComputation import CurveComputation
import luban.orm
Actor = luban.orm.object2actor(CurveComputation, needauthorization=False)
def actor(): return Actor('orm/curvecomputation')
Please point your browser to
http://localhost:8801/cgi-bin/main.py?actor=orm/curvecomputation&routine=debug_edit
you will see a form in which you can edit a computation, including the
sin function:
Main actor
We can now edit the main actor
plotfx/content/components/actors/plotfx.odb
to be:
# -*- python -*-
from luban.content import select, load, alert
import luban.content as lc
from luban.components.Actor import Actor as base
class Actor(base):
class Inventory(base.Inventory):
import pyre.inventory
id = pyre.inventory.str('id')
pass
def default(self, director):
"this is the main routine of this main actor"
# create a new computation
comp = CurveComputation()
# save it to db
orm = director.clerk.orm
orm.save(comp)
id = orm(comp).id
# load skeleton
frame = self._createSkeleton()
# fill the input container with the form of the computation
inputcontainer = frame.find(id='input-container')
inputcontainer.oncreate = select(element=inputcontainer).replaceContent(
load(actor='orm/curvecomputation', routine='edit', id=id)
)
# action of run button: create plot and add it to the output container
runbutton = frame.find(id='run-button')
runbutton.onclick = select(id='output-container').replaceContent(
load(actor=self.name, routine='createPlot', id=id)
)
return frame
def createPlot(self, director):
# load computation from db
id = self.inventory.id
orm = director.clerk.orm
comp = orm.load(CurveComputation, id)
# run the computation to get x, y
x, y = comp()
# create plot
plot = lc.plot2d()
plot.curve(x=list(x), y=list(y))
return plot
def _createSkeleton(self):
"create the skeleton of the main interface"
# the frame
frame = lc.frame()
# a splitter to split the space to left, middle, right
sp = lc.splitter(orientation='horizontal')
frame.add(sp)
# left: input
left = sp.section(Class='align-top')
left.document(title='Input', id='input-container')
# middle: run button
middle = sp.section()
button = lc.button(id='run-button', label='plot')
middle.add(button)
# right: output
right = sp.section(Class='align-top')
right.document(title='Output', id='output-container')
return frame
from plotfx.CurveComputation import CurveComputation
def actor():
return Actor("plotfx")
Then, if you point your browser to
http://localhost:8801/cgi-bin/main.py
you will see the web service. Click the “plot” button to see a plot of
sin wave:
You can change the parameters of the function and the x axis, and
click the “save” buttons for the function and the computation, and
click “plot” button again, you will see an updated plot. For example,
the following one:
Support more types of functions
We can make this application more flexible; it should be able to plot
various kinds of functions. To do this we first define an abstract
interface for functors (plotfx/plotfx/Functor.py):
class Functor(object):
def __call__(self, x):
raise NotImplementedError
And we modify the Sin functor to inherit from Functor
(plotfx/plotfx/Sin.py):
import numpy
from Functor import Functor
class Sin(Functor):
a = 1.0
b = 0.0
def __call__(self, x):
a = self.a
b = self.b
return numpy.sin(a*x+b)
and we create a new functor “exponential”
(plotfx/plotfx/Exponential.py):
import numpy
from Functor import Functor
class Exponential(Functor):
c = 1.0
def __call__(self, x):
c = self.c
return numpy.exp(c*x)
and create a orm actor for the new functor “exponential”
at plotfx/content/components/actors/orm/exponential.odb:
from plotfx.Exponential import Exponential
import luban.orm
Actor = luban.orm.object2actor(Exponential, needauthorization=False)
def actor(): return Actor('orm/exponential')
Then we modify the CurveComputation class
(plotfx/plotfx/CurveComputation.py)
to
import numpy
from Functor import Functor
from Sin import Sin
from Exponential import Exponential
functor_types = [Sin, Exponential]
from dsaw.model.Inventory import Inventory as InvBase
class CurveComputation(object):
function = None
xmin = 0.0
xmax = 10
xstep = 0.1
class Inventory(InvBase):
function = InvBase.d.reference(
name='function',
targettype=None, targettypes=functor_types,
owned = 1)
xmin = InvBase.d.float(name='xmin', default=0.)
xmax = InvBase.d.float(name='xmax', default=10.)
xstep = InvBase.d.float(name='xstep', default=0.1)
def __call__(self):
xmin = self.xmin
xmax = self.xmax
xstep = self.xstep
x = numpy.arange(xmin, xmax, xstep)
f = self.function
return x, f(x)
Here we introduce descriptors to better describe the
data members of CurveComputation.
Especially important is the descriptor of “function” reference:
function = InvBase.d.reference(
name='function',
targettype=None, targettypes=functor_types,
owned = 1)
It tells luban that the reference “function” is owned by a
CurveComputation object.
The reference is polymorphic and the allowed types are
the list “functor_types”.
Now we are almost ready to see the more powerful application
running. Before that we need to remove the old database file
since the reference “function” has changed from a non-polymorphic
one to a polymorphic one:
$ rm plotfx/content/db.sqlite
Now we can point out browser to
http://localhost:8801/cgi-bin/main.py?actor=plotfx
again.
In the Function section you can now change the type of the function
you want to plot, and change parameters, and plot it: