Integrate your device
This library was initially designed to support the Pixoo64 device. However early on the development I realized that the library could be easily extended to support other devices, so I decided to make it as flexible as possible. Now the library is designed to be renderer agnostic, meaning that you can create your own renderer and use the library with any device (physical or virtual) you want. This is mainly done by extending the Renderer
class and implementing the methods that are needed for the device to work.
The Renderer
class
The Renderer
class is the base class for any renderer that you want to implement. It has a set of attributes and methods that are needed for the library to work, and some of them are mandatory.
Attributes
All the required attributes are marked with an exclamation icon.
_size
: The size of the device in pixels._max_frames
: The maximum number of frames that the device can handle._pizzoo
: The instance of thePizzoo
class that is using the renderer. It is used to access the methods of thePizzoo
class._debug
: The debug mode of the renderer. If set toTrue
, the renderer may print debug information on the console._address
: The address of the device. This is mainly used if your device needs to connect to any IP or physical address.
Any other attribute that you want/need to add to make your renderer work is up to you.
Methods
All the methods that throw a NotImplementedError
if not implemented are marked with an exclamation icon.
__init__
: The initialization method of the renderer. Here you may call thesuper().__init__()
method and initialize your own renderer with the needed attributes and setup.get_size
: This method returns_size
by default, can be overriden to return a different size or to calculate it.get_max_frames
: This method returns_max_frames
by default, can be overriden to return a different number of frames or to calculate it.get_settings
: This method should return a dictionary with the settings of the renderer. By default it throws aNotImplementedError
, as this is dependent on the renderer.set_brightness
: This method should set the brightness of the renderer. By default it throws aNotImplementedError
, as this is dependent on the renderer.switch_frame
: This method should switch on/off the renderer. By default it throws aNotImplementedError
, as this is dependent on the renderer.render
: This method should render the full animation buffer on the renderer. By default it throws aNotImplementedError
, and is the main method that you need to implement, as this determines how the renderer will show the animation.compile_node
,compile_node_root_options
andrender_template_items
: These methods are related to template compilation and are not required at all for integrating a renderer, but if you want to extend your own nodes or options, you may need to implement them. I show you how in the next optional section.
Extending template rendering capabilities
The library has a built-in compiler that supports an XML/HTML-like language for template processing. This facilitates the design of UIs with relative positioning, defined areas, and reusable components, helping you create fast and reusable interfaces.
This compiler is generic, as it transforms the nodes in drawing methods that are then drawn into a frame (Right now template rendering only works for static images, not animations, so only on one frame). However, you can extend the compiler to support your own nodes and options, so you can react to your own custom nodes, and these nodes will be simply ignored by any other renderer that has no support.
Processing root node attributes
At the top of any renderer class, you can implement the compile_node_root_options
method. This method is called when the root node (<pizzoo>
) is processed, and it receives an options
dictionary with some root attributes. This method should return a list with tuples (function, {args})
that will be executed on the render phase. Additionally any other side-effect or action can be done here.
The options
dictionary has the following keys:
brigthness
: The brightness of the renderer.turn_screen
: If the screen should be turned on for starting the render.notification
: If the renderer should trigger a notification system (Sound or similar).clear
: If the renderer should clear the screen before rendering.
Current Pixoo64 renderer implementation
def compile_node_root_options(self, options):
result = []
if options['brigthness'] is not None:
result.append((self.set_brightness, {'brightness': options['brigthness']}))
if options['turn_screen'] is True:
result.append((self.switch, {}))
if options['notification'] is True:
result.append((self.buzzer, {'duration': 2}))
return result
Implementing a custom node
Let's see as an example how the date
node is implemented in the included ImageRenderer
. This node is there to emulate the network updated date
command of the Pixoo64Renderer
, that it's not available on the ImageRenderer
as this is a static image renderer.
First a new case should be added to the compile_node
method (That you must implement) of your Renderer
class.
Compiling a <date>
node on your renderer
def compile_node(self, node, parent, inherited_props, node_props):
tag = node.tag
attributes = {}
abs_x, abs_y = node_props['abs_coords']
if tag == 'date':
attributes['color'] = get_color_rgb(node.attrib.get('color', '7'))
date_format = node.attrib.get('format', 'DD/MM/YYYY')
# This is a simple way to convert the Pixoo64 format to a python datetime format
date_format = date_format.replace('DD', '%d').replace('MM', '%m').replace('YYYY', '%Y').replace('WWW', '%a')
text = datetime.now().strftime(date_format)
return (self._pizzoo.draw_text, {'text': text, 'xy': (abs_x, abs_y), **attributes})
The compile_node
method receives the node, the parent node, the inherited properties, and the node properties. The node is a lxml
node, so you can access the tag and the attributes of the node. In this case, we are checking if the tag is date
, and if it is, we are getting the color and the format of the date. Then we are converting the format to a python datetime format and getting the current date in that format. Finally, we are returning a tuple with the drawing method and the attributes that will be used to draw the text.
In case that you don't return a function on the first member of the tuple, the compiler will include the item on a list that will be returned to the renderer after the render phase has finished (Through the render_template_items
method), so you can decide the logic there.
Custom existing renderers
Aside from the discussed Pixoo64Renderer
, there are two other simple renderers included:
-
ImageRenderer
: This renderer is a static image renderer that can be used to test the library without any physical device. It will render the full animation buffer on a single frame and show it on a window. -
WindowRenderer
: A simple and basic renderer that will render the full animation buffer on a single frame and show it on a window. This renderer is used to test the library without any physical device, and on the current iteration lacks a lot of features and functionality.
You can use these renderers as a base to create your own renderer, or you can create a new renderer from scratch.
Submiting a renderer
If you have created a new renderer that you think could be useful for the community, you can submit it to the library. To do so, you can create a pull request on the GitHub repository and I will review it. If the renderer is useful and well implemented, I will include it in the library and give you credit for it.