Tuesday, April 30, 2013

Egui - EFL gui builder

* Assumed, reader knows how Evas/Elementary/Eo are used

Usually developer creates both user interface and application logic. If application wasn't properly designed, changes in interface can affect logic and vice versa.
We want to separate work of designer from developer, and provide opportunity to check how user interface looks like, even if logic is not still inside.
We are going to implement all main gui creator's functionality and in addition simulation.
Simulation can be used by gui designers, to check gui behavior without compilation and logic.
Also we are going to provide code export to different languages(C, Python, JavaScript).

Here I'm going to describe how we are going to do this.

The main idea is that we don't want to have separate project file, but we want to put "easy-to-write-by-hands" object's description into json metadata right into C source file's comments. (if we still want to have project file, we can put all jsons into separate file)

So part of our source file will look like this:

/**
* @egui
* {
*    "win": {
*          "parent" : null,
*          "public" : true,
*          "class" : "elm_win",
*          "constructor" : "elm_obj_win_constructor",
*          "elm_obj_win_constructor" : [null, "ELM_WIN_BASIC"],
*          "title_set" : "window!!!",
*          "autodel_set" : true,
*          "size_set" : [320, 320],
*          "visibility_set" : true,
*          "resize_object_add" : "bg"
*    }
* }
* @!egui
*/
Eo *win = eo_add_custom(ELM_OBJ_WIN_CLASS, NULL,
                       elm_obj_win_constructor(NULL, ELM_WIN_BASIC));
eo_do(win, evas_obj_size_set(320, 320));
eo_do(win, evas_obj_visibility_set(EINA_TRUE));
eo_do(win, elm_obj_win_title_set("window!!!"));
eo_do(win, elm_obj_win_autodel_set(EINA_TRUE));

/**
* @egui
* {
*    "but1" : {
*          "parent" : "win",
*          "public" : false,
*          "class" : "elm_button",
*          "constructor" : "eo_constructor",
*          "eo_constructor" : null,
*          "color_set" : [10, 255, 0, 255],
*          "visibility_set" : true,
*          "part_text_set" : [null, "button1"],
*          "size_set" : [100, 50],
*          "position_set" : [40, 20]
*       }
* }
* @!egui
*/

Eo *but1 = eo_add(ELM_OBJ_BUTTON_CLASS, win);
eo_do(but1, evas_obj_color_set(0, 250, 0, 255));
eo_do(but1, evas_obj_position_set(40, 20));
eo_do(but1, evas_obj_size_set(100, 50));
eo_do(but1, evas_obj_visibility_set(EINA_TRUE));
eo_do(but1, elm_wdg_part_text_set(NULL, "button1"));
 
In this example, "win" and "but1" are widgets, "parent", "public", "size_set" etc - are properties. You can see different properties names.
Some of them are shorter names of well-known evas/elementary functions.
Some other are used for internal builder needs.
As soon as there is such data, it's possible to parse json, save data and later simulate current gui behavior or generate sources in desired language.

How do we get such property names?
Gui Builder is based on Eo (EFL object oriented model).
Currently each Evas object or Elementary widget are implemented using Eo.
So widget's class name is provided in json.

During simulation and source code generation a proper function is fetched from
internal Egui's database according to property name and widget's class name.

What is internal Egui's database?
We have a script which searches for all data (functions, parameters, types, enums, etc)
of all classes in the provided paths. (all Elementary widgets, etc).
Only needed data(based on gui_db.in) will be selected and database for Egui will be generated.
(Script and "gui_db.in" currently are not in egui project)

Currently "gui_db.in" looks like this.
OPERATIONS =
["evas_obj_color", "set"] : "color_set" :
["evas_obj_size_set"] : "size_set" :
["evas_obj_position", "set"] : "position_set" :
["evas_obj_size_hint_weight", "set"] : "size_hint_weight_set" :
["evas_obj_visibility_set" : "visibility_set" :
["elm_obj_bg_color_set"] : "bg_color_set" :
["elm_obj_win_title_set"] : "title_set" :
["elm_obj_win", "autodel_set"] : "autodel_set" :
["elm_obj_win_resize_object_add"] : "resize_object_add" :
["elm_wdg_part_text_set"] : "part_text_set" :
["elm_obj_win_constructor"] : "" :
["elm_obj_container_content_set"] : "content_set" :
;

CLASSES =
elm_win  ,
elm_button,
elm_bg,
elm_label,
elm_bubble,
;
CLASSES - literally list of widgets which can be used in Egui. We will add all needed classes here.
OPERATIONS - list of tokens, which will be looked for in function names, and mapped into Egui property.
For example: ["evas_obj_color", "set] : "color_set" looks for every function,
which has parts "evas_obj_color" and "set" and stores it's data into hash.

In this case "evas_object_color_set" will be found in "Evas_Obj" class.
Data about this func will be stored in a hash with a key {"Evas_Obj", "color_set"}.

Another example:
Rule ["_multi_select_set"] : "multi"
will find two functions: "elm_obj_genlist_multi_select_set",  "elm_obj_gengrid_multi_select_set"
and store it into {"elm_genlist", "multi"} and {"elm_gengrid", "multi"} database records. So property "multi" can be used for both genlist and gengrid.
When simulating, this property will be dispatched correctrly as soon as
"multi" belongs to classes in separate hierarchies.

But name clashes in the same hierarchy should be avoided.
For example:
Suppose we will map "evas_obj_color_set" and "elm_obj_bg_color_set" into property "color_set".
Two database records will be created {"Evas_Obj", "color_set"} and {"elm_bg", "color_set"}. Dispatch will work correctly, because functions belong to different classes, but user will have to remember, that "color_set" in "elm_bg" widget receives three parameters and four in others. (To handle this, dispatch according to parameter number can be added in future).

Also, according to this database, widget's available properties will be displayed in "Property Inspector" of builder.

Code Generation
Following C source files can be generated for the described example. Header file, that declares structure which allows to get access to gui elements. Source file, that creates all widgets.
/**
***********************
** Generated gui.h file
************************
*/
#ifndef _gui_h
#define _gui_h
#include <Eo.h>

typedef struct
{
   Eo *win;
} GUI;

GUI* get_gui();

#endif

/**
***********************
** Generated gui.c file
************************
*/
#include <gui.h>
GUI* get_gui()
{
  Eo *win;
  Eo *but1;
  
  win = eo_add_custom(ELM_OBJ_WIN_CLASS, NULL, 
                      elm_obj_win_constructor(NULL, ELM_WIN_BASIC));
  but1 = eo_add(ELM_OBJ_BUTTON_CLASS, win);

  // all relevant calls of eo_do()

  // creation of external structure to provide access to widgets
  GUI *pub_widgets = (GUI*)calloc(1, sizeof(GUI));
  pub_widgets->win = win;
  return pub_widgets;
}

Trying Egui.
You will need different efl branch to do it.
> git clone git://git.enlightenment.org/core/efl.git
> cd efl
> git checkout -b eo_class_mro_get devs/yakov/eo_class_mro_get
Build and install this branch.
 
You can also rebase it on top of origin/master, if needed.
> git rebase origin/master
 
Checkout Egui:
> git clone git://git.enlightenment.org/devs/yakov/egui.git
 
Build:
> cd egui
> mkdir build
> cd build
> cmake ..
> make
Should be compiled without errors.
 
Run simulation:
> ./src/bin/egui -f ../examples/example.c -s

 
Generate source code:
  > ./src/bin/egui -f ../examples/example.c -g -o new
    - new.c and new.h will be generated in current dir
 
Run simulation on newly generated file:
> ./src/bin/egui -f new.c -s
 
Run kind of Egui main window :)
> ./src/bin/egui -f new.c
  press green button to open kind of project :)
 
Remember: sometimes, to close egui, you'll have to use "Ctrl+C" :)

We are also thinking how to name it. Maybe simply "egui", maybe "eline" (suggested by YossiK), you are welcome to suggest your own.

Waiting for your questions and comments. :)


Friday, December 21, 2012

Visitor Pattern in Python

Visitor Pattern, what is it?
In object-oriented programming and software engineering, the visitor design pattern is a way of separating an algorithm from an object structure on which it operates. A practical result of this separation is the ability to add new operations to existing object structures without modifying those structures.[1] 

When to use?
Visitor Pattern can be used when it's needed to call method, which depends on both types of two objects.
To implement this, we need to provide double dispatch mechanism to determine which function must be called.

Example 
https://github.com/yakov-g/py_visitor/blob/master/py_visitor.py
Suppose there are two types of objects: Text, Picture and three file formats: txt, jpg, blob. Each object can be saved in each file format, or error message should be printed.

In order to keep terminology used to describe Visitor Pattern in other sources, I will use names like: accept(), visit(), Visitor(); but remember, that for our example accept == save; visit == save, Visitor == Saver.

Double dispatch
We want to provide convenient way of saving objects in any format, so it could look like this:  o.save(v)
Both of our object classes must implement accept() (save) function, which can receive Visitors (Savers) of different types:

I will write it in pseudo python code, to show that function's parameter can be of any type:

class Text():
   data = "Text"
   def accept(self, v <txtVisitor> || v <jpgVisitor> || v <blobVisitor>):
      v.visit(self)

class_Picture():
  data = "Picture"
  def accept(self, v <txtVisitor> || v <jpgVisitor> || v <blobVisitor>):
      v.visit(self)

Now any object can call accept(), to save object in provided format.

From the other side, each Visitor class must implement visit() functions, which actually saves object in desired format.
So it must be set of functions, which receive object of certain type.

class txtVisitor():
   def visit(self, o <Text Object>):
      print "Saving Text object in txt format"
   def visit(self, o <Picture Object>):
      print "Can not save Picture in to txt format"

class jpgVisitor():
   def visit(self, o <Text Object>):
      print "Can not save Text in jpg format"
   def visit(self, o <Picture Object>):      

      print "Saving Picture in jpj format"
 
class jpgVisitor():
   def visit(self, o <Text Object>):
      print "Saving Text in blob format"
   def visit(self, o <Picture Object>):
      print "Saving Picture in blob format"


Now, if we would have list of different objects and list of visitors:

lst = [Text(), Picture()]
vst = [txtSaver(), jpgSaver(), blobSaver()]
for o in lst:
  for v in vst:
    o.accept(v)

First dispatch happens in o.accept(v):
  • accept function will be called according to type of object, t.e. Text or Picture, and Visitor will be passed;
  • inside of accept() func we don't know anything, about type of Visitor; we know type of object, because dispatch had already happen.
Second dispatch happens in v.visit(o):
  • visit() func will be called according to type of Visitor.
  • inside of visit(), types of both objects are known, so we actually save desired object in desired format.
Implementation in Python
As soon as there is no function overloading in Python, we can't declare several visit() funcs with different parameter's types.

We need provide another way to call needed visit() func. It can be done by defining several functions with different names according to type of object.

class txtVisitor():
   def visit_Text(self, o):
      print "Saving Text object in txt format"
   def visit_Picture(self, o):
      print "Can not save Picture in to txt format"

    etc for each Visitor class.

We will also implement visit() func, which will determine type of object and call proper method.
We'll do it in Visitor class, which will be parent for other Visitors:

class Visitor(object): #object is a base python class
   def visit(self, o):

      # class name is Text or Picture
      method_name = "visit_" + o.__class__.__name__
      
      # looking for attribute 'visit_ClassName', return False, if not found      
      method = getattr(self, method_name, False)                                                 
     

      # if there is no attribute 'visit_ClassName' in Visitor class
      # error message will be printed
      if not method:
         self.error_message(o)
         return
      if callable(method): # checking, if found attribute is func()
         method(o) # calling func
      else:
         print "%s is not callable attribute"%(method_name)

   def error_message(self, o):
      print "Error: '%s' can't save '%s'"%(self.__class__.__name__, o.data)


We also can move accept() out of object's classes and define it in base class like this:

class File(object):
   def accept(self, v):
      v.visit(self)

class Text(File):
   data = "Text"

class Picture(File):
   data = "Picture"


So dispatch actually happens here:
        method_name = "visit_" + o.__class__.__name__
and we need only one dispatch in Python.

Why do we need only one dispatch in Python?
As soon as Python works with objects, we don't need two dispatches.
We need only "first" one, where we determine type of object according to object's class names.
Call o.accept.(v) can be changed to v.visit(o), and accept() can be deleted.

But what if 'o' is not object, but pointer to base class 'File'?
(In case of C++: it's imposible to define array of elements with different types, we must define array of  pointers to base class: File *arr[]; i )
Without first dispatch, parameter of 'File' type will be passed into visit(o).
There is no visit() which accepts 'File' as argument. That's why first dispatch is needed.

You can find full example here:
   https://github.com/yakov-g/py_visitor/blob/master/py_visitor.py

References:
   [1] http://en.wikipedia.org/wiki/Visitor_pattern

External links:
   http://en.wikipedia.org/wiki/Double_dispatch
   http://pythonwise.blogspot.co.il/2006/06/visitor-design-pattern.html

I'll be happy to hear comments from you. :)