This is a layout manager I wrote to help me break up the display of a Pimoroni Inky-pHAT into seperate drawable areas.
You might find other uses for it - let me know if it's handy!
rpi-inky-layout at the Python Package Index
The outline process is:
- Use the
Layout
class to create a top-level Layout object. - Next, add new layers to a
Layout
- it automatically resizes the otherLayout
s at the same level. - Take each added sublayer
Layout
, and use itssize
to define a new PILImage
to draw on. - Draw on the image, Set the
Image
on the appropriateLayout
. - Invoke the top-level
Layout
'sdraw()
method to merge them all together, drawing borders as necessary.
And you end up with a nicely-spaced regions.
Create a new Layout:
layout = Layout( size, packingMode, border)
where:
size
is a 2-part tuple of the form(width, height)
.packingMode
is the packing direction: currently eitherh
(horizontal) orv
(vertical).border
is either a) the width of border (colour defaults to2
) or b) a 2-tuple containing(width, colour)
.
Next, add multiple sub-layouts to layout
sublayout1 = layout.addLayer()
sublayout2 = layout.addLayer()
Then, for leach sublayout, use sublayout.size
as the size of the image to draw on.
im1 = Image.new(mode, sublayout1.size, bgColour)
...
Draw on the image; Set it back on the sublayout:
sublayout1.setImage(im1)
...
And finally, draw the top level layout and write to file:
layout.draw()
layout.write(filename)
You can then do what you want with the file, e.g., load it onto an Inky display using setImage
Here's a detailed example.
Assuming you already have pip3 installed:
python3 -m pip install rpi-inky-layout
And of course, later upgrade with:
python3 -m pip install --upgrade rpi-inky-layout
Ready to use!
Import the core classes from rpi_inky_layout
package:
from rpi_inky_layout import Layout, Rotation
You will also need some sort of image manipulation library, such as
pillow
(and rpi-inky-layout
uses the pillow
library internally anyway):
from PIL import Image, ImageDraw
Optionally, if you're using it with Pimoroni's Inky display library (its original purpose), then you will probably want to import that, too:
from inky.auto import auto
Create the top-level Layout
:
topLayout = Layout((400, 100), packingMode='h', border=(1, 2))
Or, if you're using your the inky
library:
board = auto()
topLayout = Layout(board.resolution, 'h', (0, 0))
Add as many new layers as you want:
sublayout1 = topLayout.addLayer()
sublayout2 = topLayout.addLayer()
sublayout3 = topLayout.sddLayer()
sublayout31 = sublayout3.addLayer()
sublayout32 = sublayout3.addLayer()
Use the sub-layouts to create images:
mode = "P"
bgColour = 0
image31 = Image.new(mode, sublayout31.size, bgColour)
draw = ImageDraw.Draw(image31)
draw.text(tuple(s/2 for s in sublayout31.size), "Hello",1)
sublayout31.setImage(image31)
image32 = Image.new(mode, sublayout32.size, bgColour)
draw = ImageDraw.Draw(image32)
draw.text(tuple(s/2 for s in sublayout32.size), "World!",1)
sublayout32.setImage(image32)
topLayout.draw()
topLayout.write("hello-world.png")
If you're using the Inky, you can load the image up:
inky_image = Image.open("hello-world.png")
board.set_image(inky_image)
And you're done!
Alternate packing mode is an implicit mechanism.
You can specify a different packingMode
in the addLayer
method:
layout.addLayer(packingMode='v')
By default Layouts added using addLayer
inherit their parent's packing mode.
Figure 1.1: Alternate Packing Mode example.
Three Layout layers are illustrated, each with different packing modes.
The top Layout uses packingMode='h'
, the left Layout also uses
packingMode='h'
, and the right Layout uses packingMode='v'
.
If you want to render the Layout in a rotated orientation, you can set the Rotation parameter. It takes the values:
- UP - normal, no rotation; (Default behaviour)
- LEFT - rotation by -90 or 270 degrees
- RIGHT - rotation by +90 degrees
- DOWN - rotated 180 degrees (up side down)
For all the following rotation examples, packingMode='v'
.
Figure 2.1 Rotation.UP (the default)
Figure 2.2: Rotation.LEFT
Figure 2.3: Rotation.DOWN
Figure 2.4: Rotation.RIGHT
When adding a layer, you can specify packing bias:
layout = new Layout()
sublayout1 = layout.addLayer(packingBias=3)
sublayout2 = layout.addLayer() # default packingBias=1
In this example, sublayout1
will take up 3/4s of the space (3/(1+3)),
while sublayout2
will be left with the remaining 1/4.
Figure 3.1: Two layers, the first uses the default packingBias
,
the second uses packingBias=3
.
Subclassing the Layout class allows you to create pre-decorated layouts, which
dynamically re-draw themselves.
Ensure that Layout.__init__()
is called from your
subclass's constructor method. Implement the default draw behaviour
by overriding the Layout.drawOverride(self)
method, too. Remember that this
method is called after resizing, so you should be able to resize the
information that you display at runtime.
layout = new Layout()
child = new DynamicLayout() # your subclass, overrides `Layout.draw()`
layout.addLayout(child) # returns child, resized. will be auto-redrawn.