Sugar API Documentation
“Sugar” is a new python programming interface for eStudio created by Clever Plugs in collaboration with Brainstorm, the makers of eStudio.
History
Up until very recently, the only way to program ipf was with the function itemset and itemget (together with itemnew, itemgo, itemcurve, etc.). While there is no limit to how much you can theoretically do with these functions, using them can get a bit longwinded. And, certainly if you recruit a python programmer, or learn some python, there is no synergy between python and these functions.
Both Brainstorm (specifically Jorge!) and Clever Plugs had the feeling a better interface was possible – one with some ‘syntactic sugar’. Jorge worked on a first version of this ‘sugar’ interface, and Cleverplugs meanwhile had independently created similar features in a framework during their work for Cheerful Scout.
Sugar2 is a ground-up creation based on Jorge’s concepts, making a programming interface to ipf (estudio) that is very “pythonic”, “object-oriented” and can be quite concise. The interface has been created “test-first”, meaning that there are full unit and regression tests for all its functions, and interestingly, there is also a version (a ‘mock’ or a ‘stub’) of sugar that can load outside of ipf (for testing scripts, for example).
Requirements and Installation
If you are running v11, you need to have a separate Python installation (not use the included python) and have set appropriate environment variables (reference needed to brainstorm site).
You need the sugar bundle, which is available as a zip archive
Download the bundle and put it somewhere (typically next to your db loader).
You need to create a new custom file which runs (PreLoad) sugar_loader.py.
Don’t move sugar_loader.py!
There is a useful sugar code editor (supporting drag and drop) implemented as an ipf folder (sugar_code_folder.py)
- this can be loaded (after sugar) as a separate custom file.
For simplicity when testing, you may want to use the alternative sugar_loader_with_stuff.py (instead of sugar_loader.py)
as this automatically creates a few visible GFX objs and opens the sugar_code_folder for you.
You can load this as if it was a database (on the command line after estudio).
First concepts
The basic way of accessing ipf is through the lists. There is an item called lists already available within ipf.
You can iterate through all the lists:
for l in lists: print l
This should give you an output including one for the obj list
lists.obj
Using the lists.obj, you can create new objects:
lists.obj.new() lists.obj.new()
You can also grab the created item and assign it to a variable:
o = lists.obj.new() print o
This should print something like
lists.obj['<dbs1>obj3']
You can then print all the objects you have created:
for o in lists.obj: print o
lists.obj['<dbs1>obj1'] lists.obj['<dbs1>obj2'] lists.obj['<dbs1>obj3']
These print-outs give an indication of how to access items. Now let’s go into more detail.
Creating items
When you create items you can create them with or without a name (and leave the naming to be the default name), and you can set values for one or more of their editors:
lists.obj.new('my new item')
lists.obj.new()
lists.obj.new('my displaced new item', displacement=(0,1,33), cull_tog=True)
lists.obj.new(displacement=(0,1,33))
Accessing items
You can access items by name, by order (the first one in the list is at index 0 – an old programming (including python) convention), and by ‘iteration’
print lists.obj['somename'] print lists.obj[0] for o in lists.obj: print o.displacement
If your item is in the current database you can leave out the database name (but you can optionally include it). If it is in a different database, or in the globals, you need to specify the database as part of the name.
lists.obj["obj1"] #same as next lists.obj["<dbs1>obj1"] lists.obj["<dbs2>obj1"] # different! lists.obj["<>obj1"] # different!
Setting and getting editors
Given an item, you can set an editor on it, either at creation or later on
lists.obj.new('something', dispacement=(0,1,2))
o = lists.obj.new('something')
o.displacement = (0,1,2)
lists.obj['something'].displacement = (0,1,2)
Strictly speaking (and for maximum speed) you should actually use the method “set”:
o.displacement.set((0,1,2)) lists.obj['something'].displacement.set((0,1,2))
However sugar can usually infer the set for you.
The opposite of set() is get() – and again sugar can do a good job of guessing what you mean, but this occasionally can become a problem.
print lists.obj['obj1'].displacement.get() print lists.obj['obj1'].displacement filename = lists.obj['obj1'].prim.get().recglo_mat.get().selsurf.get().file.get() filename = lists.obj['obj1'].prim.recglo_mat.selsurf.file.get()
Making actions and icons
To make actions, you can do the following, this is the equivalent of an “itemgo”:
o.displacement.makeAction((0,1,3), delay=0.5, transition=2).run()
You can separate them out:
action = o.displacement.makeAction((0,1,3), delay=0.5, transition=2) action.run()
and for example keep them around so that you can do
action = o.displacement.makeAction((0,1,3), auto_delete=False, delay=0.5, transition=2)
action.run()
then, later
action.goto = 0
You can also create icons:
o.displacement.makeIcon()
By default this makes an IconEditor. If you want to make an IconCode (a button), you can do that:
o.displacement.makeIcon(value=(0,0,0), type=ipfconstants.ICON_TYPES_ICONCODE)
If you don’t provide a value, it will use the current value of the editor
Walking up and down the tree of parents and children
o = lists.obj.new('obj1')
c = lists.obj.new('obj1/child1')
g = lists.obj.new('obj1/child1/grandchild1')
lists.obj.new('obj1/child1/grandchild2')
lists.obj.new('obj1/child2')
lists.obj.new('obj1/child3')
You can “iterate” through them
for child in o.children: print child
You can access them by an index
print o.children[0]
Te parent editor is also useful
print g.parent.parent.get() print g.parent.parent.parent.get()
You can use them to rearrange lists
g.parent = o for child in o.children: print child o.children = [g, c] for child in o.children: print child
LSel, LString, LBell editors
LSel, LString and LBell editors support standard python len (length), iteration, and indexed access (but not assignment):
text = lists.text.new() text.feeder_list = ["one", "two"] print text.feeder_list[0] # ==> one for s in text.feeder_list: print s # ==> one... two print len(text.feeder_list) #==> 2 #works similarly for LSel (like children) and LBell editors
Editors’ names
You can predict the name of an editor:
Some editors have odd names, that either don’t match the rules (e.g. FVEL in the pivot list doesn’t start with PIVOT (not “PIVOT_FVEL”). Other editors have names that would yield illegal python names, e.g. 3dcursor (starts with a number, in text list).
For these, you can just use the full name of the editor IN CAPITALS, e.g.
lists.pivot['pivot1'].FVEL.get() lists.text['text1'].TEXT_3DCURSOR.get()
For a del editor like OBJ_DEL, there’s a special case for this which is preferable:
pre(code).lists.obj[‘obj1’].DEL()
Easy access
You can easily get at the list item for a list:
lists.obj._listitem #==> lists.list['<>obj'] lists.obj._listitem.show = True lists.obj['obj1']._list._listitem
You can also do the reverse, but this is an advanced feature, mostly used in plugin programming
lists['<>obj']._sugarlist['obj1']
Usefully, you can also iterate across all the editor items, or a specific editor item (i.e. a member of the editors list) for a list:
for editor in lists.obj._editors: print editor, editor.type
print lists.editor["<>OBJ_DISPLACEMENT"] print lists.obj._editors.displacement #same thing
You can also get the instance to the editor item via a specific item in the original list:
print lists.obj['obj1'].displacement._editor lists.obj['obj1'].displacement._editor.show() lists.obj['obj1'].displacement._editor.managed = False # same as print lists.obj._editors.displacement lists.obj._editors.displacement.show() lists.obj._editors.displacement.managed = False
Values and callbacks
Sometimes (particularly when writing new lists (plugins) for eStudio , but occasionally with normal ipf)
you want to set a value for an editor without doing any consequent changes – the “callbacks” of a plugin.
In the itemset/get API, you could modify the name of the editors by adding “~” or “!” (no callback, always callback),
but in sugar it is more clear, we believe:
#to set a value without a callback: lists.obj['obj1'].displacement._value = (0,0,1)
#to run the callback without changing the value: lists.obj['obj1'].displacement._callback()
Future possibilities
not yet working!
Optimization for real-time speed
There is very little optimization of the code currently, and it is probably only appropriate for authoring. We should really be doing appropriate caching. However to do this effectively we need to use item ids rather than item names, which in itself is likely to do a speedup. This will require ctypes.
Indexed assignment and retrieval
o.displacement[2] = 0 print o.displacement[2]
Currently there’s no way to do indexed assignment, for retrieval you have to use get:
print o.displacement.get()[2]
A finder function:
for o in lists.obj.find(displacement=(1,2,0)): print o
A comparator function?
print o1.diff(o2)
Differences between lists.obj['obj1'] and lists.obj['ob2']:
{
displacement: ( (0,0,0), (0,1,2)),
size: ( (1,1,1), (5,5,5))
}
An equals function?
if o.displacement.equals((0,0,0)): print "no displacement" if o.displacement.equals(o2.displacement): print "o and o2 have same displacement"
Currently you have to do:
if o.displacement.get() == (0,0,0): print "no displacement" if o.displacement.get() == o2.displacement.get(): print "o and o2 have same displacement"
Advanced stuff: see advanced
