I know I must be mad but I’m trying to use Circuitpython graphics. (Why on earth did Adafruit make their graphics system so complicated? Picographics are much easier to understand, use and produce excellent results.)
I’m currently using a CLUE:
Adafruit CircuitPython 9.2.4 on 2025-01-28; Adafruit CLUE nRF52840 Express with nRF52840
Here is my code:
# Circuitpython vectorio test
# Tony Goodhew 2 Feb 2025
# I'm using a CLUE with 240x 240 display
import time
import board
import displayio
import vectorio
display = board.DISPLAY # get display object (built-in on some boards)
maingroup = displayio.Group()
display.root_group = maingroup
palette = displayio.Palette(1)
palette[0] = 0xff0000
circle = vectorio.Circle(pixel_shader=palette, radius=25, x=100, y=200)
maingroup.append(circle)
rectangle = vectorio.Rectangle(pixel_shader=palette, width=40, height=45, x=155, y=45)
maingroup.append(rectangle)
points=[(5, 5), (100, 20), (20, 100)]
triangle = vectorio.Polygon(pixel_shader=palette, points=points, x=0, y=0)
maingroup.append(triangle)
while True:
pass
How can I get the three shapes to have different colours? I would like RED,GREEN and BLUE.
I feel your pain. A while back I bought a Matrix Portal to use with some LED Matrix Panels. I found it so exceedingly frustrating trying to use Adafruits graphics offering that I eventually just threw in the towel and bought an Interstate 75.
My current project requires a high pixel count round display and I need to be able to draw a black circle over a series of 60 narrow radial triangles to leave a ring of thick ‘seconds ticks’ round the edge of the screen. Unfortunately, Pimoroni do not supply a round RGB666 display and driver board. The only one I can find is the Adafruit one. Their documentation of the over complicated displayio and vectorio graphics system leaves too much unexplained and there are no complete and working code examples to use as a starting point. I am not interested in “1980’s games” running small sprites over complicated backgrounds when I push buttons. I would rather build interesting dials, meters and graphs which react to inputs from sensors and data from the internet.
I have the whole project working perfectly on a wonderful Presto - but it is square and the corners stick out too much.
How did you configure your SPI-bus? This makes a huge difference.
And btw, CircuitPython has a huge base of learning-guides with ready to use examples for displayio, including many real life examples. In addition, every part of displayio (drivers, higher level widgets and so on) has an example-subdirectory in the repository with a number of examples, from absolute basic to more complex.
Also keep in mind: PicoGraphics is implemented in C for a extremely limited set of displays all from Pimoroni. So they can optimize everything with the caveat that you cannot use this software for 99.99% of the screens on the market.
In addition, the graphics system of CircuitPython is fully portable. You can take your code and just run it on a different processor with a potentially different screen.
Like always in IT, there are competing goals. Best performance is generally not possible with generic, portable solutions.
Regarding the speed: the NRF of the Clue is a 64MHz processor. The Pico you are used to has two 133 MHz processors. The new Pico2 even has up to 150 MHz. And since this are different CPU generations, the speed differences are even higher. So yes, your Clue will always be slow compared to the Pico.
So you should think about some changes in your code: first thing I would do is turn auto-refresh off. Depending on what you do, an explicit refresh at the right time saves a lot of processing. I would also replace the tight loop at the end of your program. Instead of just looping you could add a tiny amount of sleep to give background tasks a chance to do their processing.
I would also get rid of the black background, at least for this example. And I would stick with vectorio when using filled shapes. vectorio is implemented in C, while display_shapes is implemented in Python. Your very first example should be fine, but you should use a different palette for every shape if you want different colors. Or use a palette with all your colors and pass the appropriate color-index to the shapes.
I am using the latest version of the CLUE uf2 and bootloader - so I hope the SPI has been set up in the most efficient way.
I realise that a Pico has a faster processor but this CLUE is the most recent Adafruit board/display combo I have at the moment and so will be slower than a Pico board.
This uses vectorio, implemented in C, so is faster than display_shapes.
# Circuitpython vectorio test
# Tony Goodhew 3rd Feb 2025
# I'm using a CLUE with 240x240 display
import time
import board
import displayio
import vectorio
display = board.DISPLAY # get display object (built-in on some boards)
maingroup = displayio.Group()
display.root_group = maingroup
pal = displayio.Palette(4)
pal[0] = 0xff0000 # RED
pal[1] = 0x00ff00 # GREEN
pal[2] = 0x0000ff # BLUE
pal[3] = 0x000000 # BLACK
display.auto_refresh=False
points=[(5, 5), (0, 200), (200, 0)]
triangle = vectorio.Polygon(pixel_shader=pal, points=points, x=0, y=0)
maingroup.append(triangle)
circle = vectorio.Circle(pixel_shader=pal, radius=50, x=100, y=90)
maingroup.append(circle)
rectangle = vectorio.Rectangle(pixel_shader=pal, width=60, height=45, x=155, y=15)
maingroup.append(rectangle)
display.auto_refresh=True
while True:
pass
I’ve put some colours in the palette but cannot see how to use the palette to turn the circle BLACK and the rectangle BLUE.
SPI is hardwired at 60_000_000, so this should be fine for the Clue. You should use display.refresh() and keep auto_refresh = False. Doesn’t matter for this example, but eventually you will start reading the sensors and updating some text on the screen. A final refresh after all the updates is much cleaner visually IMHO.