It's easy to use the CursorControl module with CircuitPython.  This module allows you to easily write Python code which generates and controls a mouse cursor on your CircuitPython device's display.

CursorControl Module Installation

Next you'll need to install the Adafruit CircuitPython CursorControl library on your CircuitPython board.  

First make sure you are running the latest version of Adafruit CircuitPython for your board.

Next you'll need to install the necessary libraries to use the hardware--carefully follow the steps to find and install these libraries from Adafruit's CircuitPython library bundle.  Our introduction guide has a great page on how to install the library bundle for both express and non-express boards.

Remember for non-express boards like the, you'll need to manually install the necessary libraries from the bundle:

  • adafruit_cursorcontrol.mpy
  • cursorcontrol_cursormanager.mpy

You can also download the adafruit_cursorcontrol.mpy from its releases page on Github.

Before continuing make sure your board's lib folder or root filesystem has the adafruit_cursorcontrol.mpy and cursorcontrol_cursormanager.mpy files are copied over.

Next connect to the board's serial REPL so you are at the CircuitPython >>> prompt.

CircuitPython Usage

To demonstrate the usage of the CursorControl library, we'll use the example below. Save the file below to a code.py file. Then, upload the code.py file to the CIRCUITPY drive.

# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries
# SPDX-License-Identifier: MIT

import time
import board
import displayio
from adafruit_cursorcontrol.cursorcontrol import Cursor
from adafruit_cursorcontrol.cursorcontrol_cursormanager import CursorManager

# Create the display
display = board.DISPLAY

# Create the display context
splash = displayio.Group()

# initialize the mouse cursor object
mouse_cursor = Cursor(display, display_group=splash)

# initialize the cursormanager
cursor = CursorManager(mouse_cursor)

# show displayio group
display.root_group = splash

while True:
    cursor.update()
    if cursor.is_clicked:
        if mouse_cursor.hidden:
            mouse_cursor.show()
        else:
            mouse_cursor.hide()
    time.sleep(0.01)

After saving the code.py to your CIRCUITPY drive, you should see a cursor appear on your device's display.

Move the cursor around by pressing the PyBadge's D-Pad buttons or moving the PyGamer's Joystick!

CursorControl Module Overview

The CursorControl library contains two classes, Cursor and CursorManager. The Cursor class is responsible for generating a cursor bitmap, displaying it on the screen, and adjusting the cursor's properties.

The CursorManager is a high-level class responsible for initializing a hardware interface for the cursor. This includes methods to set up the joystick or buttons and control the cursor's properties using them.

Let's take a look at a short example to understand how these two classes interact.

First, the example creates a display object (using the board's builtin DISPLAY) and a displayio group. This step is required - the Cursor class does not handle generating a displayio group.

# Create the display
display = board.DISPLAY

# Create the display context
splash = displayio.Group(max_size=5)

The example next initializes the Cursor object by passing it the board's display interface and the display group.

If you want to set the cursor's properties during Cursor initialization (to hide the cursor when a game starts or increase the cursor's scale) - take a look at the __init__ keyword arguments for this class here.

# initialize the mouse cursor object
mouse_cursor = Cursor(display, display_group=splash)

While you now have a cursor object (and loading the code so far will make a cursor appear on your display)  - you need a way of controlling it. You could control it from the loop and create methods to read the joystick and buttons, but we've already gone ahead and built a class to do this.

If you're using a PyGamer or PyBadge, all you need to do to control a Cursor is pass the Cursor object to the CursorManager

# initialize the cursormanager
cursor = CursorManager(mouse_cursor)

Then, you'll set the display to "show" the displayio splash group

# show displayio group
display.show(splash)

The top of the loop calls cursor.update(). This method within CursorManager handles button presses, joystick movement and d-pad button clicks. It also sets the CursorManager's is_clicked property. 

while True:
    cursor.update()
    if cursor.is_clicked:
        if mouse_cursor.hide:
            mouse_cursor.hide = False
        else:
            mouse_cursor.hide = True
    time.sleep(0.01)

Next, we'll want to see if the cursor was clicked. The .is_clicked property is set by a call to cursor.update() and is a boolean. If it's true, you can hide the Cursor by setting its .hide property.

Buttons and Text Elements with Cursor

Below is an advanced example which uses the Display Text module as well as the Button module. Moving the cursors over the button and clicking set different Cursor attributes such as cursor speed and cursor scale size. 

# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries
# SPDX-License-Identifier: MIT

import time
import board
import displayio
from adafruit_button import Button
from adafruit_display_text import label
import terminalio
from adafruit_cursorcontrol.cursorcontrol import Cursor
from adafruit_cursorcontrol.cursorcontrol_cursormanager import CursorManager

# Create the display
display = board.DISPLAY

# Create the display context
splash = displayio.Group()

# Use the built-in system font
font = terminalio.FONT

##########################################################################
# Make a background color fill

color_bitmap = displayio.Bitmap(display.width, display.height, 1)
color_palette = displayio.Palette(1)
color_palette[0] = 0x404040
bg_sprite = displayio.TileGrid(color_bitmap, pixel_shader=color_palette, x=0, y=0)
splash.append(bg_sprite)

##########################################################################

# Set up button/label properties
BUTTON_WIDTH = 80
BUTTON_HEIGHT = 40
BUTTON_MARGIN = 20
LBL_HEADER = [100, 20]
LBL_TEXT = [120, 40]

# Resize buttons for small display (PyGamer)
if display.width < 240:
    BUTTON_WIDTH = int(BUTTON_WIDTH / 2)
    BUTTON_HEIGHT = int(BUTTON_HEIGHT / 2)
    BUTTON_MARGIN = int(BUTTON_MARGIN / 2)
    LBL_HEADER[0] -= 75
    LBL_HEADER[1] -= 10
    LBL_TEXT[0] -= 70
    LBL_TEXT[1] += 55

# Create the buttons
buttons = []

button_speed_inc = Button(
    x=BUTTON_MARGIN,
    y=BUTTON_MARGIN + BUTTON_HEIGHT,
    width=BUTTON_WIDTH,
    height=BUTTON_HEIGHT,
    label="Speed+",
    label_font=font,
)
buttons.append(button_speed_inc)

button_speed_dec = Button(
    x=BUTTON_MARGIN,
    y=BUTTON_MARGIN * 4 + BUTTON_HEIGHT,
    width=BUTTON_WIDTH,
    height=BUTTON_HEIGHT,
    label="Speed-",
    label_font=font,
)
buttons.append(button_speed_dec)

button_scale_pos = Button(
    x=BUTTON_MARGIN * 3 + 2 * BUTTON_WIDTH,
    y=BUTTON_MARGIN + BUTTON_HEIGHT,
    width=BUTTON_WIDTH,
    height=BUTTON_HEIGHT,
    label="Scale+",
    label_font=font,
    style=Button.SHADOWRECT,
)
buttons.append(button_scale_pos)

button_scale_neg = Button(
    x=BUTTON_MARGIN * 3 + 2 * BUTTON_WIDTH,
    y=BUTTON_MARGIN * 4 + BUTTON_HEIGHT,
    width=BUTTON_WIDTH,
    height=BUTTON_HEIGHT,
    label="Scale-",
    label_font=font,
    style=Button.SHADOWRECT,
)
buttons.append(button_scale_neg)

# Show the button
for b in buttons:
    splash.append(b.group)

# Create a text label
text_label = label.Label(
    font, text="CircuitPython Cursor!", color=0x00FF00, x=LBL_HEADER[0], y=LBL_HEADER[1]
)
splash.append(text_label)

text_speed = label.Label(font, color=0x00FF00, x=LBL_TEXT[0], y=LBL_TEXT[1])
splash.append(text_speed)

text_scale = label.Label(font, color=0x00FF00, x=LBL_TEXT[0], y=LBL_TEXT[1] + 20)
splash.append(text_scale)

# initialize the mouse cursor object
mouse_cursor = Cursor(display, display_group=splash)

# initialize the cursormanager
cursor = CursorManager(mouse_cursor)

# show displayio group
display.root_group = splash

prev_btn = None
while True:
    cursor.update()
    if cursor.is_clicked is True:
        for i, b in enumerate(buttons):
            if b.contains((mouse_cursor.x, mouse_cursor.y)):
                b.selected = True
                print("Button %d pressed" % i)
                if i == 0:  # Increase the cursor speed
                    mouse_cursor.speed += 1
                elif i == 1:  # Decrease the cursor speed
                    mouse_cursor.speed -= 1
                if i == 2:  # Increase the cursor scale
                    mouse_cursor.scale += 1
                elif i == 3:  # Decrease the cursor scale
                    mouse_cursor.scale -= 1
                prev_btn = b
    elif prev_btn is not None:
        prev_btn.selected = False
    text_speed.text = "Speed: {0}px".format(mouse_cursor.speed)
    text_scale.text = "Scale: {0}px".format(mouse_cursor.scale)
    time.sleep(0.1)

This guide was first published on Jul 01, 2019. It was last updated on Nov 28, 2023.

This page (Code Overview ) was last updated on Nov 28, 2023.

Text editor powered by tinymce.