5. API - pi-top Device

5.1. Pitop

This class represents a pi-top device. Each of the on-board features of pi-tops can be accessed from this object.

Note

This class has been built with pi-top [4] in mind, as is in early development. You may notice that some features do not behave as expected on other platforms.

If you would like to help us with development, please refer to the Contributing document in this repository for information!

Here is some sample code demonstrating how the various subsystems of a pi-top [4] can be accessed and used:

from time import sleep

from PIL import Image

from pitop import Pitop

# Set up pi-top
pitop = Pitop()

# Say hi!
pitop.miniscreen.display_text("Hello!")
sleep(2)

# Display battery info
battery_capacity = pitop.battery.capacity
battery_charging = pitop.battery.is_charging

pitop.miniscreen.display_multiline_text(
    "Battery Status:\n"
    f"-Capacity: {battery_capacity}%\n"
    f"-Charging: {battery_charging}",
    font_size=15,
)
sleep(2)


# Configure buttons to do something
keep_running = True


def display_gif_and_exit():
    image = Image.open(
        "/usr/lib/python3/dist-packages/pitop/miniscreen/images/rocket.gif"
    )
    pitop.miniscreen.play_animated_image(image)
    pitop.miniscreen.display_text("Bye!")
    sleep(2)
    global keep_running
    keep_running = False


pitop.miniscreen.select_button.when_pressed = display_gif_and_exit
pitop.miniscreen.cancel_button.when_pressed = display_gif_and_exit
pitop.miniscreen.up_button.when_pressed = display_gif_and_exit
pitop.miniscreen.down_button.when_pressed = display_gif_and_exit

pitop.miniscreen.display_multiline_text("Press any button...", font_size=25)

# Sleep until `display_gif_and_exit` runs
while keep_running:
    sleep(0.3)

Although it is possible to access pi-top subsystems individually, it is recommended to access them via this class.

5.1.1. Class Reference: Pitop

class pitop.Pitop[source]

Represents a pi-top Device.

When creating a Pitop object, multiple properties will be set, depending on the pi-top device that it’s running the code. For example, if run on a pi-top [4], a miniscreen attribute will be created as an interface to control the miniscreen OLED display, but that won’t be available for other pi-top devices.

The Pitop class is a Singleton. This means that only one instance per process will be created. In practice, this means that if in a particular project you instance a Pitop class in 2 different files, they will share the internal state.

property miniscreen
If using a pi-top [4], this property returns a pitop.miniscreen.Miniscreen object, to interact with the device’s Miniscreen.
property oled
Refer to miniscreen.
property battery
If using a pi-top with a battery, this property returns a pitop.battery.Battery object, to interact with the device’s battery.
own_state

Representation of an object state that will be used to determine the current state of an object.

All pi-tops come with some software-controllable onboard hardware. These sections of the API make it easy to access and change the state of your pi-top hardware.

5.1.2. Using the Pitop object

5.1.2.1. Attaching objects and saving configuration to a file

from time import sleep

from pitop import LED, Pitop
from pitop.robotics.drive_controller import DriveController

pitop = Pitop()
drive_controller = DriveController()
led = LED("D0", name="red_led")

# Add components to the Pitop object
pitop.add_component(drive_controller)
pitop.add_component(led)

# Do something with the object
pitop.red_led.on()
pitop.drive.forward(0.5)
sleep(2)
pitop.red_led.off()
pitop.drive.stop()

# Store configuration to a file
pitop.save_config("/home/pi/my_custom_config.json")

5.1.2.2. Loading an existing configuration

from time import sleep

from pitop import Pitop

# Load configuration from a previous session
pitop = Pitop.from_file("/home/pi/my_custom_config.json")

# Check the loaded configuration
print(pitop.config)

# Do something with your device
pitop.red_led.on()
pitop.drive.forward(0.5)
sleep(2)
pitop.red_led.off()
pitop.drive.stop()

# Check the state of all the components attached to the Pitop object
pitop.print_state()

5.2. pi-top Battery

This class provides a simple way to check the current onboard pi-top battery state, and handle some state change events.

This class will work with original pi-top, pi-top [3] and pi-top [4]. pi-topCEED has no onboard battery, and so will not work.

from pitop import Pitop

battery = Pitop().battery

print(f"Battery capacity: {battery.capacity}")
print(f"Battery time remaining: {battery.time_remaining}")
print(f"Battery is charging: {battery.is_charging}")
print(f"Battery is full: {battery.is_full}")
print(f"Battery wattage: {battery.wattage}")


def do_low_battery_thing():
    print("Battery is low!")


def do_critical_battery_thing():
    print("Battery is critically low!")


def do_full_battery_thing():
    print("Battery is full!")


def do_charging_battery_thing():
    print("Battery is charging!")


def do_discharging_battery_thing():
    print("Battery is discharging!")


# To invoke a function when the battery changes state, you can assign the function to various 'when_' data members
battery.when_low = do_low_battery_thing
battery.when_critical = do_critical_battery_thing
battery.when_full = do_full_battery_thing
battery.when_charging = do_charging_battery_thing
battery.when_discharging = do_discharging_battery_thing


# Another way to react to battery events is to poll
while True:
    if battery.is_full:
        do_full_battery_thing()

5.2.1. Class Reference: pi-top Battery

class pitop.battery.Battery[source]
capacity
classmethod get_full_state()[source]
is_charging
is_full
time_remaining
wattage

5.3. pi-top Display

This class provides a simple way to check the current onboard pi-top display state, and handle state change events.

This class will work with original pi-top, pi-topCEED and pi-top [3].

Note

Not compatible with pi-top [4].

pi-top [4] has no onboard display, and the official pi-top [4] FHD Display is not software-controllable.

from signal import pause
from time import sleep

from pitop import Pitop

pitop = Pitop()
display = pitop.display


# Get display information
print(f"Display brightness: {display.brightness}")
print(f"Display blanking timeout: {display.blanking_timeout}")
print(f"Display backlight is on: {display.backlight}")
print(f"Display lid is open: {display.lid_is_open}")

# Change the brightness levels incrementally
display.increment_brightness()
display.decrement_brightness()

# Set brightness explicitly
display.brightness = 7

# Set screen blank state
display.blank()
display.unblank()

# Set screen blanking timeout (s)
display.blanking_timeout = 60


# Define some functions to call on events
def do_brightness_changed_thing(new_brightness):
    print(new_brightness)
    print("Display brightness has changed!")


def do_screen_blanked_thing():
    print("Display is blanked!")


def do_screen_unblanked_thing():
    print("Display is unblanked!")


def do_lid_closed_thing():
    print("Display lid is closed!")


def do_lid_opened_thing():
    print("Display lid is open!")


# 'Wire up' functions to display events
display.when_brightness_changed = do_brightness_changed_thing
display.when_screen_blanked = do_screen_blanked_thing
display.when_screen_unblanked = do_screen_unblanked_thing
display.when_lid_closed = do_lid_closed_thing
display.when_lid_opened = do_lid_opened_thing


# Wait indefinitely for events to be handled in the background
pause()


# Or alternatively poll
print("Polling for if lid is open (Original pi-top/pi-top [3] only)")
while True:
    if display.lid_is_open:
        do_lid_opened_thing()
    sleep(0.1)

5.3.1. Class Reference: pi-top Display

class pitop.display.Display[source]
backlight
blank()[source]
blanking_timeout
brightness
decrement_brightness()[source]
increment_brightness()[source]
lid_is_open
unblank()[source]

5.4. pi-top [4] Miniscreen

_images/pi-top_4_Front.jpg

The miniscreen of the pi-top [4] can be found on the front, comprised of an 128x64 pixel OLED screen and 4 programmable buttons.

Check out Key Concepts: pi-top [4] Miniscreen for useful information about how this class works.

5.4.1. Using the Miniscreen’s OLED Display

_images/pi-top_4_Front_OLED.jpg

The OLED display is an array of pixels that can be either on or off. Unlike the pixels in a more advanced display, such as the monitor you are most likely reading this on, the display is a “1-bit monochromatic” display. Text and images can be displayed by directly manipulating the pixels.

The pitop.miniscreen.Miniscreen class directly provides display functions for the OLED.

5.4.1.1. Displaying text

from time import sleep

from pitop import Pitop

pitop = Pitop()
miniscreen = pitop.miniscreen
miniscreen.display_multiline_text("Hello, world!", font_size=20)
sleep(5)

5.4.1.2. Showing an image

from time import sleep

from pitop import Pitop

pitop = Pitop()
miniscreen = pitop.miniscreen

miniscreen.display_image_file(
    "/usr/lib/python3/dist-packages/pitop/miniscreen/images/rocket.gif"
)

sleep(2)

5.4.1.3. Loop a GIF

from PIL import Image, ImageSequence

from pitop import Pitop

pitop = Pitop()
miniscreen = pitop.miniscreen

rocket = Image.open("/usr/lib/python3/dist-packages/pitop/miniscreen/images/rocket.gif")

while True:
    for frame in ImageSequence.Iterator(rocket):
        miniscreen.display_image(frame)

5.4.1.4. Displaying an GIF once

from PIL import Image

from pitop import Pitop

pitop = Pitop()
miniscreen = pitop.miniscreen

rocket = Image.open("/usr/lib/python3/dist-packages/pitop/miniscreen/images/rocket.gif")

miniscreen.play_animated_image(rocket)

5.4.1.5. Displaying an GIF once through frame by frame

from PIL import Image, ImageSequence

from pitop import Pitop

pitop = Pitop()
miniscreen = pitop.miniscreen

rocket = Image.open("/usr/lib/python3/dist-packages/pitop/miniscreen/images/rocket.gif")

for frame in ImageSequence.Iterator(rocket):
    miniscreen.display_image(frame)

5.4.1.6. Displaying an GIF looping in background

from time import sleep

from PIL import Image

from pitop import Pitop

pitop = Pitop()
miniscreen = pitop.miniscreen

image = Image.open("/usr/lib/python3/dist-packages/pitop/miniscreen/images/rocket.gif")

# Run animation loop in background by setting `background` to True
miniscreen.play_animated_image(image, background=True, loop=True)


# Do stuff while showing image
print("Counting to 100 while showing animated image on miniscreen...")

for i in range(100):
    print("\r{}".format(i), end="", flush=True)
    sleep(0.2)

print("\rFinished!")

# Stop animation
miniscreen.stop_animated_image()

5.4.1.7. Handling basic 2D graphics drawing and displaying

from PIL import Image, ImageDraw, ImageFont

from pitop import Pitop

pitop = Pitop()
miniscreen = pitop.miniscreen
image = Image.new(
    miniscreen.mode,
    miniscreen.size,
)
canvas = ImageDraw.Draw(image)
miniscreen.set_max_fps(1)


def clear():
    canvas.rectangle(miniscreen.bounding_box, fill=0)


print("Drawing an arc")
canvas.arc(miniscreen.bounding_box, 0, 180, fill=1, width=1)
miniscreen.display_image(image)

clear()

print("Drawing an image")
# Note: this is an animated file, but this approach will only show the first frame
demo_image = Image.open(
    "/usr/lib/python3/dist-packages/pitop/miniscreen/images/rocket.gif"
).convert("1")
canvas.bitmap((0, 0), demo_image, fill=1)
miniscreen.display_image(image)

clear()

print("Drawing a chord")
canvas.chord(miniscreen.bounding_box, 0, 180, fill=1)
miniscreen.display_image(image)

clear()

print("Drawing an ellipse")
canvas.ellipse(miniscreen.bounding_box, fill=1)
miniscreen.display_image(image)

clear()

print("Drawing a line")
canvas.line(miniscreen.bounding_box, fill=1)
miniscreen.display_image(image)

clear()

print("Drawing a pieslice")
canvas.pieslice(miniscreen.bounding_box, 0, 180, fill=1)
miniscreen.display_image(image)

clear()

print("Drawing a point")
canvas.point(miniscreen.bounding_box, fill=1)
miniscreen.display_image(image)

clear()

print("Drawing a polygon")
canvas.polygon(miniscreen.bounding_box, fill=1)
miniscreen.display_image(image)

clear()

print("Drawing a rectangle")
canvas.rectangle(miniscreen.bounding_box, fill=1)
miniscreen.display_image(image)

clear()

print("Drawing some text")
canvas.text((0, 0), "Hello\nWorld!", font=ImageFont.load_default(), fill=1)
miniscreen.display_image(image)

5.4.1.8. Displaying a clock

from datetime import datetime

from PIL import Image, ImageDraw

from pitop import Pitop

pitop = Pitop()
miniscreen = pitop.miniscreen
miniscreen.set_max_fps(1)

image = Image.new(
    miniscreen.mode,
    miniscreen.size,
)
canvas = ImageDraw.Draw(image)

bounding_box = (32, 0, 95, 63)

big_hand_box = (
    bounding_box[0] + 5,
    bounding_box[1] + 5,
    bounding_box[2] - 5,
    bounding_box[3] - 5,
)

little_hand_box = (
    bounding_box[0] + 15,
    bounding_box[1] + 15,
    bounding_box[2] - 15,
    bounding_box[3] - 15,
)

while True:
    current_time = datetime.now()

    # Clear
    canvas.rectangle(bounding_box, fill=0)

    # Draw face
    canvas.ellipse(bounding_box, fill=1)

    # Draw hands
    angle_second = (current_time.second * 360 / 60) - 90
    canvas.pieslice(big_hand_box, angle_second, angle_second + 2, fill=0)

    angle_minute = (current_time.minute * 360 / 60) - 90
    canvas.pieslice(big_hand_box, angle_minute, angle_minute + 5, fill=0)

    angle_hour = (
        (current_time.hour * 360 / 12) + (current_time.minute * 360 / 12 / 60)
    ) - 90
    canvas.pieslice(little_hand_box, angle_hour, angle_hour + 5, fill=0)

    # Display to screen
    miniscreen.display_image(image)

5.4.1.9. Display a particle-based screensaver

from random import randint

from PIL import Image, ImageDraw

from pitop import Pitop

pitop = Pitop()
miniscreen = pitop.miniscreen
image = Image.new(
    miniscreen.mode,
    miniscreen.size,
)
canvas = ImageDraw.Draw(image)

speed_factor = 15
particles = []


class Particle:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.update()

    def get_position(self):
        return (self.x, self.y)

    def update(self):
        dx = (
            (self.x - (miniscreen.width / 2)) / speed_factor
            if self.x < (miniscreen.width / 2)
            else (self.x - (miniscreen.width / 2)) / speed_factor
        )
        dy = (
            (self.y - (miniscreen.height / 2)) / speed_factor
            if self.y < (miniscreen.height / 2)
            else (self.y - (miniscreen.height / 2)) / speed_factor
        )
        self.x += dx
        self.y += dy


def add_new_particle():
    x = randint(0, miniscreen.width)
    y = randint(0, miniscreen.height)
    particles.append(Particle(x, y))


while True:
    # Clear display
    canvas.rectangle(miniscreen.bounding_box, fill=0)
    particles.clear()

    speed_factor = randint(5, 30)
    particle_count = randint(5, 50)

    for count in range(particle_count):
        add_new_particle()

    for _ in range(100):
        for particle in particles:
            x, y = particle.get_position()

            if (x < 0 or x > miniscreen.width) or (y < 0 or y > miniscreen.height):
                particles.remove(particle)
                add_new_particle()
            else:
                canvas.point((x, y), fill=1)
                particle.update()

        miniscreen.display_image(image)

5.4.1.10. Prim’s algorithm

from random import randint, random
from time import sleep

from PIL import Image, ImageDraw

from pitop import Pitop

# https://en.wikipedia.org/wiki/Maze_generation_algorithm

pitop = Pitop()
miniscreen = pitop.miniscreen
image = Image.new(
    miniscreen.mode,
    miniscreen.size,
)
canvas = ImageDraw.Draw(image)
miniscreen.set_max_fps(50)


def draw_pixel(pos):
    canvas.point(pos, fill=1)
    miniscreen.display_image(image)
    drawn_pixels.append(pos)


width = (miniscreen.width // 2) * 2 - 1
height = (miniscreen.height // 2) * 2 - 1

while True:
    print("Initialising...")
    canvas.rectangle(miniscreen.bounding_box, fill=0)

    drawn_pixels = list()
    complexity = int(random() * (5 * (width + height)))
    density = int(random() * ((width // 2) * (height // 2)))

    print("Drawing the borders...")

    for x in range(width):
        draw_pixel((x, 0))
        draw_pixel((x, (height // 2) * 2))

    for y in range(height):
        draw_pixel((0, y))
        draw_pixel(((width // 2) * 2, y))

    print("Filling the maze...")

    for i in range(density):
        x, y = randint(0, width // 2) * 2, randint(0, height // 2) * 2
        if (x, y) not in drawn_pixels:
            draw_pixel((x, y))

            for j in range(complexity):
                neighbours = []
                if x > 1:
                    neighbours.append((x - 2, y))
                if x < width - 3:
                    neighbours.append((x + 2, y))
                if y > 1:
                    neighbours.append((x, y - 2))
                if y < height - 3:
                    neighbours.append((x, y + 2))
                if len(neighbours):
                    x_, y_ = neighbours[randint(0, len(neighbours) - 1)]
                    if (x_, y_) not in drawn_pixels:
                        draw_pixel((x_, y_))
                        draw_pixel((x_ + (x - x_) // 2, y_ + (y - y_) // 2))
                        x, y = x_, y_

    print("Done!")

    sleep(10)

5.4.1.11. 2-Player Pong Game

from random import randrange
from time import sleep

from PIL import Image, ImageDraw, ImageFont

from pitop import Pitop

# Game variables
BALL_RADIUS = 2
PADDLE_SIZE = (2, 20)
PADDLE_CTRL_VEL = 4


class Ball:
    def __init__(self):
        self.pos = [0, 0]
        self.vel = [0, 0]

        # 50/50 chance of direction
        self.init(move_right=randrange(0, 2) == 0)

    def init(self, move_right):
        self.pos = [miniscreen.width // 2, miniscreen.height // 2]

        horz = randrange(1, 3)
        vert = randrange(1, 3)

        if move_right is False:
            horz = -horz

        self.vel = [horz, -vert]

    @property
    def x_pos(self):
        return self.pos[0]

    @property
    def y_pos(self):
        return self.pos[1]

    def is_aligned_with_paddle_horizontally(self, paddle):
        return abs(self.x_pos - paddle.x_pos) <= BALL_RADIUS + PADDLE_SIZE[0] // 2

    def is_aligned_with_paddle_vertically(self, paddle):
        return abs(self.y_pos - paddle.y_pos) <= BALL_RADIUS + PADDLE_SIZE[1] // 2

    def is_touching_paddle(self, paddle):
        hor = self.is_aligned_with_paddle_horizontally(paddle)
        ver = self.is_aligned_with_paddle_vertically(paddle)
        return hor and ver

    @property
    def is_touching_vertical_walls(self):
        return (
            self.y_pos <= BALL_RADIUS
            or self.y_pos >= miniscreen.height + 1 - BALL_RADIUS
        )

    def change_direction(self, change_x=False, change_y=False, speed_factor=1.0):
        x_vel = -self.vel[0] if change_x else self.vel[0]
        self.vel[0] = speed_factor * x_vel

        y_vel = -self.vel[1] if change_y else self.vel[1]
        self.vel[1] = speed_factor * y_vel

    def update(self):
        self.pos = [x + y for x, y in zip(self.pos, self.vel)]

        if self.is_touching_vertical_walls:
            self.change_direction(change_y=True, speed_factor=1.0)

    @property
    def bounding_box(self):
        def get_circle_bounds(center, radius):
            x0 = center[0] - radius
            y0 = center[1] - radius
            x1 = center[0] + radius
            y1 = center[1] + radius
            return (x0, y0, x1, y1)

        return get_circle_bounds(self.pos, BALL_RADIUS)


class Paddle:
    def __init__(self, start_pos=[0, 0]):
        self.pos = start_pos
        self.vel = 0
        self.score = 0

    def increase_score(self):
        self.score += 1

    @property
    def x_pos(self):
        return self.pos[0]

    @property
    def y_pos(self):
        return self.pos[1]

    @y_pos.setter
    def y_pos(self, new_y):
        self.pos[1] = new_y

    @property
    def touching_top(self):
        return self.y_pos - PADDLE_SIZE[1] // 2 <= 0

    @property
    def touching_bottom(self):
        return self.y_pos + PADDLE_SIZE[1] // 2 >= miniscreen.height - 1

    def update(self):
        moving_down = self.vel > 0

        if self.touching_top and not moving_down:
            return

        if self.touching_bottom and moving_down:
            return

        self.y_pos += self.vel

        if self.touching_top:
            self.y_pos = PADDLE_SIZE[1] // 2

        if self.touching_bottom:
            self.y_pos = miniscreen.height - PADDLE_SIZE[1] // 2 - 1

    @property
    def bounding_box(self):
        return (
            self.x_pos,
            self.y_pos - PADDLE_SIZE[1] // 2,
            self.x_pos,
            self.y_pos + PADDLE_SIZE[1] // 2,
        )


def update_button_state():
    down_pressed = miniscreen.down_button.is_pressed
    up_pressed = miniscreen.up_button.is_pressed
    select_pressed = miniscreen.select_button.is_pressed
    cancel_pressed = miniscreen.cancel_button.is_pressed

    if down_pressed == up_pressed:
        l_paddle.vel = 0
    elif down_pressed:
        l_paddle.vel = PADDLE_CTRL_VEL
    elif up_pressed:
        l_paddle.vel = -PADDLE_CTRL_VEL

    if select_pressed == cancel_pressed:
        r_paddle.vel = 0
    elif select_pressed:
        r_paddle.vel = PADDLE_CTRL_VEL
    elif cancel_pressed:
        r_paddle.vel = -PADDLE_CTRL_VEL


def update_positions():
    round_finished = False

    l_paddle.update()
    r_paddle.update()
    ball.update()

    paddles = {l_paddle, r_paddle}
    for paddle in paddles:
        if ball.is_aligned_with_paddle_horizontally(paddle):
            if ball.is_touching_paddle(paddle):
                ball.change_direction(change_x=True, speed_factor=1.1)
            else:
                other_paddle = paddles - {paddle}
                other_paddle = other_paddle.pop()
                other_paddle.increase_score()

                ball.init(move_right=other_paddle == r_paddle)
                paddle.y_pos = miniscreen.height // 2
                other_paddle.y_pos = miniscreen.height // 2

                round_finished = True

            break

    return round_finished


def draw(wait=False):
    canvas = ImageDraw.Draw(image)

    # Clear screen
    canvas.rectangle(miniscreen.bounding_box, fill=0)

    # Draw ball
    canvas.ellipse(ball.bounding_box, fill=1)

    # Draw paddles
    canvas.line(l_paddle.bounding_box, fill=1, width=PADDLE_SIZE[0])

    canvas.line(r_paddle.bounding_box, fill=1, width=PADDLE_SIZE[0])

    # Draw score
    font = ImageFont.truetype("VeraMono.ttf", size=12)
    canvas.multiline_text(
        (1 * miniscreen.width // 3, 2),
        str(l_paddle.score),
        fill=1,
        font=font,
        align="center",
    )
    canvas.multiline_text(
        (2 * miniscreen.width // 3, 2),
        str(r_paddle.score),
        fill=1,
        font=font,
        align="center",
    )

    # Display image
    miniscreen.display_image(image)

    if wait:
        sleep(1.5)


# Internal variables
pitop = Pitop()
miniscreen = pitop.miniscreen
miniscreen.set_max_fps(30)

ball = Ball()

l_paddle = Paddle([PADDLE_SIZE[0] // 2 - 1, miniscreen.height // 2])
r_paddle = Paddle([miniscreen.width - 1 - PADDLE_SIZE[0] // 2, miniscreen.height // 2])

image = Image.new(
    miniscreen.mode,
    miniscreen.size,
)


def main():
    while True:
        update_button_state()
        draw(update_positions())


if __name__ == "__main__":
    main()

5.4.2. Class Reference: pi-top [4] Miniscreen

class pitop.miniscreen.Miniscreen[source]

Represents a pi-top [4]’s miniscreen display.

Also owns the surrounding 4 buttons as properties (up_button, down_button, select_button, cancel_button). See pitop.miniscreen.miniscreen.MiniscreenButton for how to use these buttons.

bottom_left

Gets the bottom-left corner of the miniscreen display.

Returns:The coordinates of the bottom left of the display’s bounding box as an (x,y) tuple.
Return type:tuple
bottom_right

Gets the bottom-right corner of the miniscreen display.

Returns:The coordinates of the bottom right of the display’s bounding box as an (x,y) tuple.
Return type:tuple
bounding_box

Gets the bounding box of the miniscreen display.

Returns:The device’s bounding box as an (top-left x, top-left y, bottom-right x, bottom-right y) tuple.
Return type:tuple
cancel_button

Gets the cancel button of the pi-top [4] miniscreen.

Returns:A gpiozero-like button instance representing the cancel button of the pi-top [4] miniscreen.
Return type:pitop.miniscreen.miniscreen.MiniscreenButton
center

Gets the center of the miniscreen display.

Returns:The coordinates of the center of the display’s bounding box as an (x,y) tuple.
Return type:tuple
clear()

Clears any content displayed in the miniscreen display.

contrast(new_contrast_value)

Sets the contrast value of the miniscreen display to the provided value.

Parameters:new_contrast_value (int) – contrast value to set, between 0 and 255.
device

Gets the miniscreen display device instance.

Return type:pitop.miniscreen.oled.core.contrib.luma.oled.device.sh1106
display(force=False)

Displays what is on the current canvas to the screen as a single frame.

Warning

This method is deprecated and will be deleted on the next major release of the SDK.

This method does not need to be called when using the other draw functions in this class, but is used when the caller wants to use the canvas object to draw composite objects and then render them to screen in a single frame.

display_image(image, xy=None, invert=False)

Render a static image to the screen from a file or URL at a given position.

The image should be provided as a PIL Image object.

Parameters:
  • image (Image) – A PIL Image object to be rendered
  • xy (tuple) – The position on the screen to render the image. If not provided or passed as None the image will be drawn in the top-left of the screen.
  • invert (bool) – Set to True to flip the on/off state of each pixel in the image
display_image_file(file_path_or_url, xy=None, invert=False)

Render a static image to the screen from a file or URL at a given position.

The display’s positional properties (e.g. top_left, top_right) can be used to assist with specifying the xy position parameter.

Parameters:
  • file_path_or_url (str) – A file path or URL to the image
  • xy (tuple) – The position on the screen to render the image. If not provided or passed as None the image will be drawn in the top-left of the screen.
  • invert (bool) – Set to True to flip the on/off state of each pixel in the image
display_multiline_text(text, xy=None, font_size=None, font=None, invert=False, anchor=None, align=None)

Renders multi-lined text to the screen at a given position and size. Text that is too long for the screen will automatically wrap to the next line.

The display’s positional properties (e.g. top_left, top_right) can be used to assist with specifying the xy position parameter.

Parameters:
  • text (string) – The text to render
  • xy (tuple) – The position on the screen to render the image. If not provided or passed as None the image will be drawn in the top-left of the screen.
  • font_size (int) – The font size in pixels. If not provided or passed as None, the default font size will be used
  • font (string) – A filename or path of a TrueType or OpenType font. If not provided or passed as None, the default font will be used
  • invert (bool) – Set to True to flip the on/off state of each pixel in the image
  • align (str) – PIL ImageDraw alignment to use
  • anchor (str) – PIL ImageDraw text anchor to use
display_text(text, xy=None, font_size=None, font=None, invert=False, align=None, anchor=None)

Renders a single line of text to the screen at a given position and size.

The display’s positional properties (e.g. top_left, top_right) can be used to assist with specifying the xy position parameter.

Parameters:
  • text (string) – The text to render
  • xy (tuple) – The position on the screen to render the image. If not provided or passed as None the image will be drawn in the top-left of the screen.
  • font_size (int) – The font size in pixels. If not provided or passed as None, the default font size will be used
  • font (string) – A filename or path of a TrueType or OpenType font. If not provided or passed as None, the default font will be used
  • invert (bool) – Set to True to flip the on/off state of each pixel in the image
  • align (str) – PIL ImageDraw alignment to use
  • anchor (str) – PIL ImageDraw text anchor to use
down_button

Gets the down button of the pi-top [4] miniscreen.

Returns:A gpiozero-like button instance representing the down button of the pi-top [4] miniscreen.
Return type:pitop.miniscreen.miniscreen.MiniscreenButton
draw()

warning:: This method is deprecated in favor of display_image() and display_text(), and will be deleted on the next major release of the SDK.

draw_image(image, xy=None)

warning:: This method is deprecated in favor of display_image(), and will be deleted on the next major release of the SDK.

draw_image_file(file_path_or_url, xy=None)

warning:: This method is deprecated in favor of display_image_file(), and will be deleted on the next major release of the SDK.

draw_multiline_text(text, xy=None, font_size=None)

warning:: This method is deprecated in favor of display_multiline_text(), and will be deleted on the next major release of the SDK.

draw_text(text, xy=None, font_size=None)

warning:: This method is deprecated in favor of display_text(), and will be deleted on the next major release of the SDK.

height

Gets the height of the miniscreen display.

Return type:int
hide()

The miniscreen display is put into low power mode.

The previously shown image will re-appear when show() is given, even if the internal frame buffer has been changed (so long as display() has not been called).

is_active

Determine if the current miniscreen instance is in control of the miniscreen hardware.

Returns:whether the miniscreen instance is in control of the miniscreen hardware.
Return type:bool
mode
play_animated_image(image, background=False, loop=False)

Render an animation or a image to the screen.

Use stop_animated_image() to end a background animation

Parameters:
  • image (Image) – A PIL Image object to be rendered
  • background (bool) – Set whether the image should be in a background thread or in the main thread.
  • loop (bool) – Set whether the image animation should start again when it has finished
play_animated_image_file(file_path_or_url, background=False, loop=False)

Render an animated image to the screen from a file or URL.

Parameters:
  • file_path_or_url (str) – A file path or URL to the image
  • background (bool) – Set whether the image should be in a background thread or in the main thread.
  • loop (bool) – Set whether the image animation should start again when it has finished
prepare_image(image_to_prepare)

Formats the given image into one that can be used directly by the OLED.

Parameters:image_to_prepare (PIL.Image.Image) – Image to be formatted.
Return type:PIL.Image.Image
refresh()
reset(force=True)

Gives the caller access to the miniscreen display (i.e. in the case the system is currently rendering information to the screen) and clears the screen.

select_button

Gets the select button of the pi-top [4] miniscreen.

Returns:A gpiozero-like button instance representing the select button of the pi-top [4] miniscreen.
Return type:pitop.miniscreen.miniscreen.MiniscreenButton
set_control_to_hub()

Signals the pi-top hub to take control of the miniscreen display.

set_control_to_pi()

Signals the pi-top hub to give control of the miniscreen display to the Raspberry Pi.

set_max_fps(max_fps)

Set the maximum frames per second that the miniscreen display can display. This method can be useful to control or limit the speed of animations.

This works by blocking on the OLED’s display methods if called before the amount of time that a frame should last is not exceeded.

Parameters:max_fps (int) – The maximum frames that can be rendered per second
should_redisplay(image_to_display=None)

Determines if the miniscreen display needs to be refreshed, based on the provided image. If no image is provided, the content of the display’s deprecated internal canvas property will be used.

Parameters:image_to_display (PIL.Image.Image or None) – Image to be displayed.
Return type:bool
show()

The miniscreen display comes out of low power mode showing the previous image shown before hide() was called (so long as display() has not been called)

size

Gets the size of the miniscreen display as a (width, height) tuple.

Return type:tuple
sleep()

The miniscreen display in set to low contrast mode, without modifying the content of the screen.

spi_bus

Gets the SPI bus used by the miniscreen display to receive data as an integer. Setting this property will modify the SPI bus that the OLED uses. You might notice a flicker in the screen.

Parameters:bus (int) – Number of the SPI bus for the OLED to use. Accepted values are 0 or 1.
stop_animated_image()

Stop background animation started using start(), if currently running.

top_left

Gets the top left corner of the miniscreen display.

Returns:The coordinates of the center of the display’s bounding box as an (x,y) tuple.
Return type:tuple
top_right

Gets the top-right corner of the miniscreen display.

Returns:The coordinates of the top right of the display’s bounding box as an (x,y) tuple.
Return type:tuple
up_button

Gets the up button of the pi-top [4] miniscreen.

Returns:A gpiozero-like button instance representing the up button of the pi-top [4] miniscreen.
Return type:pitop.miniscreen.miniscreen.MiniscreenButton
visible

Gets whether the device is currently in low power state.

Returns:whether the screen is in low power mode
Return type:bool
wake()

The miniscreen display is set to high contrast mode, without modifying the content of the screen.

when_system_controlled

Function to call when user gives back control of the miniscreen to the system.

This is used by pt-miniscreen to update its ‘user-controlled’ application state.

when_user_controlled

Function to call when user takes control of the miniscreen.

This is used by pt-miniscreen to update its ‘user-controlled’ application state.

width

Gets the width of the miniscreen display.

Return type:int

5.4.3. Using the Miniscreen’s Buttons

_images/pi-top_4_Front_BUTTONS.jpg

The miniscreen’s buttons are simple, and behave in a similar way to the other button-style components in this SDK. Each miniscreen button can be queried for their “is pressed” state, and also invoke callback functions for when pressed and released.

The pitop.miniscreen.Miniscreen class provides these buttons as properties:

>>> from pitop import Pitop
>>> pitop = Pitop()
>>> miniscreen = pitop.miniscreen
>>> miniscreen.up_button
<pitop.miniscreen.miniscreen.MiniscreenButton object at 0xb3e44e50>
>>> miniscreen.down_button
<pitop.miniscreen.miniscreen.MiniscreenButton object at 0xb3e44d30>
>>> miniscreen.select_button
<pitop.miniscreen.miniscreen.MiniscreenButton object at 0xb3e44e90>
>>> miniscreen.cancel_button
<pitop.miniscreen.miniscreen.MiniscreenButton object at 0xb3e44e70>

Here is an example demonstrating 2 ways to make use of these buttons:

from time import sleep

from pitop import Pitop

pitop = Pitop()
miniscreen = pitop.miniscreen
up = miniscreen.up_button
down = miniscreen.down_button


def do_up_thing():
    print("Up button was pressed")


def do_down_thing():
    print("Down button was pressed")


def do_another_thing():
    print("do_another_thing invoked")


def select_something():
    print("select_something called")


# To invoke a function when the button is pressed/released,
# you can assign the function to the 'when_pressed' or 'when_released' data member of a button
print("Configuring miniscreen's up and down button events...")
up.when_pressed = do_up_thing
down.when_pressed = do_down_thing
down.when_released = do_another_thing


# Another way to react to button events is to poll the is_pressed data member
print("Polling for if select button is pressed...")
while True:
    if miniscreen.select_button.is_pressed:
        select_something()
        sleep(0.1)

5.4.4. Class Reference: pi-top [4] Miniscreen Button

class pitop.miniscreen.miniscreen.MiniscreenButton[source]

Represents one of the 4 buttons around the miniscreen display on a pi- top [4].

Should not be created directly - instead, use pitop.miniscreen.Miniscreen.

is_pressed

Get or set the button state as a boolean value.

Return type:bool
when_pressed

Get or set the ‘when pressed’ button state callback function. When set, this callback function will be invoked when this event happens.

Parameters:callback (Function) – Callback function to run when a button is pressed.
when_released

Get or set the ‘when released’ button state callback function. When set, this callback function will be invoked when this event happens.

Parameters:callback (Function) – Callback function to run when a button is released.