kaa.canvas is a second generation canvas API and deprecates kaa.mevas. kaa.canvas uses kaa.evas for most of the heavy lifting and builds a high-level API on top of it, providing support for layout and several other features. Canvas objects know their state, and are able to recreate themselves on any evas canvas, allowing objects to be moved between canvases, and, more importantly, for delayed instantiation, so that an object can be defined and modified before it is added to the canvas.
kaa.canvas provides basic objects like Image, Text, and Rectangle, as well as more advanced objects like TextBlock (for complex multi-line text layout) and Movie (for displaying video on the canvas using kaa.player). It also integrates into the kaa.notifier mainloop so that it is not typically necessary to explicitly render the canvas. Like mevas, kaa.canvas implements container objects, which can hold any kind of object (including other containers). Unlike mevas, however, kaa.canvas provides a basic layout model that borrows from html as well as gtk's box model, which allows you to implement aspect- and resolution-independent layouts. This is particularly important as interfaces should be built to work on both 4:3 and 16:9 displays at varying resolutions.
Each canvas object has a set of properties that are set at any point after the object's instatiation, and synchronized immediately prior to rendering. Properties set on containers apply to their children as well. For example, if the container's
color property is set to
(255, 255, 255, 128) (a 4-tuple representing red, green, blue, and alpha values) then all children of the container will have that color value multiplied with their own. So if a child of that container had its own color property also set to
(255, 255, 255, 128), the final, rendered color value would be
(255, 255, 255, 64).
kaa.canvas has the following object hierarchy:
The following table outlines the properties supported by each object and their purpose. Subclasses inherit all properties of their superclasses:
|visible||boolean: True if the object is visible on the canvas, False if not. Default:
|layer||integer: layer or z-index of the object; the greater the number, the higher the object is rendered on the stacking order. Objects at the same layer value will be layered in the order in which they were added to their parents. Default:
|color||4-tuple or string: A tuple representing the red, green, blue, alpha color of the object, or a string in HTML-style notation, such as #ff00ff80 (which represents the tuple (255, 0, 255, 128)). Default:
|expand||boolean: Used for children of Box objects. If True, the object will occupy the maximum amount of space the box permits; if False, will occupy only as much space as necessary for the object. Default:
|width||integer or string: A value representing the width of the object. Width can be a fixed (integer) size (in pixels), or a relative (percentage) size. Percentage values (e.g. "75%") are relative to the object's container. Width may also be
|height||integer or string: Same as the
|left||integer or string: A value representing where the left edge of the object is to be positioned, relative to its container. May be a fixed position (integer value) in pixels, or a string representing the percentage of the object's container's width. Default:
|top||integer or string: A value representing where the top edge of the object is to be positioned, relative to its container. May be a fixed position (integer value) in pixels, or a string representing the percentage of the object's container's height. Default:
|right||integer or string: Same as the
|bottom||integer or string: Same as the
|hcenter||integer or string: Same as the
|vcenter||integer or string: Same as the
|clip||2-tuple or |
|filename||string: The path to a file from which to load the image. Default:
|image||imlib2.Image object: Wraps an existing Imlib2 image object. Default:
|pixels||4-tuple: A tuple holding (data, width, height, format) where data is a buffer object or memory address, and format is
|data||2-tuple: A tuple holding (data, copy) where data is a buffer object or memory address, and
|dirty||boolean: Indicates whether the image set by
|has_alpha||boolean: Indicates whether the image has a valid alpha channel. Default:
|detached||boolean: If True, the movie is detached from the canvas and fills the canvas window. If False, the movie is attached to the canvas. Default:
|font||2-tuple: A tuple in the form
|text||string: A string containing the text to render. Default:
|markup||string: HTML-style markup containing the text to render. This text may contain template variables in the form @variable@ which can be defined by calling
|fontpath||list: A list of paths from which to load true-type fonts. Default: all directories within /usr/share/fonts. Methods:
Here's the Hello World of kaa.canvas:
import kaa, kaa.canvas # Create a 640x480 window into which the canvas will be added. canvas = kaa.canvas.X11Canvas((640, 480)) # Create a Text object with the text "Hello world!" hello = kaa.canvas.Text("Hello world!") # Now add the new text object to the canvas. canvas.add_child(hello) # Enter the mainloop, which implicitly displays the window and renders the canvas. kaa.main()
This code creates an X11 window that's 640x480 in size, and displays "Hello world!" at the default font (which is currently hardcoded as arial/24) at the top left corner of the window. X11Canvas subclasses Canvas which subclasses Container.
add_child is a method of Container, and causes the child (in this case, the Text object) to be adopted by the container (in this case, the Canvas itself). The next example builds on the previous one by adding a background image to the canvas, and instead of displaying the text at the default position ((0, 0), or top left corner of the container), it will center the text:
import kaa, kaa.canvas canvas = kaa.canvas.X11Canvas((640, 480)) background = kaa.canvas.Image("background.jpg") canvas.add_child(background) hello = kaa.canvas.Text("Hello world!") hello.move(hcenter = "50%", vcenter = "50%") canvas.add_child(hello) kaa.main()
Container.add_child allows most of the common properties (left, top, width, height, layer, color, name, clip, etc.) to be set by keyword arguments, so an object can easily be added and adjusted with minimal code.
add_child will also return the object that was provided in the first argument. The next example shortens the above code to use the
add_child kwargs, and also adds a new feature which will change the text after 2 seconds to say something else:
import kaa, kaa.canvas canvas = kaa.canvas.X11Canvas((640, 480)) canvas.add_child( kaa.canvas.Image("background.jpg")) text = canvas.add_child(kaa.canvas.Text("Hello world!"), vcenter="50%", hcenter="50%") def change_text(text): text.set_text("This is a kaa.canvas example.") kaa.notifier.OneShotTimer(change_text, text).start(2) kaa.main()
When the text is changed (inside the
change_text callback), the text object's size becomes wider (the string is longer than the previous one), so the object's position is recalculated so that the object remains centered.
kaa.canvas offers basic layout support, primarily in the form of relative values for position and size (for example, percentage values), as well as containers, and horizontal and vertical packing boxes. A Container object is simply a collection of objects that are logically grouped. When you move a container object, all children of the container move as well. When you hide a container, all children of the container become hidden. A container does not enforce a layout policy on its children, except inasmuch as the maximum size allowed for children is the container's own size. For example, if a container's size is fixed to 320x200 (that is, width of 320 pixels and a height of 200 pixels), and a child of that container specifies a width of 100%, the child's width will be computed as 320. Children of containers may overlap, and their stacking order is determined by the order they were added to the container, as well as their
Containers have a default width and height of
auto, which means that the container "shrinkwraps" to fit its children. However, if a child requests a size of "100%" when its container's size is set to
auto, the child is granted the full extent of its container's parent. For example, if a container of size
auto is added to a canvas of size 640x480, and an image of width "100%" is added to that container, the image will be 640 pixels wide, thus causing its parent's computed width to also be 640 pixels (assuming no other children of that container have a width greater than 640 pixels). Similarly, if the image is 50% wide, its computed width will be 320 pixels, and its container will be 320 pixels. This allows you to position containers relative to their size (such as centered, or bottom or right justified) and the container will be properly aligned as it grows and shrinks to fit its children.
Children are aligned relative to their parent container. So, if you have a container of size
auto that is parent to an image object and a text object with the properties hcenter="50%" and vcenter="50%", the text object will be centered on the image, assuming the image is larger than the text object. Here's an example that illustrates this:
import kaa, kaa.canvas canvas = kaa.canvas.X11Canvas((640, 480)) canvas.add_child(kaa.canvas.Image("background.png")) container = canvas.add_child(kaa.canvas.Container(), hcenter = "50%", vcenter = "50%") frame = kaa.canvas.Image("frame.png") frame.set_border(30, 30, 30, 30) container.add_child(frame, width = "75%", height = "50%") text = kaa.canvas.Text("Text inside a container.") container.add_child(text, hcenter = "50%", vcenter = "50%") kaa.main()
Box is a subclass of
Container and implements a layout policy for non-overlapping children. You'll never use Box directly, but rather one of
HBox. VBox lays out its children in rows, whereas HBox lays out its children in columns. Because boxes are containers, they will also shrinkwrap their children unless a size is explicitly specified for the box. Children whose
expand property is False will use only the minimum space in the box required for the child, whereas those with
expand set to True will all available space in the Box. The
expand property also modifies how relative sizes of children are interpreted. If a child specifies a width of 30% and has expand set to False, it will be given 30% of the box's total width. However, if expand is set to True, first all the space for other children that are not expanded is allocated, and the remaining space is distributed evenly for those children that are expanded. Thus, an expanded child that specifies a width of 30% will be given 30% of the available space, rather than of the box's total width.
For example, if an HBox has two children, the first child which requests a width of 30% and has expand set to False, and the second child which requests a width of 100% and has expand set to True, then this will result in a 2-column layout where the first column occupies 30% of the box, and the second column occupies 70% of the box. For such a layout, it would be preferable to explicitly specify 30% and 70% for non-expanded children, however the box layout semantics make the specifications equivalent. The following code illustrates this layout:
import kaa, kaa.canvas canvas = kaa.canvas.X11Canvas((640, 480)) box = canvas.add_child(kaa.canvas.HBox()) box.add_child(kaa.canvas.Rectangle(), width="30%", height="100%", color = "#ff0000") box.add_child(kaa.canvas.Rectangle(), width="70%", height="100%", color = "#0000ff") kaa.main()
By default, objects are not clipped, and if they exceed the bounds of their container, the "spill-over" or overflow will still be visible. (A caveat to this is the Text object, which is clipped to its container by default.) For example, if you create a container with size 320x200 and then add an rectangle to that container that is 640x480, the whole rectangle will be visible, but if you center the container, it will be centered as if its size was 320x200. The best solution is to ensure that children of a container will not overflow its container's bounds by using relative sizes. However, if this is not possible, the object can be clipped by specifying its
Text objects are clipped by default. If clipping occurs, the last one or two characters of the text will be faded from opaque to transparent, rather than being abruptly clipped.
Objects can be clipped individually by specifying a clip region in the form
(pos, size), or, in other words,
((left, top), (width, height)). The
clip property for Container objects may also be
auto, which is equivalent to
((0, 0), ("100%", "100%")), and will cause all children to be clipped to the container's bounds. Width and height may also be negative, in which case they are relative to the right or bottom edge of the object. The following example shows two fixed-width containers that are centered, one which is not clipped, and the other which is.
import kaa, kaa.canvas canvas = kaa.canvas.X11Canvas((640, 480)) canvas.add_child(kaa.canvas.Image("background.png")) c = canvas.add_container(width=100, height=100, hcenter="50%") c.add_child(kaa.canvas.Rectangle(), width=200, height=200, color="#44ffff55") c.add_child(kaa.canvas.Text("Text and rect will overflow"), clip = None) c = canvas.add_container(width=100, height=100, clip="auto", hcenter="50%", top=300) c.add_child(kaa.canvas.Rectangle(), width=200, height=200, color="#ff44ff55") c.add_child(kaa.canvas.Text("Text is clipped")) kaa.main()
Canvas objects can also be constructed using XML. Objects map to elements, and, for the most part, object properties map to element attributes. A canvas may be populated by calling the
Canvas.from_xml method, or objects instantiated by calling
kaa.canvas.xml.get_object_from_xml(). For example, the second Hello World example earlier can be rewritten in XML:
<canvas class="hello"> <image file="background.png" /> <text hcenter="50%" vcenter="50%">Hello world!</text> </canvas>
And, if that XML were saved to a file called
canvas.xml, this code would create a canvas object from the XML description:
import kaa, kaa.canvas canvas = kaa.canvas.X11Canvas((640, 480)) canvas.from_xml("canvas.xml", classname = "hello") kaa.main()
In the above example, the
text elements refer to Image and Text objects respectively, and the
file properties all behave as outlined in the properties table. For properties where boolean values are required, the attribute value can be
no to represent a False value, or
yes to represent a True value. Colors are specified using the HTML-style notation
#rrggbbaa (where 'aa' specifies alpha level and is optional). Otherwise, where tuples are expected, such as in the
border properties, a space-delimited sequence of values can be specified.
kaa.canvas.xml.get_object_from_xml both accept a classname parameter, and a path parameter which defines a list of paths on the filesystem. Where path names are specified in XML attributes (such as the
file attribute for the
image element), the current working directory is first checked, and then each path specified in the path list is checked for the filename specified.
Objects can be named using the
name attribute, and subsequently retrieved using
Canvas.find_object so that they may be modified programmatically. If an object's name begins with a dot (
.), then its name is appended to its container's name. For example, if a vbox element has a name of
box and a child has a name of
.image, the image object can be gotten by calling
This example displays the time on the canvas and updates every second:
<canvas class="clock"> <image file="background.png" /> <text hcenter="50%" vcenter="50%" name="time">Current time goes here</text> </canvas>
import time, kaa, kaa.canvas canvas = kaa.canvas.X11Canvas((640, 480)) canvas.from_xml(xml, classname = "clock") def update_clock(text): text.set_text(time.strftime("%I:%M:%S %p")) kaa.notifier.Timer(update_clock, canvas.find_object("time")).start(1) kaa.main()
The canvas Movie object relies on kaa.player, and provides the same interface as any kaa.player object, except that the movie may be rendered as a canvas object. This state is referred to as attached. When the movie is detached, the video stops rendering to the canvas object and begins rendering to an external window. In the case of X11, this external window is a subwindow of the X11Canvas window and expands to fit this window. This allows seamless integration, switching between the canvas view and the movie view (using an Xv overlay, for example) in a single window.
The movie object may be manipulated like any other canvas object, including size, clip, and alpha level. This makes any manner of transition effects possible, for example, a movie that scales from a small, thumbnail view up to fill the whole canvas, just before it is detached.
This example creates a canvas and plays the movie specified on the command line, centered on the canvas and slightly transparent. The user can hit 'f' on the console to toggle between detached and attached mode.
import os, sys, kaa, kaa.canvas canvas = kaa.canvas.X11Canvas((640, 480)) canvas.add_child(kaa.canvas.Image("background.png")) canvas.add_child(kaa.canvas.Text("Now playing: " + os.path.split(sys.argv))) movie = kaa.canvas.Movie(sys.argv) canvas.add_child(movie, width="70%", aspect="preserve", hcenter="50%", vcenter="50%") movie.set_color(a = 100) movie.play() def handle_key(key, movie): if key == "q": raise SystemExit elif key == "f": movie.set_detached(not movie.get_detached()) kaa.signals["stdin_key_press_event"].connect(handle_key, movie) kaa.main()
If the player supports it, Movie instances will have an
osd member, which is a PlayerOSDCanvas instance. This behaves like any other canvas, and allows rendering an overlay to the video when it is in detached mode.
canvas.TextBlock allows for complex, styled, multi-line layouts that can be specified using an XML syntax. Text may be given various effects (such as underlined, outline, shadow, glow), aligned, and adjusted via margins. The markup may also act as a template, where template variables are specified within the markup text in the form
@variable. Variables may be defined by calling
Here's an example:
import time, kaa, kaa.canvas canvas = kaa.canvas.X11Canvas((640, 480)) canvas.add_child(kaa.canvas.Image("royale/background.png")) text = canvas.add_child(kaa.canvas.TextBlock(), width = "50%", hcenter = "50%", vcenter = "50%) text.set_markup(""" <format align="right"> <format style="far_shadow" shadow_color="#0004" font="tahoma" font_size="90" color="#fff5"> mebox </format><br /> <format font="trebucbd" font_size="16"> <format color="#8cbaf7">@date@</format><br /> @time@ </format> </format> """) text.set_template_variable(date = time.strftime("%A, %B %d"), time = time.strftime("%I:%M:%S %p")) kaa.main()