Skip to content
adammhaile edited this page Nov 8, 2015 · 6 revisions

Python has some great functionality around being able to inspect the contents of a module or package without prior knowledge, but PixelWeb still needs a little help loading previously unseen modules. That help comes in the form of the MANIFEST property.

MANIFEST is just a list Python dictionaries that contains information telling PixelWeb what is available in the module, what the parameters are, and how to load everything. It should always be placed at the very end of a module file, in the global scope. You can build the MANIFEST property by hand, but for animations there is a quicker way. Once PixelWeb is installed, simply run the following at the console:

pixelweb_genmanifest <module_file_path>

This will scan the file for valid animations and automatically append a stub MANIFEST to the end of the module file, if it finds any. Once complete, you will still need to fill in any information that it could not automatically pull from the file.

Once your modules have the proper MANIFEST entries, they need to be loaded by PixelWeb. Launch the UI (typically http://localhost:8080) and navigate to the "App Config" page. Click the plus icon next to "Module Directories" and enter the path to the directory where your module files are saved, as seen below. It can be a mix of animations, controllers, drivers, and presets. They will all be loaded. Just be sure to restart PixelWeb after added a new module directory.

A basic MANIFEST and it's corresponding animation will look like this:

class MyAnimation(BaseMatrixAnim):
    def __init__(self, led, fill = colors.Red, growth=2, clear = False):
        super(MyAnimation, self).__init__(led)
        #init here

    def step(self, amt = 1):
        #anim code here

MANIFEST = [
        {
            "id":"MyAnimation",
            "class":MyAnimation,
            "type": "animation",
            "display": "My Animation",
            "controller": "matrix",
            "desc": "My First Animation",
            "params": [{
                "id": "fill",
                "label": "Fill Color",
                "type": "color",
                "default": (255,0,0),
                "help":"Color to fill the screen"
            },{
                "id": "growth",
                "label": "Growth",
                "type": "int",
                "default": 2,
                "help":"How fast to fill the screen"
            },{
                "id": "clear",
                "label": "Clear After",
                "type": "bool",
                "default": False,
                "help":"Enable to clear screen when complete"
            },]
        }
]

Note that pixelweb_genmanifest can only infer default value or value data-type if a default parameter is provided in the class definition. If not, you will have to fill that information in manually.

Controllers, drivers, and presets currently have no automatic generation tool, but can be created manually without too much work. For examples of controller and driver MANIFEST entries, you can view the entries currently used in BiblioPixel (at the end of the file):

Also note that the root of MANIFEST is a Python list. By adding multiple dictionaries in the list, you can add PixelWeb definitions for any other animation, driver, or controller that lives in the module. pixelweb_genmanifest will only, however, pick up animations.

MANIFEST Keys

The items in each MANIFEST dictionary entry are shown below. Note that some entries are specific to certain object types (animation, controller, driver, or preset) and are noted on the entry.

id

Unique ID name of the entry, which is used by PixelWeb for tracking. Any string is valid and is not human read at any point. For uniqueness, a format like <obj_type>.<developer_name>.<obj_name> is recommended. For example: anim.ManiacalLabs.Bloom or driver.ManiacalLabs.AllPixel

class

The class object that this entry represents.

type

Type of object represented by the manifest entry. Valid options are:

animation
controller
driver
preset

See the Presets section below for more details on the preset type.

display

Display string to show in the PixelWeb UI.

controller

For animations only. Specifies the controller type required by the animation. Built in options are:

  • strip - LEDStrip
  • matrix - LEDMatrix

Custom controllers or controller pre-configs can specify any control_type value and animations that use that should match that same string here.

control_type

For controllers only, including presets representing a controller. Used for animation type filtering. Aside from the built in strip and matrix control types, any string value can be used here as long as the controller entry in the corresponding animations matches this value. For example, this value could be set to circle if you had created circular display preset (using LEDCircle for example). Then any animations using BaseCircleAnim would need their controller entry set to circle.

preset_type

For presets only. When the type entry is set to preset, this entry must be set to one of:

animation
controller
driver

See the Presets section below for more details on the preset type.

preconfig:

For presets only. Specifies the parameter dictionary to be passed into the given class' __init__ method. This is intended to be used for parameter types that do not have a type compatible with any of the parameter type options. May be either a dictionary or a reference to a method that returns a dictionary.

See the Presets section below for more details on the preset type.

desc

Tool-tip detail information to show in PixelWeb UI.

params

Contains a list of dictionaries, each of which specify a control to be shown in the PixelWeb UI. Each parameter requires id and label entries, the rest are optional. The available parameter entries are:

id The exact parameter name as specified in the Class __init__ method definition.

label Label string to display in the PixelWeb parameter UI

type Type of parameter value represented by the entry. See Parameter Types below for more details.

default Default value used by the parameter.

help Tool-tip pop-up text to display when hovering over the parameter in the PixelWeb UI.

group String value specifying a grouping in the PixelWeb parameter roll-up view to place the parameter. For example: "group":"Advanced" will place the parameter in a roll-up view with the title "Advanced" with any other parameters that have the same group. By default, all parameters are in the "Basic" group, which need not be specified. "Basic" is always the top-most, open roll-up when the parameter UI loads. All other groups are closed by default. This is particularly helpful for displaying basic parameters that most users will need but then hiding more advanced or complex parameters.

presets

Specifies a list of dictionaries, each representing preset options, just as if you had saved a preset in the PixelWeb UI and it will show up in the saved presets drop-down. For example:

"presets" : [
    {
        "display": "Blue Path, Orange Ant",
        "desc": "Demo Built-In Preset",
        "config":{
            "pathColor": [0,0,255],
            "antColor": [255,143,0]
        },
        "run": {
            "amt": 1,
            "fps": 30,
            "max_cycles": 1,
            "max_steps": 0,
            "untilComplete": False
        }

    }
]

config is a dictionary with an entry representing one or more of the specified params for the MANIFEST entry.

run is for animations only and specifies the run parameters to be used when starting the animation. The above entries are the only valid options.

Parameter Types

Below are the valid parameter type values and details on any associated entries that can be added to the parameter dictionary to control its use.

int

{
    "id": "number",
    "label": "Integer",
    "type": "int",
    "min": 1,
    "max": 64
    "default": 16,
    "help": "Integer Value",
}

Specifies an integer input value. min and max (inclusive) can also be specified and the PixelWeb UI will constrain the value.

float

{
    "id": "number",
    "label": "Floating",
    "type": "float",
    "min": 1,
    "max": 64
    "default": 16,
    "help": "Float Value",
}

Specifies a float input value. min and max (inclusive) can also be specified and the PixelWeb UI will constrain the value.

bool

{
    "id": "useColor",
    "label": "Boolean",
    "type": "bool",
    "default": False,
    "help": "True/False Value",
}

Specifies a True/False value. Will be displayed as a simple labeled toggle.

str

{
    "id": "device",
    "label": "SPI Device Path",
    "type": "str",
    "default": "/dev/spidev0.0",
}

Specifies string value.

str_multi

{
    "id": "image_files",
    "label": "Image Files",
    "type": "str_multi",
    "default": [],
    "help":"Image files to load",
}

Specifies a list of string values. Values can be dynamically added and removed in the PixelWeb UI.

color

{
    "id": "fill",
    "label": "Fill Color",
    "type": "color",
    "default": (255,0,0),
}

Specifies a color value which can be selected with a graphical color selector in the PixelWeb UI. Values are represented as a 3-value RGB tuple (pure Red in the above example).

combo

{
    "id": "value",
    "label": "Choose Value",
    "type": "combo",
    "options": {
        0: "RGB",
        1: "RBG",
        2: "GRB"
    },
    "default": 0
}

Displays a drop-down control in the PixelWeb UI. The options entry is a dictionary specifying what to display in the drop-down. Each key is the data that will be returned when selected and the dictionary value is what will be displayed in the drop-down. If the data value is more complicated than a number or string (such as a list), you can specify an options_map like this:

"options": {
    0: "RGB",
    1: "RBG",
    2: "GRB"
},
"options_map": [
    [0, 1, 2],
    [0, 2, 1],
    [1, 0, 2]
],

multi

{
    "help": "Colors",
    "id": "rain_colors",
    "label": "Colors",
    "type": "multi",
    "controls": {
        "default": (0,255,0),
        "help": "Drop color",
        "label": "Color",
        "type": "color"
    },
    "default": [(0,255,0), (255,0,0)]
}

Displays multiple controls of any other type under a single heading. The controls entry can be a single dictionary as show above containing a parameter definition, just like any other. In this case you will be able to have one or more controls of that type and dynamically add or remove them in the PixelWeb UI. If the default entry is provided, it must be a list of size 1 or greater, with each value being of the type specified by the controls definition.

Optionally, you can instead define controls as a list of parameter definitions. In this case with will show just those control types and add/remove will not be available, as shown below.

{
    "help": "Image placement offset",
    "id": "offset",
    "label": "Offset",
    "type": "multi",
    "controls": [{
        "label": "X",
        "type": "int",
        "default": 0
    },{
        "label": "Y",
        "type": "int",
        "default": 0
    }],
    "default": [0,0]
}

In this case, if the default entry is provided it must be a list of exactly the same size as the controls list which each value the same as that represented by each control definition, in the same order.

Optionally, type can be specified as "multi_tuple". This will force contained values of the color, str_multi, and the returned list itself to be converted to a tuple before being passed to the class object.

Preset Entries

Preset manifest entries typically represent something that requires parameters for which there is currently no defined parameter type. For example, if you wrote an animation that takes a Python object from another module as an input, such as a numpy object. Or, as another example, PixelWeb does not yet have direct support for setting up the required pixel map when using the multiple driver support in BiblioPixel. In this case, you can use a preset MANIFEST entry to setup your controller with the pixel map manually and then have PixelWeb load that manual setup.

Preset Example: Controller

Below is the controller preset used by our Colossus LED display. The display itself is build of two 25x25 pixel sub displays and uses two AllPixel controller boards. Therefore, to make everything appear as a single display to BiblioPixel we need to generate the pixel map using MultiMapBuilder, as shown below.

from bibliopixel.led import LEDMatrix, mapGen, MultiMapBuilder, MatrixRotation

def genDisplayParams():
    gen = MultiMapBuilder()
    gen.addRow(mapGen(25, 25, rotation = MatrixRotation.ROTATE_90, vert_flip = True))
    gen.addRow(mapGen(25, 25, rotation = MatrixRotation.ROTATE_90, vert_flip = False))

    params = {
        "width": 25,
        "height": 50,
        "coordMap": gen.map,
    }
    return params

MANIFEST = [
    {
        "id":"controller.ManiacalLabs.Colossus",
        "class":LEDMatrix,
        "type": "preset",
        "preset_type": "controller",
        "control_type": "matrix",
        "display": "Colossus",
        "desc": "Colossus 25x50 Display",
        "params": [
            {
                "id": "threadedUpdate",
                "label": "Threaded Update",
                "type": "bool",
                "default": True,
                "help":"Enable to run display updates on a separate thread, which can improve speed."
            },
            {
                "id": "masterBrightness",
                "label": "Master Brightness",
                "type": "int",
                "min": 1,
                "max": 255,
                "default": 255,
                "help":"Master brightness for display, 0-255"
            }
        ],
        "preconfig": genDisplayParams
    }
]

As you can see above; type is "preset", preset_type is "controller", and control_type is still "matrix". There is no need to use a different control_type value as we are still referencing an LEDMatrix instance.

Nearly everything else is the same. There's even a params entry like normal, since those parameters are still items in the LEDMatrix __init__ that should be user editable. But where it deviates is the preconfig entry. In this case, it is method reference to genDisplayParams, defined earlier in the file. The reason for doing this as opposed to directly giving it the dictionary is that now the code in genDisplayParams is only ever run when the preset is selected and loaded, instead of every time the file is scanned when PixelWeb launches. Because MultiMapBuilder is being used to run some actual code to generate the values, instead of them just being static, this is greatly prefered.

However, if the desired values were truly static, it is perfectly safe to simply include that dictionary as the preconfig value instead of the method reference.

When the preset is actually loaded by PixelWeb, it takes the values set by the user from params and merges them with those in preconfig (or generated by the given method) and passes the result along to your specified class __init__ method. This allows loading a very complex controller configuration with a little work up front and just a couple mouse clicks in PixelWeb.

Preset Example: Animation

This example shows one of the preset files also used by our Colossus display for loading some game animations into PixelWeb. These are unable to be loaded as a normal animation because the BaseGameAnim class requires an instance of a class inheriting from bibliopixel.gamepad.BaseGamePad as one of the parameters, in this case the SerialGamePad object, which connects to a USB Serial based controller.

from BiblioPixelAnimations.game.flappy import Flappy
from BiblioPixelAnimations.game.Tetris import Tetris
from bibliopixel.serial_gamepad import SerialGamePad

def genParams():
    return {"inputDev": SerialGamePad()}

MANIFEST = [
    {
        "id":"flappy",
        "class":Flappy,
        "type": "preset",
        "preset_type": "animation",
        "display": "Flappy Pixel",
        "desc": "Low-Res Flappy Bird Clone",
        "controller": "matrix",
        "params": [],
        "preconfig": genParams
    },
    {
        "id":"tetris",
        "class":Tetris,
        "type": "preset",
        "preset_type": "animation",
        "display": "Tetris",
        "desc": "Low-Res Tetris Clone",
        "controller": "matrix",
        "params": [{
            "default": False,
            "help": "On for the harder, more Maniacal version.",
            "id": "evil",
            "label": "Maniacal Mode",
            "type": "bool"
        }],
        "preconfig": genParams
    }
]

Everything works just like in the above example, except preset_type is now "animation" and control_type is swapped for controller, but with the same value of "matrix". Again, we use a method reference for preconfig because then SerialGamePad() is instantiated, it tries to connect to the serial device. We only want this to happen when the animation loads, so using genParams() keeps it out of scope until needed.

Also, not that in this case, the MANIFEST actually contains more than one entry. You can place as many entries as you would like in a single MANIFEST, enabling everything to be in one file.