Field's boxes contain code which can be executed. Of course, code can be directed executed in the editor, just as you are writing it, usually using keyboard shortcuts and so on. But there are a host of other ways — executing the code that you are writing, over and over again, as you redraft it is really what Field is all about.
So for completeness we'll list all of the ways that code can be executed here. Our best advice is that you don't try to memorize this list, but try to get a few shortcuts under your fingers.
Inside the editor: The most common way of running things. command-return — executes the current line or selection, command-alt-return — executes the current line or selection asynchronously inside a generator environment (see below); command-0 — executes all the code that you are editing (this is equivelent to command-A (for select-all) followed by command-return. command-n — executes the marked block that the cursor is inside that start's with the number n. command_-alt-leftarrow — executes the left-most area that the cursor's in inside the execution ruler (see TextEditor), command_-alt-rightarrow the right-most. You can make an area the right-most with the contextual menu. From there you'll also be able to can assign a keyboard shortcut (starting with command_-control).
Option-clicking on: The second-most common way of running things. inside a visual elements frame runs the visual element. You'll get an option to allow the box to keep running even when you release the mouse button (with practice this becomes a seamless gesture). See OptionClicking for a tutorial. Other places where the option-click executes something: option-clicking on an area in the execution ruler executes that area; and option-clicking on the top of a text-transform block executes that block (having been transformed of course).
command-PgUp /command-PgDown is just like option clicking on and off the thing that you are currently editing in the text editor, only without having to take your hands off of the keyboard.
Programmatically: If ve
is a variable that refers to some visual element (for example ve = _self.find["something"]
) then you can just "call" the visual element like you'd call a function in python ve()
to execute all the code inside it. Further, you can write ve.begin()
and ve.end()
to simulate option-clicking running on an element (begin
is the 'down' part of option clicking, and end
is the 'up' part). This way boxes can choreograph the running of other boxes (see TopologyAndControlFlow for an extended example). As an added bonus, someElement("1")
executes only the area numbered '1' (see TextEditor and EmbeddingGui for more on labelled blocks).
Automatically: Code inside the property 'Auto Executed", editable from the text editor, gets automatically executed when the sheet is open. Note that this can be a good spot to stick a _self.begin()
if you want your box to start running like it has been option-clicked.
With a keyboard shortcut: Assigning text to the property keyboardShortcut
will give you a box that .begin()
s when you push that key (in the canvas) and .end()
s when you push shift-that-key. So _self.keyboardShortcut="a"
gives you an command-a
/ command-shift-a
toggle for this element.
With the default Time Slider: Time Sliders that cross boxes run them while they intersect (or run them for one update cycle if they hop over them completely). You can drag these sliders around by the mouse, using the ScrubberPlugin, or by assigning keyboard shortcuts to Time Markers (see TheCanvas), or by manipulating their position programatically (_self.timeSlider.frame.x=10
) or by manipulating the "time system" that's connected to them (now.value=10
see TheCanvas). Finally, we should mention that you can create new time sliders that execute only portions of the sheet, and only then when specific code tells them to update — see SubTimeSliders.
With your own time slider: You can make more time sliders, ones that are "local" to a subselection of boxes:
updater = makeLocalSlider("mySlider",0.1, lambda : _self.subelements)
This makes a time slider that only runs the visual elements that are children of this box. updater
as a few useful methods on it:
updater.update()
# actually run anything that needs starting, updating or finishing.
updater.moveTo(_self.frame.x+_self.frame.w*0.2)
# move to 20% of the way across this box.
updater.stopAll()
# forcibly "stop" everything thats ongoing (see below)
updater.begin()
# just go update by yourself (no need to call updater.update() any more)
updater.end()
# stop updating by yourself (you now need to call updater.updater() to see any code executed)
In the above list of ways of executing code, you'll see we've been a bit sneaky: we've distinguished between "executing" code and "running" it. Executing code is just what you'd expect: the code gets parceled up and given to Python and the Python interpreter is told to do it's thing and return control back to Field.
When placing elements in a timeline, or option-clicking on them in the TheCanvas, it turns out that it's useful to have a little more structure to govern what it means to "execute" the code in an element, especially when it comes to situations where code is going to get "executed" over and over again in consecutive update cycles.
So in Field we define a special variable _r
that's the "return value" of executing a visual element and interpret that according to the rules set out below to work out what it means to keep on "running" this element.
(During this description keep your minds eye the vision of a Time Slider crossing over a visual element — starting it's execution (as it crosses over the front of the visual elements "frame"); continuing it, (as it continues to intersect it) and finally stopping (as it leaves the visual element). Such a life-cycle also corresponds to option-clicking on an element: option-mouse-down (start), hold (ongoing), option-mouse-up (stop).)
What can _r be? Here are the basics:
_r = someFunction
— run this function while this visual element is ongoing. What this function returns is irrelevant, unless it returns a generator (see below), in which case, the generator is run until completion and then restarted._r = someGenerator()
— run this generator while this visual element is ongoing and while this generator has got something to return. Generators in Field are executed inside a Generator Stack. This means that should a generator return another generator, this generator is executed in full before controll passes to the original generator. This allows quite sophisticated composition of generator routines, all while being interleaved with graphics and UI updates, and all while avoiding the pitfalls of multi-threading. Note the difference between someGenerator
and someGenerator()
. The first is actually a function that when called, returns a generator._r = (start, ongoing, stop)
— this is a 3-tuple of things that appear elsewhere in this list, typically functions, but they could be other things. In the case of functions, start
is called once, ongoing
while the box is active, and stop
is the last thing that called just before the element becomes inactive. If start
, ongoing
or stop
isn't a function the intention is to do the "expected thing" — i.e. the thing that allows routines to be composed in a well ordered fashion. For example the tuple of tuples ( (a,b,c), (d,e,f), (g,h,i) )
means to call (a,b,c)
at the start, d
then e
and then just e
while ongoing and then finally f, g, h, i
at the stop. _r (start, someGenerator(), stop)
is quite common._r = 1
— if _r
is set to a non-zero number, then Field will keep evaluating the box over and over again until it's set to something else (either 0
or one of the things below. Note, this can be quite slow — you are asking Field to re-parse the executing property over and over again. Much better to use one of the other things above.In any case, to be as concrete as possible with the typical case:
print "hello field"
def start():
print "start"
def ongoing():
print "ongoing %s" % _t
def end():
print "end"
r = (start, ongoing, end)
prints, as a time slider sweeps over the box, something like:
hello field
start
ongoing 0.1
ongoing 0.2
ongoing 0.3
ongoing 0.4
ongoing 0.5
ongoing 0.6
ongoing 0.7
ongoing 0.8
ongoing 0.9
end
Finally, while executing there are a couple more special variables set:
* _t
— set to a number that indicates the "time" that's being executed: 0
means the left edge of the visual element frame, 1
means the right. Note you can get values outside this range (in particular stop is called with the time that the marker ends up at, which might be >1, and when using the mouse, the x position of the mouse is effectively the position of the timeline).
* _x
— set to a number that indicates the the "raw" x-position of that slider — if you are option-clicking on a visual element to run it, this is the mouse position, in sheet coordinates. If this is the case, you'll also have a _y
set as well.
* _backwards
— set to 1 to indicate that the timeline is going the "other way": that is, we've started because the time line has entered the right edge of the frame.
* _jump
— set to 1 if the timeline has jumped completely over us. In this case you should expect a "call" to "start" and "stop" but never any ongoing, but you can use the presence of this variable to do something different — or perhaps nothing at all — in this case.
So to repeat the main point, using the concrete example above. To execute the box containing this code would simply print "hello field" and define some functions. To run the box containing this code (via-option click, time slider drag, .begin()
and so on) would print "hello field", define some functions and then tell Field to start with the function start
, keep going with the function ongoing
and end by calling the function stop
.