Stage
A CeedStage
combines ceed.shapes
on the screen with a
FuncBase
or sequence of
FuncBase
which determines the intensity of the shapes as
time progresses in the experiment. This module defines the CeedStage
and associated classes used to store and compute the intensity values during
the experiment.
Stage factory and plugins
The StageFactoryBase
is a store of the defined CeedStage
sub-classes and customized stage instances. Stage classes/instances
registered with the StageFactoryBase
instance used by the
the GUI are available to the user in the GUI. During analysis, stages are
similarly registered with the StageFactoryBase
instance used in the
analysis and can then be used to get these stages. E.g.:
>>> # get a function and shape factory
>>> function_factory = FunctionFactoryBase(...)
>>> shape_factory = CeedPaintCanvasBehavior(...)
>>> # create the stage store, linking to the other factories
>>> stage_factory = StageFactoryBase(
function_factory=function_factory, shape_factory=shape_factory)
>>> register_all_stages(stage_factory) # register plugins
>>> StageCls = stage_factory.get('CeedStage') # get the class
>>> StageCls
<class 'ceed.stage.CeedStage'>
Classes can be registered manually with
StageFactoryBase.register()
or they can be registered automatically
with register_all_stages()
if they are an internal plugin or
register_external_stages()
for an external plugin. The GUI calls
register_all_stages()
when started as well as
register_external_stages()
if the
external_stage_plugin_package
configuration
variable contains a package name.
See ceed.stage.plugin
for details on writing plugins. For details on
how to customize stages, see below for important steps and methods during a
stage’s lifecycle.
To get a stage class registered with StageFactoryBase
, e.g. the
connonical CeedStage
base class:
>>> StageCls = stage_factory.get('CeedStage') # get the class
>>> StageCls
<class 'ceed.stage.CeedStage'>
Stage basics
All stages are instances of CeedStage
or any plugin-defined
sub-classes.
A stage is composed of one or more CeedStage.shapes
, a
series of ceed.function.FuncBase
functions
that
govern the intensity these shapes take across time, and
sub-CeedStage.stages
, that are simultanously evaluated during this
stage.
All stages have a CeedStage.name
, and stages that are available globally
through the StageFactoryBase
and shown as root stages in the GUI have
unique names. To run an experiment you select a named stage to run.
Stage duration
Ignoring sub-stages, a stage will sample trhough its CeedStage.functions
sequentally until they are all done, CeedStage.loop
times. After
CeedStage.loop
times, the stage is done. For example, a stage with
function f1
and f2
that CeedStage.loop`s 3 times, will tick
through the functions as follows: ``f1, f2, f1, f2, f1, f2`
. At each sample,
all the CeedStage.shapes
of the stage (and possibly sub-stages) are
set to the value in the [0, 1]
range returned by the functions for that
time step.
If there are child CeedStage.stages
that have their own
CeedStage.shapes
, we also sample these stages simultanously during each
root stage loop iteration. That means that while shapes associated with the root
stage are updated from the root stage’s functions, the shapes of the sub-stages
currently active are updated from their functions.
E.g. if we have a root stage which contains 4 children CeedStage.stages
A, B, C, D:
root
A
B
C
D
If root’s CeedStage.order
is 'serial'
, then for each root
CeedStage.loop
iteration, each sub-stage A - D is evaluated sequentially
after the previous sub-stage has finished in the order A -> B -> C -> D.
If it’s 'parallel'
, then each sub-stage is evaluated simultaneously
rather than sequentially.
While the sub-stages are executed in parallel or serially, the root’s
CeedStage.functions
are evaluated and the root’s shapes set to those
values.
If CeedStage.complete_on
is 'any'
then a loop iteration for the
root stage will be considered completed after all the root’s
CeedStage.functions
are done and any of the
CeedStage.stages
have completed. The sub-stages that have
not yet completed will just end early.
Otherwise, CeedStage.complete_on
is 'all'
and a loop iteration for
the root will complete after all the root’s CeedStage.functions
and
all the CeedStage.stages
have completed.
In all cases, shapes that are not associated with a stage that is currently being evaluated will be set to transparent. E.g. in the serial case, shapes that are associated with stage C will be transparent in all other stages, except if they also appear in those stages.
If a shape occurs in multiple stages (e.g. in both the root and A), then if the root and A set different color channels (e.g. root sets blue, A sets green), the shape will will set each channel from each active stage simultaneously because they don’t conflict. If they do set the same channel, the sub-stages win over parent stages in the stage tree.
Color channels
Each stage contains CeedStage.shapes
that are all set to the value
returned by the currently active function in CeedStage.functions
for the current time step. That is the function yields a single floating point
value between 0-1, inclusive and all the shapes get set to that value for that
timestep.
However, there are 3 channels to be set; red, green, and blue. You can select
which of these channels the stage sets using CeedStage.color_r
,
CeedStage.color_g
, and CeedStage.color_b
and those channels are
set to the same function value. The remaining channels are not set by this stage
and default to zero if no other parent or sub-stage sets them.
Shapes belonging to sub-stages are not controlled by parent stages, only by the direct stage(s) that contains them.
If the projector video_mode
is
set to "QUAD12X"
, then the function value is used for all three channels
so it’s grayscale. E.g. even if only CeedStage.color_r
and
CeedStage.color_b
are true, all three red, green, and blue channels
will be set to the same value . If the channels are set to different values
in parallel stages, the channels are first averaged.
Stage lifecycle
The ultimate use of a stage is to sample it for all timepoints that the stage is
active. Before the stage is ready, however, it needs to be initialized. Folowing
are the overall steps performed by StageFactoryBase.tick_stage()
that
initialize a stage before running it as an experiment.
Starting with a stage, first the stage is coped and all
CeedStage.functions
resampled using
CeedStage.copy_and_resample()
. Then the stage is renamed to
last_experiment_stage_name
. This displays the copied stage to be run in
the GUI under the name last_experiment_stage_name
.
Next, we call CeedStage.init_stage_tree()
on the root stage. This
calls init_func_tree()
on all the stage’s
CeedStage.functions
and recursively calls
CeedStage.init_stage_tree()
on all the sub-CeedStage.stages
as well as some initialization.
Next, using CeedStage.apply_pre_compute()
it pre-computes all the stages
and functions for which it is enabled. See below for details. Finally, the
stage is sampled, a sample at a time using the CeedStage.tick_stage()
generator. This generator either returns the pre-comuted values, computed
before the experiment started if enabled, or
it computes the sample values and yields them until the root stage is done.
When the stage is done, it internally raises a StageDoneException
.
This gets caught and Ceed knows that the stage is done. At the end of each loop
iteration and at the end of the overall stage it calls
CeedStage.finalize_loop_iteration()
, and
CeedStage.finalize_stage()
, respectively.
Customizing stages
A CeedStage
has multiple methods that can be overwritten by a plugin.
Following are some relevant methods - see their docs for more details.
If the stage generates the samples directly without using the function classes,
CeedStage.resample_parameters()
needs to be augmented if the stage has any randomness.CeedStage.init_stage_tree()
,FuncBase.init_func()
,CeedStage.init_loop_iteration()
,CeedStage.finalize_loop_iteration()
, andCeedStage.finalize_stage()
may be augmented if any of these stage lifecycle events requires additional initialization/finalization.CeedStage.evaluate_stage()
is the most approperiate method to overwrite to have the stage directly compute values for the shapes. By default it goes through all the loops and for each loop it goes through all the functions and sub-stages. It can be overwritten instead to just yield any specific desired values.CeedStage.tick_stage()
can be overwritten, but it requires more care. Since this yields the values directly and is called directly by the higher level code, ifCeedStage.tick_stage()
is overwritten it won’t participate in pre-computing or any other stage behaviors, so pre-computing should be disabled by settingCeedStage.disable_pre_compute
to True. See the method for other requirements.Additionally, if it’s a root stage, it should pad the stage runtime to
CeedStage.pad_stage_ticks
, if nonzero.CeedStage.apply_pre_compute()
,CeedStage.runtime_functions
, andCeedStage.runtime_stage
can be overwritten/manually set, but great care must be taken. See their docs and the pre-computing section below.See the stage’s properties for additional customizations.
In all cases, CeedStage.get_state()
may also need to
be augmented to return any parameters that are part of the instance, otherwise
they won’t be copied when the stage is internally copied and the stage will use
incorrect values when run e.g. in the second process that runs the experiment.
If stages are pre-computed, it may not have any side-effects, because they would occur before the experiment during pre-computing. So if these side-effects are needed (e.g. drug delivery), either turn off pre-computing for the stage or set its function’s duration to negative to disable it for the function and consequently the stage containing it.
Running a stage
Once we have a stage, shapes, and a function, the stage is ready to be run with
StageFactoryBase.tick_stage()
The following is a worked through example showing the steps the Ceed GUI goes through automatically to run a stage and what you should do to manually run a stage for testing purposes.
First create the stage, shape, and function:
from ceed.stage import CeedStage, StageFactoryBase, register_all_stages, \
StageDoneException
from ceed.function import FunctionFactoryBase, register_all_functions
from ceed.shape import CeedPaintCanvasBehavior
from fractions import Fraction
# create function/shape/stage factories to house functions/shapes/stages
function_factory = FunctionFactoryBase()
# register built-in plugins
register_all_functions(function_factory)
shape_factory = CeedPaintCanvasBehavior()
stage_factory = StageFactoryBase(
function_factory=function_factory, shape_factory=shape_factory)
# register built-in plugins
register_all_stages(stage_factory)
# create a 3 second duration function, that loops twice
LinearFuncCls = function_factory.get('LinearFunc')
function = LinearFuncCls(
function_factory=function_factory, m=.33, b=0, duration=3, loop=2)
print(f'Function name: "{function.name}"')
# and a circle shape
circle = shape_factory.create_shape('circle', center=(100, 100))
# and add it
shape_factory.add_shape(circle)
print(f'Shape name: "{circle.name}"')
# and finally the stage
stage = CeedStage(
stage_factory=stage_factory, function_factory=function_factory,
shape_factory=shape_factory)
# and add it to factory
stage_factory.add_stage(stage)
print(f'Stage name: "{stage.name}"')
# now add the function and shape to stage
stage.add_func(function)
stage.add_shape(circle)
# stage will only set red and blue channels
stage.color_r = stage.color_b = True
stage.color_g = False
Once ready, we can run through the stage manually like Ceed does during an experiment:
# now create the generator that will iterate through all the stage shape
# values at a frame rate of 2Hz
tick = stage_factory.tick_stage(
t_start=0, frame_rate=Fraction(2, 1), stage_name=stage.name)
# start it
next(tick)
# start with time zero, the same as t_start
i = 0
while True:
# send the next global time value as multiples of the period (1 / 2)
t = Fraction(i, 2)
try:
# this gets the raw intensity values
shapes_intensity_raw = tick.send(t)
except StageDoneException:
# when the stage is done, it raises this exception
break
# convert raw intensity to final color intensity. This function can
# also e.g. convert the colors for quad mode where it's gray-scale
shapes_intensity = stage_factory.set_shape_gl_color_values(
shape_views=None, shape_values=shapes_intensity_raw)
print(f'time={t}s, intensity="{shapes_intensity}"')
i += 1
The above will print the following when run:
Function name: "Linear"
Shape name: "Shape"
Stage name: "Stage-2"
time=0s, intensity="[('Shape', 0, 0, 0, 1)]"
time=1/2s, intensity="[('Shape', 0.165, 0, 0.165, 1)]"
time=1s, intensity="[('Shape', 0.33, 0, 0.33, 1)]"
time=3/2s, intensity="[('Shape', 0.495, 0, 0.495, 1)]"
time=2s, intensity="[('Shape', 0.66, 0, 0.66, 1)]"
time=5/2s, intensity="[('Shape', 0.825, 0, 0.825, 1)]"
time=3s, intensity="[('Shape', 0, 0, 0, 1)]"
time=7/2s, intensity="[('Shape', 0.165, 0, 0.165, 1)]"
time=4s, intensity="[('Shape', 0.33, 0, 0.33, 1)]"
time=9/2s, intensity="[('Shape', 0.495, 0, 0.495, 1)]"
time=5s, intensity="[('Shape', 0.66, 0, 0.66, 1)]"
time=11/2s, intensity="[('Shape', 0.825, 0, 0.825, 1)]"
Some important points to notice above: The global clock is run with multiples of the period. This period is the projector frame rate, and when ticking we must only increment the time by single period increments. This is required for stage pre-computing to work because once pre-computed, we have a list of intensity values with the expectation that each value corresponds to single period increment because that’s how they are pre-computed.
To skip a frame, you must still tick that frame time, but you can discard the yielded value. This is how Ceed drops frames when the GPU takes too long to render a frame and we must compensate by dropping a frame.
One can also use the stage factory to compute all the intensity values without having to iterate as follows. Once we have the stage ready:
from pprint import pprint
# now create the generator that will iterate through all the stage shape
# values at a frame rate of 2Hz
intensity = stage_factory.get_all_shape_values(
frame_rate=Fraction(2, 1), stage_name=stage.name)
pprint(intensity)
This prints:
defaultdict(<class 'list'>,
{'Shape': [(0, 0, 0, 1),
(0.165, 0, 0.165, 1),
(0.33, 0, 0.33, 1),
(0.495, 0, 0.495, 1),
(0.66, 0, 0.66, 1),
(0.825, 0, 0.825, 1),
(0, 0, 0, 1),
(0.165, 0, 0.165, 1),
(0.33, 0, 0.33, 1),
(0.495, 0, 0.495, 1),
(0.66, 0, 0.66, 1),
(0.825, 0, 0.825, 1)]})
Pre-computing
By default, during an experiment as the global clock ticks in multiple of the
period, the root stage is given the current time and it compute the intensity
values for all the shapes from its CeedStage.functions
and
sub-CeedStage.stages
. If the intensity computation is slow, the CPU
may miss updating the GPU with a new frame before the next frame deadline
and consequently we will need to drop a frame.
Ceed can pre-compute the intensity values for all the shapes for all time points by virtually running through the whole experiment and recording the intensities yielded by the stage into a flat list. Then during the real experiment it simply looks up the intensity from the list by sequentially iterating through the list.
This works because the global time is sequential consecutive multiples of the period both during the virtual computation and during the replay, so we can simply count frames to locate the desired intensity.
Pre-computing can be enabled in the GUI through the
pre_compute_stages
property.
Not all stages can be pre-computed. A stage could have functions that are
infinite in duration (i.e. negarive duration
,
e.g. when it’s waiting for a switch to go ON) or a stage can be manually
opted out from pre-computing by setting CeedStage.disable_pre_compute
to True. In that case, all other functions and stages that are not infinite
and not opted would will still be pre-computed as much as possible.
See CeedStage.disable_pre_compute
, CeedStage.runtime_functions
,
CeedStage.runtime_stage
, and CeedStage.can_pre_compute
for
more details.
Saving and restoring stages
Functions can be saved as a data dictionary and later restored into a function instance. E.g.:
>>> function = LinearFunc(
... function_factory=function_factory, m=.33, b=0, duration=3, loop=2)
>>> circle = shape_factory.create_shape('circle', center=(100, 100))
>>> shape_factory.add_shape(circle)
>>> stage = CeedStage(
... stage_factory=stage_factory, function_factory=function_factory,
... shape_factory=shape_factory)
>>> stage.add_func(function)
>>> stage.add_shape(circle)
>>> stage_factory.add_stage(stage)
>>> state = stage.get_state()
>>> state
{'cls': 'CeedStage',
'color_b': True,
'color_g': False,
'color_r': False,
'complete_on': 'all',
'disable_pre_compute': False,
'functions': [{'b': 0.0,
'cls': 'LinearFunc',
'duration': 3,
'loop': 2,
'm': 0.33,
'name': 'Linear',
'noisy_parameter_samples': {},
'noisy_parameters': {},
't_offset': 0.0,
'timebase_denominator': 1,
'timebase_numerator': 0}],
'name': 'Stage-2',
'order': 'serial',
'shapes': [{'keep_dark': False, 'name': 'Shape'}],
'stages': []}
>>> # this is how we create a stage from state
>>> new_stage = stage_factory.make_stage(state)
>>> new_stage
<ceed.stage.CeedStage: "Stage" children=(1, 0), at 0x22c2f3b7898>
>>> new_stage.get_state()
{'cls': 'CeedStage',
'color_b': True,
'color_g': False,
'color_r': False,
...
'name': 'Stage',
'order': 'serial',
'shapes': [{'keep_dark': False, 'name': 'Shape'}],
'stages': []}
If you notice, name
was not restored to the new stage. That’s because
name
is only restored if we pass clone=True
as name is considered an
internal property and not always user-customizable. Because we ensure
each stage’s name in the GUI is unique. E.g. with clone:
>>> new_stage = stage_factory.make_stage(state, clone=True)
>>> new_stage.get_state()
{'cls': 'CeedStage',
...
'name': 'Stage-2',
'stages': []}
A fundumental part of Ceed is copying and reconstructing stage objects.
E.g. this is required to recover functions from a template file, from old data,
or even to be able to run the experiment because it is run from a second
process. Consequently, anything required for the stage to be reconstructed
must be returned by CeedStage.get_state()
.
Reference stages
Stages can contain other CeedStage.stages
as children. Instead of
copying stages around we want to be able to reference another stage and add
that reference as a child of a stage. This is useful so that these sub-stages
update when the original stage’s parameters are updated. This is mostly meant
to be used from the GUI, although work fine otherwise.
CeedStageRef
allows one to reference stages in such a manner.
Instead of copying a stage, just get a reference to it with
StageFactoryBase.get_stage_ref()
and add it to another stage.
When removed and destroyed, such stage references must be explicitly released
with StageFactoryBase.return_stage_ref()
, otherwise the original stage
cannot be deleted in the GUI.
Methods that accept stages (such as CeedStage.add_stage()
) should also
typically accept CeedStageRef
stages.
CeedStageRef
cannot be used directly during an experiment, unlike
normal stages. Therefore, they or any stages that contain them must first copy
them and expand them to refer to the orignal stages being referenced before
using them, with CeedStage.copy_expand_ref()
or
CeedStageRef.copy_expand_ref()
.
E.g.
>>> stage = CeedStage(...)
>>> stage
<ceed.stage.CeedStage: "Stage-2" children=(1, 0), at 0x258b6de3c18>
>>> ref_stage = stage_factory.get_stage_ref(stage=stage)
>>> ref_stage
<ceed.stage.CeedStageRef object at 0x00000258B6DE8128>
>>> ref_stage.stage
<ceed.stage.CeedStage: "Stage-2" children=(1, 0), at 0x258b6de3c18>
>>> stage_factory.return_stage_ref(ref_stage)
Before an experiment using a stage is run, the stage and all its sub-stages and stage functions that are such references are expanded and copied.
Copying stages
Stages can be copied automatically using deepcopy
or
CeedStage.copy_expand_ref()
. The former makes a full copy of all the
stages, but any CeedStageRef
stages will only copy the
CeedStageRef
, not the original stage being refered to. The latter,
instead of copying the CeedStageRef
, will replace any
CeedStageRef
with copies of the class it refers to.
Stages can be manually copied with CeedStage.get_state()
and
CeedStage.set_state()
(although StageFactoryBase.make_stage()
is
more appropriate for end-user creation).
Create stage in script
A stage complete with functions and shapes can be created in a script,
saved to a yaml file, and then imported from the GUI ready to be
used in an experiment. See
save_config_to_yaml()
for an example.
Custom plugin stage example
As explained above, plugins can register customized CeedStage
sub-classes to be included in the GUI. Following is an example of how
CeedStage.evaluate_stage()
can be overwritten.
By default the CeedStage.evaluate_stage()
generator cycles through
CeedStage.loop
times and for each loop iteration it ticks through all
the stage’s CeedStage.functions
, setting the stage’s shapes to their
values in addition to ticking through the sub-stages simultaniously and then
yielding.
CeedStage.evaluate_stage()
can be safely overwritten to yield directly
whatever values you wish ignoring any functions or sub-stages.
E.g. if you have a shape in the stage named "circle"
(in the GUI this shape
will have to be added to the stage) and you want its RGB value to to be
(0.5, 0, 0) for 2 frames, (0, 0.6, 0) for 3 frames, and finally (0, 0.2, 0.1)
for 4 frames for a total experiment duration of 9 frames you would write the
following sub-class in the stage plugin:
class SlowStage(CeedStage):
def evaluate_stage(self, shapes, last_end_t):
# always get the first time
self.t_start = t = yield
for _ in range(2):
# r, g, b, a values. a (alpha) should be None
shapes['circle'].append((0.5, 0, 0, None))
# this yields so GUI can use the change shape colors
t = yield
for _ in range(3):
shapes['circle'].append((0, 0.6, 0, None))
t = yield
for _ in range(4):
shapes['circle'].append((0, 0.2, 0.1, None))
t = yield
# this time value was not used and this effectively makes the
# stage 9 samples long, and it ends on the last sample so
# that last time will be used as start of next stage
self.t_end = t
# raising this exception is how we indicate we're done
raise StageDoneException
The above class will behave correctly whether the stage is pre-computed or not
because either way it’s called to get the values. See
CeedStage.evaluate_stage()
for further details.
To add stage settings to the GUI, see CeedStage.get_settings_display()
and the CSV stage plugin implamentation.
Other methods could potentially also be overwritten to hook into the stage
lifecycle, but they generally require more care. See all CeedStage
methods and below for further details.
Custom graphics
Besides the shapes drawn in the Ceed GUI or script generated, stages could add arbitrary Kivy GL graphics to the experiment screen and update them during an experiment. This e.g. allows the display of a circle whose intensity falls off as it’s farther from the center of the circle.
CeedStage
provides the following methods to add, update, and remove
these graphics for an experiment: add_gl_to_canvas()
,
set_gl_colors()
, and remove_gl_from_canvas()
.
Additionally, like GUI drawn shapes that automatically log the shape intensity
for each frame to be accessible from
shapes_intensity
, stages can overwrite
get_stage_shape_names()
to add names and use those names to
log arbitrary 4-byte (nominally RGBA for shapes) values for each frame.
These values are also displayed in the graph preview window for all shapes
in the GUI. However, you have to ensure to compute and log the rgba data during
each tick.
See the example plugins in the examples directory.
- TODO: if a function or stage has zero duration, any data events logged during
intitialization is not logged if pre-computing. Log these as well. Similarly, logs created after the last frame of a stage/function is not logged when pre-computing.
- exception ceed.stage.StageDoneException
Bases:
Exception
Raised as a signal by a
CeedStage
when it is done.
- ceed.stage.StageType
The type-hint type for
CeedStage
.alias of TypeVar(‘StageType’, bound=
CeedStage
)
- ceed.stage.CeedStageOrRefInstance
Instance of either
CeedStage
orCeedStageRef
.alias of
Union
[CeedStage
,CeedStageRef
]
- ceed.stage.last_experiment_stage_name = 'experiment_sampled'
The stage name used for the last experiment run. This name cannot be used by a stage and this stage is overwritten after each experiment run.
- class ceed.stage.StageFactoryBase(function_factory, shape_factory, **kwargs)
Bases:
kivy._event.EventDispatcher
A global store of the defined
CeedStage
classes and customized stage instances.Stages in the factory are displayed to the user in the GUI. Similarly, when a user creates a custom stage in the GUI, it’s added here automatically.
See
ceed.stage
for details.- Events
- on_changed:
Triggered whenever a stage is added or removed from the factory.
- on_data_event:
The event is dispatched by stages whenever the wish to log something. During an experiment, this data is captured and recorded in the file.
To dispatch, you must pass the function’s
ceed_id
and a string indicating the event type. You can also pass arbitrary arguments that gets recorded as well. E.g.stage_factory.dispatch('on_data_event', stage.ceed_id, 'drug', .4, 'h2o')
.See
event_data
for details on default events as well as data type and argument requirements.
- stage_names: Dict[str, ceed.stage.CeedStage]
A dict of all the stages whose keys are the stage
CeedStage.name
and whose values are the correspondingCeedStage
instances.Contains stages added with
add_stage()
as well as those automatically created and added whenregister()
is called on a class.>>> stage_factory.stage_names {'Stage': <ceed.stage.CeedStage at 0x1da866f00b8>}
- shape_factory: ceed.shape.CeedPaintCanvasBehavior = None
The
CeedPaintCanvasBehavior
instance that contains or is associated with all the shapes used in the stages.
- function_factory: ceed.function.FunctionFactoryBase = None
The
FunctionFactoryBase
instance that contains or is associated with all the functions used in the stages.
- stages: List[ceed.stage.CeedStage]
The list of the currently available
CeedStage
instances added withadd_stage()
.These stages are listed in the GUI and can be used by name to start a stage to run.
It does not include the instances automatically created and stored in
stages_inst_default
when youregister()
a stage class.
- stages_cls: Dict[str, Type[ceed.stage.StageType]] = {}
Dict whose keys is the name of the stage classes registered with
register()
and whose values is the corresponding classes.:>>> stage_factory.stages_cls {'CeedStage': ceed.stage.CeedStage}
- stages_inst_default: Dict[str, ceed.stage.CeedStage] = {}
Dict whose keys is the function
CeedStage.name
and whose values are the corresponding stage instances.Contains only the stages that are automatically created and added when
register()
is called on a class.>>> stage_factory.stages_inst_default {'Stage': <ceed.stage.CeedStage at 0x1da866f00b8>}
- unique_names: ceed.utils.UniqueNames = None
A set that tracks existing stage names to help us ensure all global stages have unique names.
- plugin_sources: Dict[str, List[Tuple[Tuple[str], bytes]]] = {}
A dictionary of the names of all the plugin packages imported, mapped to the python file contents of the directories in the package. Each value is a list of tuples.
The first item of each tuple is also a tuple containing the names of the directories leading to and including the python filename relative to the package. The second item in the tuple is the bytes content of the file.
- get_stage_ref(name: Optional[str] = None, stage: Optional[Union[ceed.stage.CeedStage, ceed.stage.CeedStageRef]] = None) ceed.stage.CeedStageRef
Returns a
CeedStageRef
instance that refers to the original stage. Seeceed.stage
for details.One of
name
orstage
must be specified. The stage being referenced bystage
should have been added to this instance, although it is not explicitly enforced currently.If used,
return_stae_ref()
must be called when the reference is not used anymore.- Parameters
name – The name of the stage to lookup in
stage_names
.stage – Or the actual stage to use.
- Returns
A
CeedStageRef
to the original stage.
- return_stage_ref(stage_ref: ceed.stage.CeedStageRef) None
Releases the stage ref created by
get_stage_ref()
.- Parameters
stage_ref – Instance returned by
get_stage_ref()
.
- register(cls: Type[ceed.stage.StageType], instance: Optional[ceed.stage.CeedStage] = None)
Registers the class and adds it to
stages_cls
. It also creates an instance (unlessinstance
is provided, in which case that is used) of the class that is added tostage_names
andstages_inst_default
.See
ceed.stage
for details.- Params
- cls: subclass of
CeedStage
The class to register.
- instance: instance of cls
The instance of cls to use. If None, a default class instance, using the default
CeedStage.name
is stored. Defaults to None.
- cls: subclass of
- add_plugin_source(package: str, contents: List[Tuple[Tuple[str], bytes]])
Adds the package contents to
plugin_sources
if it hasn’t already been added. Otherwise raises an error.
- get(name: str) Optional[Type[ceed.stage.StageType]]
Returns the class with name
name
that was registered withregister()
.See
ceed.stage
for details.- Params
- name: str
The name of the class (e.g.
'CosStage'
).
- Returns
The class if found, otherwise None.
- get_names() List[str]
Returns the class names of all classes registered with
register()
.
- get_classes() List[Type[ceed.stage.StageType]]
Returns the classes registered with
register()
.
- make_stage(state: dict, instance: Optional[Union[ceed.stage.CeedStage, ceed.stage.CeedStageRef]] = None, clone: bool = False, func_name_map: Dict[str, str] = {}, old_name_to_shape_map: Optional[Dict[str, Union[ceed.stage.CeedStage, ceed.stage.CeedStageRef]]] = None) Union[ceed.stage.CeedStage, ceed.stage.CeedStageRef]
Instantiates the stage from the state and returns it.
This method must be used to instantiate a stage from state. See
ceed.stage
for details and an example.- Parameters
state – The state dict representing the stage as returned by
FuncBase.get_state()
.instance – If None, a stage instance will be created and state will applied to it. Otherwise, it is applied to the given instance, which must be of the correct class.
clone – See
CeedStage.apply_state()
.func_name_map – See
CeedStage.apply_state()
.old_name_to_shape_map – See
CeedStage.apply_state()
.
- Returns
The stage instance created or used.
- add_stage(stage: ceed.stage.CeedStage, allow_last_experiment=True) None
Adds the
CeedStage
to the stage factory (stages
) and makes it available in the GUI.See
ceed.stage
for details.Remember to check
can_other_stage_be_added()
before adding if there’s potential for it to return False.If the
CeedStage.name
already exists instages
,CeedStage.name
will be set to a unique name based on its original name. Once added until removed, anytime the stage’sCeedStage.name
changes, if it clashes with an existing stage’s name, it is automatically renamed.- Parameters
stage – The
CeedStage
to add.allow_last_experiment – Whether to allow the stage to have the same name is
ceed.stage.last_experiment_stage_name
. If False and a stage with that name is added, it is renamed. Otherwise, it’s original name is kept.
- remove_stage(stage: ceed.stage.CeedStage, force=False) bool
Removes a stage previously added with
add_stage()
.- Params
- stage:
CeedStage
The stage to remove.
- force: bool
If True, it’ll remove the stage even if there are references to it created by
get_stage_ref()
.
- stage:
- Returns
Whether the stage was removed successfully. E.g. if force is False and it has a ref, it won’t be removed.
- clear_stages(force=False) None
Removes all the stages registered with
add_stage()
.- Params
- force: bool
If True, it’ll remove all stages even if there are references to them created by
get_stage_ref()
.
- find_shape_in_all_stages(_, shape, process_shape_callback) None
Searches for the
ceed.shape.CeedShape
instance in all the known stages and callsprocess_shape_callback
on each found.- Params
- _: anything
This parameter is ignored and can be anything.
- shape:
ceed.shape.CeedShape
The shape to search in all stages.
- process_shape_callback: callback function
It is called with two parameters; the
CeedStage
andceed.shape.CeedShape
instance for each found.
- save_stages(use_cache=False) List[dict]
Returns a dict representation of all the stages added with
add_stage()
.- Parameters
use_cache – If True, it’ll get the state using the cache from previous times the state was read and cached, if the cache exists.
It is a list of dicts where each item is the
CeedStage.get_state()
of the corresponding stage instages
.
- recover_stages(stage_states: List[dict], func_name_map: dict, old_name_to_shape_map: Dict[str, Union[ceed.stage.CeedStage, ceed.stage.CeedStageRef]]) Tuple[List[ceed.stage.CeedStage], Dict[str, str]]
Takes a list of stages states such as returned by
save_stages()
and instantiates the stages represented by the states and adds (add_stage()
) the stages to the factory.- Parameters
stage_states – List of stages state.
func_name_map – See
CeedStage.apply_state()
.old_name_to_shape_map – See
CeedStage.apply_state()
.
- Returns
A tuple
stages, name_map
,stages
is the list of stages,name_map
is a map from the original stage’s name to the new name given to the stage (in case a stage with that name already existed).
- tick_stage(t_start: Union[float, int, fractions.Fraction], frame_rate: fractions.Fraction, stage_name: str = '', stage: Optional[ceed.stage.CeedStage] = None, pre_compute: bool = False) Generator[List[Tuple[str, List[Tuple[Optional[float], Optional[float], Optional[float], Optional[float]]]]], Union[float, int, fractions.Fraction], None]
A generator which starts a
CeedStage
and can be time-ticked to generate the shape intensity values for each time point in the experiment.A
CeedStage
represents a collection of shapes with functions applied to them. Each of these shapes has a defined intensity for every time point. This generator walks through time computing the intensity for each shape for every time point and yielding it.See
get_all_shape_values()
for example usage. Ceed GUI uses this to generate the shape intensity values shown during an experiment.See the example in
stage
showing how to run a stage with this method.- Params
- t_start: a number
The global time at which the stage starts.
- frame_rate: fraction
The frame rate to sample at, as a fraction.
- stage_name: str
The
CeedStage.name
of the stage to start.- stage: str
The
CeedStage
to start.- pre_compute: bool
Whether to pre-compute the stages, for those stages that support it.
- Yields
A list of the intensity values for each shape.
Each item in the list is a 2-tuple of
(name, values)
.name
is thekivy_garden.painter.PaintShape.name
of the shape and is listed only once in the list.values
is a list of color values and each item in that list is a 4-tuple of(r, g, b, a)
. Any of these values can be None, in which case that color remains the same (seeset_shape_gl_color_values()
). This way a shape can be updated from multiple sub-stages, where only e.g. ther
value is changed.- Raises
- StageDoneException:
When done with the stage (time is out of bounds). The time at which this is raised was not used by the stage so there’s no shape values for that last time point.
- add_shapes_gl_to_canvas(canvas: kivy.graphics.instructions.Canvas, name: str, quad: Optional[int] = None, shapes: Optional[Container[str]] = None) Dict[str, kivy.graphics.context_instructions.Color]
Adds all the kivy OpenGL instructions required to display the intensity-varying shapes to the kivy canvas and returns the color classes that control the color of each shape.
This is called by Ceed when it creates a new experiment to get all the graphics for the shapes that it will control during the experiment.
- Params
- canvas: Kivy canvas instance
The canvas to which the gl instructions are added. It add e.g. the polygon and its color.
- name: str
The name to associate with these OpenGL instructions. The name is used later to remove the instructions as it allows to clear all the instructions with that name.
- quad: int or None
When in quad mode, we add the instructions 4 times, one for each quad (top-left, top-right, bottom-left, bottom-right) so it is called 4 times sequentially. This counts up from 0-3 in that case. Otherwise, it’s None. From a user POV it should not matter whether we’re in quad mode because Ceed handles scaling and placing the graphics in the right area.
- Returns
a dict whose keys is the
kivy_garden.painter.PaintShape.name
of theceed.shape.CeedShape
and whose value is the KivyColor
instruction instance that controls the color of the shape.
- set_shape_gl_color_values(shape_views: Optional[Dict[str, kivy.graphics.context_instructions.Color]], shape_values: List[Tuple[str, List[Tuple[Optional[float], Optional[float], Optional[float], Optional[float]]]]], quad: Optional[int] = None, grayscale: Optional[str] = None) List[Tuple[str, float, float, float, float]]
Takes the dict of the Color instances that control the color of each shape as well as the list of the color values for the current time point for each shape and sets the shape’s color to those values.
This is called by Ceed for every time step to set the current shape color values. In QUAD4X it’s called 4 times per frame, in QUAD121X it’s called 12 times per frame.
The shape color values is a list of 4-tuples, each a
r, g, b, a
value. In each tuple, any of them can be None, in which case that color channel is skipped for that tuple. The list is flattened and the last value for each channel across all tuples is used (after being forced to the[0, 1]
range). If any are None across all tuples, it’s left unchanged and not set. If all r, g, b, and a is None, that shape becomes transparent.- Params
- shape_views: dict
The dict of shape names and shapes colors as returned by
add_shapes_gl_to_canvas()
.If it is None, the color will not be updated but the return result will be identical to when not None.
- shape_values: list
The list of color intensity values to use for each shape as yielded by
tick_stage()
.- quad: int or None
When in quad mode, we added the instructions 4 times, one for each quad. This indicates which quad is being updated, counting up from 0-3 in that case. Otherwise, it’s None.
- grayscale: str
The colors to operate on. Can be any subset of the string ‘rgb’. Specifically, although we get intensity values for some subset of r, g, b values for each stage from the stage settings, this computes the average intensity for the active RGB channels selected in the stage and assigns the mean to all of the colors listed in
grayscale
.E.g. if a stage selects the r and g colors in its config, and
grayscale
is"gb"
, then both the g and b channels are set to the mean of the r and g values provided by the stage for this timestep (b is excluded since the stage provides no value for it). The b channel is not set so it’s left unchanged (i.e. it’ll keep the last value).This is how we turn the color into a gray-scale value when e.g. in
QUAD12X
mode. Specifically, in that mode, this method is called 12 times, 4 for the 4 quads, and 3 for the r, g, and b color channel for each quad. It gets called 4 times for the red channel withgrayscale
set to'r'
, followed by 4 times for the green channel withgrayscale
set to'g'
, followed by 4 times for the blue channel withgrayscale
set to'b'
. This sets the value for 12 frames.
- Returns
A list of the colors each shape was set to. Each item in the list is
(name, r, g, b, a)
, wherename
is the shape’s name andr
,g
,b
,a
is the color value it was set to. Eachname
occurs at most once in the list.
E.g. from the worked example in
stage
, by default we calledset_shape_gl_color_values()
with no grayscale parameter value, which printed:time=0s, intensity="[('Shape', 0, 0, 0, 1)]" time=1/2s, intensity="[('Shape', 0.165, 0, 0.165, 1)]" time=1s, intensity="[('Shape', 0.33, 0, 0.33, 1)]" ...
If we provide
"r"
for grayscale, it prints:time=0s, intensity="[('Shape', 0.0, 0.0, 0.0, 1)]" time=1/2s, intensity="[('Shape', 0.165, 0.165, 0.165, 1)]" time=1s, intensity="[('Shape', 0.33, 0.33, 0.33, 1)]" ...
That is the stage only sets red and blue, so it averages those two values, which happen to be the same because there’s only one stage setting the color value for both red/blue channels. This mean value is assigned to r, g, and b in the result. However, if
shape_views
was provided to the function, only the red channel’s color would be set to this value because grayscale was"r"
. If it was"rg"
, the printed value would be the same, but only red and green of the Color graphics instructions would be set to the mean value and the others (green/blue or blue, respectively) remain unchanged.
- remove_shapes_gl_from_canvas(canvas: kivy.graphics.instructions.Canvas, name: str) None
Removes all the shape and color instructions that was added with
add_shapes_gl_to_canvas()
.This is called by Ceed after an experiment and it removes all the instructions added with this group name.
- Params
- canvas: Kivy canvas instance
The canvas to which the gl instructions were added.
- name: str
The name used when adding the instructions with
add_shapes_gl_to_canvas()
.
- get_all_shape_values(frame_rate: fractions.Fraction, stage_name: str = '', stage: Optional[ceed.stage.CeedStage] = None, pre_compute: bool = False) Dict[str, List[Tuple[float, float, float, float]]]
Uses
tick_stage()
for every shape in the stagestage_name
or givenstage
, it samples all the shape intensity values at the given frame rate for the full stage duration and returns a list of intensity values for each shape corresponding to each time point.frame_rate is not
frame_rate
(although it can be) bur rather the rate at which we sample the functions.- TODO: skip functions that are infinite duration. Add option to indicate
stage is also infinite. Currently it would just hang for infinite stage.
- add_manual_gl_to_canvas(screen_width: int, screen_height: int, stage: ceed.stage.CeedStage, canvas: kivy.graphics.instructions.Canvas, name: str, quad_mode: str, quad: Optional[int] = None) List[ceed.stage.CeedStage]
Adds all the kivy OpenGL instructions that a stage may manually set. It internally calls
add_gl_to_canvas()
for the root stage and all its substages.This is called by Ceed when it creates a new experiment to get all the graphics for the stage used during an experiment.
- Parameters
screen_width – The width of the projector in pixels.
screen_height – The height of the projector in pixels.
stage – The root
CeedStage
that will be run.canvas – The Kivy canvas instance to which the gl instructions must be added.
name – The name to associate with these OpenGL instructions. The name is used later to remove the instructions in
remove_shapes_gl_from_canvas()
as it allows to clear all the instructions with that name.quad_mode – Whether we’re in quad mode. This is the specific quad mode used. It can be one of ‘RGB’ (normal mode), ‘QUAD4X’, or ‘QUAD12X’.
quad –
When in quad mode, we have to add the instructions 4 times, one for each quad (top-left, top-right, bottom-left, bottom-right) so it is called 4 times sequentially. This counts up from 0-3 in that case. Otherwise, it’s None.
From a user POV it should not matter whether we’re in quad mode because Ceed handles scaling and placing the graphics in the right area. So the user must always create their graphics at full screen size and relative to bottom left corner. Ceed will automatically scale and translate them to the appropriate quad.
- Returns
The list of stages who added graphics instructions (their
add_gl_to_canvas()
returned True).
- set_manual_gl_colors(stages: List[ceed.stage.CeedStage], quad: Optional[int] = None, grayscale: Optional[str] = None, clear: bool = False) None
Calls
set_gl_colors()
for all the stages with manual graphics.Called by Ceed for every time step to allow the stages to update their manually added gl instructions (in
add_manual_gl_to_canvas()
) for this frame. In QUAD4X it’s called 4 times per frame, in QUAD121X it’s called 12 times per frame.- Parameters
stages – The list of stages returned by
add_manual_gl_to_canvas()
.quad – Same as in
set_shape_gl_color_values()
.grayscale – Same as in
set_shape_gl_color_values()
.clear – Unlike for the shape graphics that Ceed controls directly Ceed does not control the manual graphics. If a stage ends in the middle of a frame in quad mode, then the rest of the graphics or color channels for that frame must be cleared to black. Therefore, This will be called for those quads/channels with this parameter True and you must clear it.
- remove_manual_gl_from_canvas(stage: ceed.stage.CeedStage, canvas: kivy.graphics.instructions.Canvas, name: str) None
Removes all the gl instructions that was added with
add_manual_gl_to_canvas()
. It internally callsremove_gl_from_canvas()
for the root stage and all its substages.This is called by Ceed after an experiment and it should remove all the instructions added. Instructions added with this
name
will be automatically removed byremove_shapes_gl_from_canvas()
so they don’t have to be removed manually.- Parameters
stage – The root
CeedStage
that was run.canvas – The Kivy canvas instance to which the gl instructions was added.
name – The name associated with these OpenGL instructions.
- class ceed.stage.CeedStage(stage_factory: ceed.stage.StageFactoryBase, function_factory: ceed.function.FunctionFactoryBase, shape_factory: ceed.shape.CeedPaintCanvasBehavior, **kwargs)
Bases:
kivy._event.EventDispatcher
,ceed.utils.CeedWithID
The stage that controls a time period of an experiment.
See
ceed.stage
for details.- Events
- on_changed:
Triggered whenever a stage’s configuration option changes or if one of the functions or shapes of the stage is added/removed.
- name: str
The name of this stage.
- order: str
The order in which the sub-stages,
stages
, are evaluated. Can be one of'serial'
(one after the other),'parallel'
(all in parallel).See
CeedStage
description for details.
- complete_on: str
When to consider the stage’s children stages to be complete if we contain sub-stages -
stages
. Can be one of'all'
,'any'
.If
'any'
, this stage is done when any of the children stages is done, and when all of this stage’s functions are done. If'all'
, this stage is done when all children stages are done, and when all of this stage’s functions are done.See
CeedStage
description for details.
- disable_pre_compute: bool
Whether to disable pre-computing for this stage.
When pre-computing, either the stage is completely pre-computed from its functions and all sub-stages and then the stage’s intensity values for all time-points becomes essentially a flat list stored in
runtime_stage
. Or, if e.g. some of the sub-stages or functions cannot be pre-computed, then only the stage’s functions (or those among the functions that can be pre-computed) are pre-computed and all are stored inruntime_functions
.When
disable_pre_compute
is True, neither is pre-computed, even if the overallpre_compute_stages
is True.
- loop: int
The number of loop iterations the stage should do.
If more than one, the stage will go through all its
functions
andstages
loop
times.
- parent_stage: ceed.stage.CeedStage
The parent stage when this stage is a sub-stage of another.
- has_ref: bool
Whether there’s a CeedStageRef pointing to this stage.
- color_r: bool
Whether the
shapes
red channel should be set to thefunctions
value or if it should remain at zero intensity (False).
- color_g: bool
Whether the
shapes
green channel should be set to thefunctions
value or if it should remain at zero intensity (False).
- color_b: bool
Whether the
shapes
blue channel should be set to thefunctions
value or if it should remain at zero intensity (False).
- randomize_child_order: bool
Whether the sub-stages order should be randomly re-shuffled before each experiment.
If True,
stages
order stays the same, but the stage executes them in random order. The order is pre-sampled before the stage is executed and the given order is then used during the stage.The order is stored in
shuffled_order
.See also
randomize_order_each_loop
,lock_after_forked
,loop_count
, andloop_tree_count
.
- randomize_order_each_loop
When
randomize_child_order
is True, whether the child order should be re-shuffled for each loop iteration including loops of parent and parent of parents etc. (True) or whether we shuffle once before each experiment and use that order for all loop iterations.See also
randomize_child_order
,lock_after_forked
,loop_count
, andloop_tree_count
.
- lock_after_forked: bool
Stages can reference other stages. After the reference stages are expanded and copied before running the stage as an experiment, if
lock_after_forked
is False thenshuffled_order
is re-sampled again for each copied stage. If it’s True, then it is not re-sampled again and all the stages referencing the original stage will share the same randomized re-ordering as the referenced stage.See also
copy_and_resample
.
- shuffled_order: List[List[int]] = []
When
randomize_child_order
is True, it is a list of thestages
ordering that we should use for each loop.It is a list of lists, and each internal list corresponds to a single loop iteration as indexed by
loop_tree_count
. If we don’trandomize_order_each_loop
, then it contains a single list used for all the loops, otherwise there’s one for each loop.If not
randomize_child_order
, then it’s empty and they run instages
order.If there are more loops than number of items in the outer list, we use the last sub-list for the remaining loops. If not all
stages
indices are included in the internal lists, those stages are skipped.
- display = None
A widget used by the GUI to display the stage.
- pad_stage_ticks: int = 0
If the duration of the stage as represented by the number of clock tick is less than
pad_stage_ticks
, the stage will be padded topad_stage_ticks
clock cycles at the end.During those cycles, the stage’s shapes will be unchanged by this stage (i.e. if another stage is simultaneously active and set their values, that value will be used, otherwise it’ll be transparent), except for the shapes with
StageShape.keep_dark
that will still be kept black.See
pad_to_stage_handshake
for usage details.Warning
This is for internal use and is not saved with the stage state.
- t_start: Union[float, int, fractions.Fraction] = 0
The global time with which the stage or loop iteration was initialized. The value is in seconds.
Don’t set directly, it is set in
init_stage()
andinit_loop_iteration()
. If the stage iscan_pre_compute
, this is not used after pre-computing the stage.
- t_end: Union[float, int, fractions.Fraction] = 0
The time at which the loop or stage ended in global timebase.
Set by the stage after each loop is done and is only valid once loop/stage is done. It is used by the next stage in
stages
after this stage to know when to start, or for this stage to know when the the next loop started. If the stage iscan_pre_compute
, after pre-computing the stage it is only set to indicate when the stage ends, not for each loop.If overwriting
evaluate_stage()
, this must be set with the last time value passed in that was not used, indicating the time the stage ended (i.e. the stage spanned from the stage start time untilt_end
, not including the end). The next stage in the sequence would start from this time. Similarly, if manually settingruntime_stage
, the total stage duration is included and this value is automatically set from it inevaluate_pre_computed_stage()
.It is updated before
finalize_loop_iteration()
, andfinalize_stage()
are called.
- loop_count: int = 0
The current loop iteration.
This goes from zero (set by
init_stage()
/init_loop_iteration()
) toloop
- 1. The stage is done when it is exactlyloop
- 1, having loopedtimes
. Whenfinalize_loop_iteration()
andfinalize_stage()
are called, it is the value for the loop iteration that just ended.If the stage is
can_pre_compute
, this is not used after pre-computing the stage.See also
loop_tree_count
.
- loop_tree_count: int = 0
The current iteration, starting from zero, and incremented for each loop of the stage, including outside loops that loop over the stage.
E.g.:
Stage: name: root loop: 2 Stage: name: child loop: 3
Then the root and child stage’s
loop_count
will be 0 - 1, and 0 - 2, respectively, whileloop_tree_count
will be 0 - 1 and 0 - 5, respectively.
- runtime_functions: List[Tuple[Optional[ceed.function.FuncBase], Optional[List[float]], Optional[list], Optional[float]]] = []
Stores the pre-computed function values for those can be pre-computed and the original function for the rest.
Similar to
runtime_stage
, but ifcan_pre_compute
is False yetdisable_pre_compute
is also False, then we pre-compute all the functions who are not infinite in duration (duration
is non-negative) and store them here interleaved with those that are infinite.It is a list of 4-tuples of the same length as
functions
. Each item is(function, intensity, logs, duration)
. It is generated bypre_compute_functions()
.If the corresponding function is pre-computable, the
function
is None and intensity, logs, and duration is similar toruntime_stage
with the same constraints about each intensity and logs value corresponds to a time point the function is sampled and the ending time-point must be larger or equal to duration, relative to the function start time.logs
may be one value larger thanintensity
, if there’s some logs to be emitted on the frame after the last sample.If the function is not pre-computable, the
function
is the original function andintensity
,logs
, andduration
are None.If set manually, ensure that
apply_pre_compute()
is overwritten to do nothing, otherwise it may overwrite your value. Similarly,runtime_stage
must be None, otherwise that will be used instead.
- runtime_stage: Optional[Tuple[Dict[str, List[Tuple[Optional[float], Optional[float], Optional[float], Optional[float]]]], list, int, Union[float, int, fractions.Fraction]]] = None
A 4-tuple of stage
(intensity, logs, count, duration)
.If
can_pre_compute
, then this stage’s intensity values are pre-computed into a list for each shape and stored here. Otherwise, if it’s not pre-computed it is None.intensity
is a dict whose keys are shape names and values are a list of r, g, b, a values, one for each time point indicating the r,g,b,a value for that shape for that time point.Each time-point value corresponds exactly to the time passed to the stage that generated the value. E.g. with a linear function, the stage may be called with times (relative to the stage start time) such as 0 / rate, 1 / rate, … n / rate and the values correspond to the function values at those times. Then during the experiment, as we get time values, we instead count the number of tick stage calls and that number is the index into the values list that we return for all the shapes.
Similarly,
logs
is a list of any data event logs captured during pre-computing. These logs are replayed during the real experiment for each corresponding frame.count
is the number of frames inintensity
andlogs
. However,logs
may be one value larger thanintensity
(count
), if there’s some logs to be emitted on the frame after the last sample.After the last value in the list is used, the next time point past will raise a
StageDoneException
indicating the stage is done and the time value will have to be larger or equal tot_end
, which is the same saying the time relative to the stage start time must be larger or equal to theduration
of the tuple.By default it is generated by
pre_compute_stage()
. If set manually by the user, the above constraints must be followed and additionally,apply_pre_compute()
should be overwritten to do nothing, otherwise it may overwrite your value.
- can_pre_compute: bool = False
Whether we can pre-compute the full stage.
It is read only and is automatically computed during
init_stage_tree()
.If True it means that all the
functions
have finite duration (i.e. non-negative), for allstages
theircan_pre_compute
is True, anddisable_pre_compute
is False.apply_pre_compute()
does the pre-computation. Ifcan_pre_compute
is True, then it precomputes the stage’s intensity values for all time-points that the stage is active from its functions and all sub-stages and then essentially stores it as a flat list inruntime_stage
.If it is False, then if
disable_pre_compute
is also False, then all the functions that can be pre-computed are pre-computed, otherwise nothing is pre-computed. In both case we still callapply_pre_compute()
on all sub-stages.
- stage_factory: ceed.stage.StageFactoryBase = None
The
StageFactoryBase
this stage is associated with.
- function_factory: ceed.function.FunctionFactoryBase = None
The
FunctionFactoryBase
thefunctions
are associated with.
- shape_factory: ceed.shape.CeedPaintCanvasBehavior = None
The
CeedPaintCanvasBehavior
theshapes
are associated with.
- functions: List[Union[ceed.function.FuncBase, ceed.function.CeedFuncRef]] = []
A list of
ceed.function.FuncBase
instances which the stage iterates through sequentially to compute the intensity of all theshapes
at each time point.
- stages: List[Union[CeedStage, CeedStageRef]] = []
A list of
CeedStage
instances that are sub-stages of this stage.See
CeedStage
description for details.
- shapes: List[ceed.stage.StageShape] = []
The list of
StageShape
instances that are associated with this stage.All the shapes are set to the same intensity value at every time point according to the
functions
value at that time point.
- get_cached_state(use_cache=False) Dict
Like
get_state()
, but it caches the result. And next time it is called, ifuse_cache
is True, the cached value will be returned, unless the config changed in between. Helpful for backup so we don’t recompute the full state.- Parameters
use_cache – If True, it’ll get the state using the cache from previous times the state was read and cached, if the cache exists.
- Returns
The state dict.
- get_state(expand_ref: bool = False) dict
Returns a dict representation of the stage so that it can be reconstructed later with
apply_state()
.- Params
- state: dict
A dict with the state, to which configuration items and their values are added. If None, the default, a dict is created and returned.
- Returns
A dict with all the configuration data.
- apply_state(state: dict = {}, clone: bool = False, func_name_map: Dict[str, str] = {}, old_name_to_shape_map: Optional[Dict[str, Union[ceed.stage.CeedStage, ceed.stage.CeedStageRef]]] = None) None
Takes the state of the stage saved with
get_state()
and applies it to this stage. it also creates any children functions and stages and creates the references to theshapes
.It is called internally and should not be used directly. Use
StageFactoryBase.make_stage()
instead.- Parameters
state – The state dict representing the stage as returned by
get_state()
.clone – If False, only user customizable properties of the stage will be set (i.e those not listed in
_clone_props
), otherwise, all properties from state are applied to the stage. Clone is meant to be a complete re-instantiation of stage function.func_name_map – a mapping that maps old function names to their new names, in case they were re-named when imported.
old_name_to_shape_map – Mapping from shape names to the shapes.
- copy_expand_ref() ceed.stage.CeedStage
Returns a copy of the stage. Any sub-stages, recursively, that are ref-stages are expanded to normal stages.
- replace_ref_stage_with_source(stage_ref: ceed.stage.CeedStageRef) Tuple[ceed.stage.CeedStage, int]
Replaces the stage ref in
stages
with a copy of the referenced stage.
- replace_ref_func_with_source(func_ref: ceed.function.CeedFuncRef) Tuple[ceed.function.FuncBase, int]
Replaces the func ref in
functions
with a copy of the referenced function.
- can_other_stage_be_added(other_stage: Union[ceed.stage.CeedStage, ceed.stage.CeedStageRef]) bool
Checks whether the other stage may be added to us.
If the stage is already a child of this stage or sub-stages, it returns False to prevent recursion loops.
- add_stage(stage: Union[ceed.stage.CeedStage, ceed.stage.CeedStageRef], after: Optional[Union[ceed.stage.CeedStage, ceed.stage.CeedStageRef]] = None, index: Optional[int] = None) None
- remove_stage(stage: Union[ceed.stage.CeedStage, ceed.stage.CeedStageRef]) bool
- add_func(func: Union[ceed.function.FuncBase, ceed.function.CeedFuncRef], after: Optional[Union[ceed.function.FuncBase, ceed.function.CeedFuncRef]] = None, index: Optional[int] = None) None
Adds the function instance
ceed.function.FuncBase
tofunctions
.- Parameters
func – The
ceed.function.FuncBase
to add.after – The
ceed.function.FuncBase
infunctions
after which to add this function, if not None, the default.index – The index in
functions
at which to add this function, if not None, the default.
- remove_func(func: Union[ceed.function.FuncBase, ceed.function.CeedFuncRef]) bool
Removes the function instance
ceed.function.FuncBase
fromfunctions
.
- add_shape(shape: Union[ceed.shape.CeedShapeGroup, ceed.shape.CeedShape]) Optional[ceed.stage.StageShape]
Creates and adds a
StageShape
instance wrapping theceed.shape.CeedShape
shape
to theshapes
. If theshape
was already added it doesn’t add it again.- Params
- shape:
ceed.shape.CeedShape
The shape instance to add.
- shape:
- Returns
The
StageShape
created if the shape wasn’t already added, otherwise None.
- remove_shape(stage_shape: ceed.stage.StageShape) None
Removes a
StageShape
that was previously added toshapes
.
- get_settings_display(stage_widget) Dict[str, Any]
Returns widgets that will be displayed to the user in the stage settings.
These widgets can be used to allow users to further configure custom stages. This is called by the Ceed GUI when the settings are first displayed to the user.
- Parameters
stage_widget – The root settings widget to which the settings will be added as grandchildren by the caller.
- Returns
It should return a dict of the name of each setting mapped to the widget controlling the setting. It will be displayed in two columns: the name followed by the widget on the same row.
- get_stages(step_into_ref: bool = True) Generator[Union[ceed.stage.CeedStage, ceed.stage.CeedStageRef], None, None]
Generator that iterates depth-first through all the stages and children
stages
and yields these stages.- Parameters
step_into_ref – bool If True, when it encounters a
CeedStageRef
instance it’ll step into it and return that stage and its children. Otherwise, it’ll just return theCeedStageRef
and not step into it.
- get_funcs() Generator[Union[ceed.function.FuncBase, ceed.function.CeedFuncRef], None, None]
Generator that iterates depth-first through all the stages and children
stages
and for each stage yields each function fromfunctions
as well as their children functions, recursively.If it encounters a
CeedStageRef
orCeedFuncRef
it’ll enter the original stage/function and yield its children.
- init_stage(t_start: Union[float, int, fractions.Fraction]) None
Initializes the stage so it is ready to be used to get the stage values. See also
init_stage_tree()
andinit_loop_iteration()
. If overriding,super
must be called.If the stage is
can_pre_compute
, this is not used after pre-computing the stage.- Parameters
t_start – The time in seconds in global time.
t_start
will be set to this value.
- init_loop_iteration(t_start: Union[float, int, fractions.Fraction]) None
Initializes the stage at the beginning of each loop.
It’s called internally at the start of every
loop
iteration, except the first. See alsoinit_stage_tree()
andinit_stage()
. If overriding,super
must be called.If the stage is
can_pre_compute
, this is not used after pre-computing the stage.- Parameters
t_start – The time in seconds in global time.
t_start
will be set to this value.
- finalize_stage() None
Finalizes the stage at the end of all its loops, when the stage is done. See also
finalize_loop_iteration()
. If overriding,super
must be called.
- finalize_loop_iteration() None
Finalizes the stage at the end of each loop, including the first and last. See also
finalize_stage()
. If overriding,super
must be called.
- tick_stage(shapes: Dict[str, List[Tuple[Optional[float], Optional[float], Optional[float], Optional[float]]]], last_end_t: Union[float, int, fractions.Fraction]) Generator[None, Union[float, int, fractions.Fraction], None]
Similar to
StageFactoryBase.tick_stage()
but for this stage. This calls internally eitherevaluate_pre_computed_stage()
if the stage was pre-computed (runtime_stage
is not None) orevaluate_pre_computed_stage`evaluate_stage()
when it is not pre-computed.It is an generator that ticks through time and updates the intensity values for the shapes associated with this stage and its sub-stages.
Specifically, at every iteration, a time value is sent to the iterator by Ceed which then updates the
shapes
dict with the intensity values of the shape for the that time-point.The method is sent time step values and it yields at every time step after the shapes dict is updated. The final time that was sent on which it raises
StageDoneException
means that the given time was not used and the stage is done for that time value.- Parameters
shapes – A dict whose keys is the name of all the shapes of this stage and its sub-stages. The corresponding values are empty lists. At every iteration the list should be filled in with the desired color values.
last_end_t – the start time of the stage in globbal time.
- Raises
- StageDoneException:
When done with the stage (time is out of bounds). The time value that raised this was not used.
- evaluate_pre_computed_stage(shapes: Dict[str, List[Tuple[Optional[float], Optional[float], Optional[float], Optional[float]]]], last_end_t: Union[float, int, fractions.Fraction]) Generator[None, Union[float, int, fractions.Fraction], None]
Generator called by
tick_stage()
if the stage was pre-computed. See that method for details.This should not be overwritten, rather one can set
runtime_stage
to desired values and this method will iterate through it.If setting
runtime_stage
manually,apply_pre_compute()
should be overwritten, otherwise it may overwriteruntime_stage
as it attempts to pre-compute again. But it’s generally safer and simpler to customizeevaluate_stage()
instead and have Ceed generated the pre-compute values from it.
- evaluate_stage(shapes: Dict[str, List[Tuple[Optional[float], Optional[float], Optional[float], Optional[float]]]], last_end_t: Union[float, int, fractions.Fraction]) Generator[None, Union[float, int, fractions.Fraction], None]
Generator called by
tick_stage()
in real-time if the stage won’t be pre-computed or before the stage is run if we’re pre-computing the stage. See that method for details.This method can safely be overwritten to set stage-shape values. And if the stage will be pre-computed, this method will still be internally called to get the shape values so pre-computing does not have to be considered at all when overwriting this method.
However,
t_end
must be set with the final stage time before the method ends, otherwise it’ll break the timing. Similarly,t_start
should be set the first time value of the stage. Following is an appropriate customization (assuming those named shapes exist in the GUI):def evaluate_stage(self, shapes, last_end_t): # always get the first time self.t_start = t = yield # we ignore any looping and just use 10 time points. for i in range(10): # r, g, b, a values shapes['my shape'].append( (float(.1 * (t - last_end_t)), .2, (i % 2) * .3, None)) shapes['other shape'].append((.1, .2, (i % 2) * .5, None)) # this yields so GUI can use the change shape colors t = yield # this time value was not used and this effectively makes the # stage 10 samples long, and it ends on the last sample so # that last time will be used as start of next stage self.t_end = t raise StageDoneException
- tick_stage_loop(shapes: Dict[str, List[Tuple[Optional[float], Optional[float], Optional[float], Optional[float]]]], last_end_t: Union[float, int, fractions.Fraction]) Generator[None, Union[float, int, fractions.Fraction], None]
If the stage was not pre-computed, ticks through one loop iteration of the stage yielding the shape values for each time-point.
- pre_compute_functions(frame_rate: fractions.Fraction) List[Tuple[Optional[ceed.function.FuncBase], Optional[List[float]], Optional[list], Optional[float]]]
Goes through all the stage’s functions and pre-computes those that are finite and can be pre-computed.
Returns a list of pre-computed values/original functions, one for each function of the stage. Each item is a 4-tuple of either
(func, None, None, None)
when the function is not finite, otherwise(None, pre_computed_values, logs, end_time)
.This allows only some functions to be pre-computed.
- pre_compute_stage(frame_rate: fractions.Fraction, t_start: Union[float, int, fractions.Fraction], shapes: Set[str]) Tuple[Dict[str, List[Tuple[Optional[float], Optional[float], Optional[float], Optional[float]]]], list, int, Union[float, int, fractions.Fraction]]
If the stage is to be pre-computed,
apply_pre_compute()
uses this to pre-compute the stage intensity values for all the shapes for all time points when the stage would be active.It returns the shape intensity values and data logs for all time points as well as the end time when the stage ended relative to zero time (not
t_start
).
- tick_funcs(last_end_t: Union[float, int, fractions.Fraction]) Generator[Optional[float], Union[float, int, fractions.Fraction], None]
Iterates through the
functions
of this stage sequentially and returns the function’s value associated with the given time passed in for each tick.If the functions were pre-computed, it goes through the pre-computed values, assuming the passed in time is exactly those values used when pre-computing relative to
last_end_t
.
- init_stage_tree(root: Optional[ceed.stage.CeedStage] = None) None
Before the stage is
apply_pre_compute()
and started, the stage and all sub-stages are recursively initialized.Initializes the stage as part of the stage tree so it is ready to be called to get the stage values as part of the tree. It is called once for each stage of the entire stage tree.
- Parameters
root – The root of the stage tree. If None, it’s self.
- apply_pre_compute(pre_compute: bool, frame_rate: fractions.Fraction, t_start: Union[float, int, fractions.Fraction], shapes: Set[str]) None
Pre-computes the intensity values for all the shapes of this stage and/or sub-stages, if enabled.
Depending on
pre_compute
,can_pre_compute
, anddisable_pre_compute
, it pre-computes the full stage and sub-stages, just the stage’s functions or only the sub-stages recursively.
- resample_parameters(parent_tree: Optional[List[ceed.stage.CeedStage]] = None, is_forked: bool = False) None
Resamples all parameters of all functions of the stage that have randomness associated with it as well as
shuffled_order
ifrandomize_child_order
.parent_tree
is not inclusive. It is a list of stages starting from the root stage leading up to this stage in the stage tree.See
FuncBase.resample_parameters()
andcopy_and_resample()
for the meaning ofis_forked
.
- copy_and_resample() ceed.stage.CeedStage
Resamples all the functions of the stage and
shuffled_order
, copies and expands the stage, possibly resamples parameters, and returns the duplicated stage.It copies a stage so that the stage is ready to run in a experiment. First it resamples all the parameters of all the functions that have randomness associated with it and it resamples
shuffled_order
. Then it copies and expends the stage and any sub-stages and functions that refer to other functions/stages. Then, it resamples again those randomized function parameters that are not marked aslock_after_forked
as well asshuffled_order
if notlock_after_forked
. Those will maintain their original re-sampled values so that all the expanded copies of reference functions/stages have the same random values.See
resample_parameters()
andFuncBase.resample_parameters()
as well.
- get_stage_shape_names() Set[str]
Gets all the names of the shapes controlled by the stage or substages. If adding names, you must call
super
to get the builtin shapes.It calls
get_stage_shape_names()
on its children, recursively, so any of them can be overwritten to return arbitrary names (that must be unique among all the shapes).This is used to create the shape logs in the HDF5 data file. Because for each shape we create a Nx4 array, where N is the number of Ceed time frames, and 4 is for the RGBA value for that frame (
shapes_intensity
).By default, it gets the name of the shapes from
shapes
from itself and its children stages. If you’re directly updating the graphics, you can log rgba values by returning additional names here and then setting their values when the stage is ticked, as if it was a real shape. The values will then be accessible inshapes_intensity
.If you set the values of the additional shapes when the stage is ticked, then the values will also show in the stage preview graph so you can use it to display arbitrary rgb data for your stage in the graph. But it must be logged during stage tick (e.g.
evaluate_stage()
), not during the actual graphing inset_gl_colors()
that follows each tick.See the stage example plugins for example usage.
- set_ceed_id(min_available: int) int
Sets the ID of this and any sub objects, each to a number equal or greater than
min_available
and returns the next minimum number available to be used.See
event_data
for more details.- Parameters
min_available – The minimum number available to be used for the ID so it is unique.
- Returns
The next minimum available number that can be used. Any number larger or equal to it is free to be used.
- get_ref_src() ceed.stage.CeedStage
Returns the stage referenced by this object if we are a
CeedStageRef
, otherwise it just returns this stage.Useful to get the referenced stage without having to check whether we are a
CeedStageRef
. I.e.stage.get_ref_src().name
.
- add_gl_to_canvas(screen_width: int, screen_height: int, canvas: kivy.graphics.instructions.Canvas, name: str, quad_mode: str, quad: Optional[int] = None, **kwargs) bool
Should add any stage specific kivy OpenGL instructions that a stage may manually set.
This is called by Ceed when it creates a new experiment to get all the graphics for the stage and substages used during an experiment.
- Parameters
screen_width – Same as
add_manual_gl_to_canvas()
.screen_height – Same as
add_manual_gl_to_canvas()
.canvas – Same as
add_manual_gl_to_canvas()
.name – Same as
add_manual_gl_to_canvas()
.quad_mode – Same as
add_manual_gl_to_canvas()
.quad – Same as
add_manual_gl_to_canvas()
.
- Returns
Whether the stage added custom graphics. If True,
set_gl_colors()
will be called for this stage for every Ceed frame. Otherwise, it is not called. Defaults to returning None.
- set_gl_colors(quad: Optional[int] = None, grayscale: Optional[str] = None, clear: bool = False, **kwargs) None
If
add_gl_to_canvas()
returned True, it is called by Ceed for every time step to allow the stage to update the manually added gl instructions for this frame. In QUAD4X it’s called 4 times per frame, in QUAD121X it’s called 12 times per frame.- Parameters
quad – Same as
set_manual_gl_colors()
.grayscale – Same as
set_manual_gl_colors()
.clear – Same as
set_manual_gl_colors()
.
Warning
For every clock tick, Ceed “ticks” the stage and then updates the graphics based on the values computed during the tick. If the frame is not dropped, the value is then applied to the graphics.
For shapes, Ceed does this automatically, only updating the shapes when the frame is not dropped, for each tick. For these manual gl graphics, the stage should only update the graphics in this method. Normally, every tick is followed by a call to this method to draw. If the frame is dropped, the draw call does not follow the tick. Hence, only draw in this method.
If the stage is pre-computed, then Ceed ticks through the stage before the stage runs. During the stage, tick won’t be called, but this method will still be called so you have to work out the timing prior and apply it during this method.
- remove_gl_from_canvas(canvas: kivy.graphics.instructions.Canvas, name: str, **kwargs) None
Should remove all the gl instructions that was added with
add_gl_to_canvas()
.It is called by Ceed for the root stage and all substages after the experiment is done. It should remove all the instructions added. Instructions added with this
name
will be automatically removed byremove_shapes_gl_from_canvas()
so they don’t have to be removed manually.- Parameters
canvas – The Kivy canvas instance to which the gl instructions was added.
name – The name associated with these OpenGL instructions.
- class ceed.stage.StageShape(stage: Optional[ceed.stage.CeedStage] = None, shape: Optional[Union[ceed.shape.CeedShape, ceed.shape.CeedShapeGroup]] = None, **kwargs)
Bases:
kivy._event.EventDispatcher
A wrapper for
ceed.shape.CeedShape
instances used inCeedStage.add_shape()
to wrap a shape or shape group to be used by the stage.- name: str
The
ceed.shape.CeedShapeGroup.name
orkivy_garden.painter.PaintShape.name
of the instance wrapped.
- keep_dark: bool
Whether this shape will be black during the whole stage. Instead of it taking the color of the stage, it’ll be kept black.
This is useful when the inside of some shape must be black, e.g. a donut. By setting
keep_dark
of the inner shape to True, it’ll be black.
- stage: ceed.stage.CeedStage = None
The
CeedStage
this is associated with.
- shape: Union[ceed.shape.CeedShape, ceed.shape.CeedShapeGroup] = None
The
ceed.shape.CeedShape
orceed.shape.CeedShapeGroup
instance being wrapped.
- get_config() Dict
(internal) used by the config system to get the config data of the shape.
- apply_config(**kwargs) None
(internal) used by the config system to set the config data of the shape.
- class ceed.stage.CeedStageRef(stage_factory: ceed.stage.StageFactoryBase, function_factory: ceed.function.FunctionFactoryBase, shape_factory: ceed.shape.CeedPaintCanvasBehavior, stage: Optional[ceed.stage.CeedStage] = None)
Bases:
object
A stage that refers to another stage.
This is never manually created, but rather returned by
StageFactoryBase.get_stage_ref()
. SeeStageFactoryBase.get_stage_ref()
andceed.stage
for details.- display = None
Same as
CeedStage.display
.
- parent_stage: ceed.stage.CeedStage = None
Same as
CeedStage.parent_stage
.
- stage: ceed.stage.CeedStage = None
The reffered to stage.
- stage_factory: ceed.stage.StageFactoryBase = None
Same as
CeedStage.stage_factory
.
- shape_factory: ceed.shape.CeedPaintCanvasBehavior = None
Same as
CeedStage.shape_factory
.
- function_factory: ceed.function.FunctionFactoryBase = None
Same as
CeedStage.function_factory
.
- get_ref_src() ceed.stage.CeedStage
- ceed.stage.remove_shapes_upon_deletion(stage_factory: ceed.stage.StageFactoryBase, shape_factory: ceed.shape.CeedPaintCanvasBehavior, process_shape_callback) None
Once called, whenever a shape or group of shapes is deleted in the
shape_factory
, it’ll also remove the shape or shape group from all stages that reference it.This is automatically called by the Ceed GUI, but should be manually called if manually creating these factories.
- Parameters
stage_factory – The
StageFactoryBase
that lists all the stages.shape_factory – The
CeedPaintCanvasBehavior
that lists all the shapes.process_shape_callback – For each stage in
stage_factory
that contains the shape,process_shape_callback
will be called with 2 parameters: theCeedStage
andStageShape
instances. The callback may e.g. then hide the shape in the GUI or whatever else it needs.
- ceed.stage.register_all_stages(stage_factory: ceed.stage.StageFactoryBase)
Registers all the internal plugins and built-in stages with the
StageFactoryBase
instance.It gets and registers all the plugins stages under
ceed/stage/plugin
usingget_plugin_stages()
. See that function for how to make your plugin stages discoverable.- Parameters
stage_factory – a
StageFactoryBase
instance.
- ceed.stage.register_external_stages(stage_factory: ceed.stage.StageFactoryBase, package: str)
Registers all the plugin stages in the package with the
StageFactoryBase
instance.See
get_plugin_stages()
for how to make your plugin stages discoverable.Plugin source code files are copied to the data file when a a data file is created. However, it doesn’t copy all files (i.e. it ignores non-python files) so it should be independently tracked for each experiment.
- Parameters
stage_factory – A
StageFactoryBase
instance.package – The name of the package containing the plugins.