Can you access FrameBuffer in Pimoroni PicoGraphics Micropython W

@Tonygo2 - that is excellent! It is the type of thing I am looking for. On my older projects I first loaded in different types of meter scales (from graphics files) over which I draw the pointer, but this is a really good jump forward for me on the pico to restart experimenting.

To remove my endless confusion and misunderstandings, would it be possible to share the complete code for me to load in and run. Also, what version of micropython.uf2 do I need to load in first?

Again, many thanks

I used the minimal UF2 from MicroPython.org for the Pi Pico original. Here is a link to a video - so you can see how fast it goes.

I will send you a message with my email. Reply and I will send you the code. (It is pretty long!)
Have fun

Here is a pure Pimoroni Graphics version with a little help with the syntax. This is running on the Pico Explorer board (240x240 pixels) and includes input from a 10 K Ohm potentiometer on ADC(0).

Speed does not appear to be a problem and it is flicker free.

# Analogue Dial on a Pimoroni Pico Explorer with Pico Graphics
# 10 K Ohm potentiometer on ADC(0) = GP26
from picographics import PicoGraphics, DISPLAY_LCD_240X240
import math
import time
import random
import machine # for ADC pot

display = PicoGraphics(display=DISPLAY_LCD_240X240)

red = display.create_pen(255,0,0)
white = display.create_pen(255,255,255)
green = display.create_pen(0,255,0)
blue = display.create_pen(0,0,255)
black = display.create_pen(0,0,0)
yellow = display.create_pen(255,255,0)

potentiometer = machine.ADC(26) # Install potentiometer


# Basics demo - Instruction crib sheet for Copy and Paste
# If you are new to Pimoroni Graphics instructions
display.set_pen(red)
w,h = display.get_bounds()
display.rectangle(0,0,w,h)
display.update()
display.set_font("bitmap8") 
display.set_pen(white)
#display.text(text, x, y, wordwrap, scale, angle, spacing)
display.text("Tony",20,20,219,4)

display.set_pen(blue)
display.text("Hello World", 70, 60, scale=2)
display.line(0,70,239,239)
display.circle(50, 190, 50)

display.set_pen(green)
display.rectangle(20,190,30,40)
display.triangle(140, 130,150,200,200,140)
display.set_pen(black)
display.pixel(30,200)

display.update()
time.sleep(2)

# =========== Main Program ================
display.clear()
xc = 120
yc = 120
display.set_pen(blue)
display.circle(xc,yc,100)
r = 103                                      # Tick outer radius
for p in range(0,101,10):                    # White Ticks at 10 % intervals
    theta = p * 1.8                          # Angle above horizontal in degrees
    theta_rad = math.radians(theta)          # Angle in radians
    yn = -int(r * math.sin(theta_rad))       # Calculate outer tick coordinates
    xn = -int(r * math.cos(theta_rad))
    display.set_pen(white)
    display.line(120,120,120+xn,120+yn) # Draw the tick from centre
display.set_pen(black)
display.circle(xc,yc,75)           # Overwrite inner tick lines
display.rectangle(0,yc+1,240,120)
display.update()

# Counting up in fives
r = 74 # Length of hand
old_xn = 0
old_yn = 0
for p in range(0,101,5): # Percentages at 5 % interval
    theta = p * 1.8
    display.set_pen(black)
    display.line(120,120,120+old_xn,120+old_yn) # Overwrite the old hand
    display.rectangle(0,121,240,120)            # Clear text area
    display.set_pen(green)
    display.text("Tony Goodhew",20,210,scale=3)
    theta_rad = math.radians(theta)
    theta_rad = math.radians(theta)
    display.set_pen(white)
    display.text(str(p) +" %",100,130,scale=3)  # Percentage value as text
    yn = -int(r * math.sin(theta_rad))
    xn = -int(r * math.cos(theta_rad))
    display.set_pen(red)
    display.line(120,120,120+xn,120+yn)         # Draw the new hand
    display.update()                            # Update screen
    time.sleep(0.1)                             # Delay
    old_xn = xn                                 # Store current hand end corordinates
    old_yn = yn                                 #  for overwriting in next loop pass
    
display.update()

# Random values
for c in range(25):
    p = random.randint(0,100)
    theta = p * 1.8
    display.set_pen(black)
    display.line(120,120,120+old_xn,120+old_yn) # Overwrite the old hand
    display.rectangle(0,121,240,120)            # Clear text area
    display.set_pen(red)
    display.text("Tony Goodhew",20,210,scale=3)
    theta_rad = math.radians(theta)
    display.set_pen(white)
    display.text(str(p) +" %",100,130,scale=3)  # Percentage value as text
    yn = -int(r * math.sin(theta_rad))
    xn = -int(r * math.cos(theta_rad))
    display.set_pen(red)
    display.line(120,120,120+xn,120+yn)         # Draw the new hand
    display.update()                            # Update screen
    time.sleep(0.1)                             # Delay
    old_xn = xn                                 # Store current hand end corordinates
    old_yn = yn                                 #  for overwriting in next loop pass
    
display.update()

# Potentiometer Control on ADC(0) 
for c in range(100):
    pot = potentiometer.read_u16()/256
    pot = int(pot * 256.0 /255.0) - 1
    pot = int(pot *100 / 255)
    if pot > 100:
        pot = 100
    p = pot
    theta = p * 1.8
    display.set_pen(black)
    display.line(120,120,120+old_xn,120+old_yn) # Overwrite the old hand
    display.rectangle(0,121,240,80)            # Clear text area
    display.set_pen(yellow)
    display.text("Tony Goodhew",20,210,scale=3)
    theta_rad = math.radians(theta)
    theta_rad = math.radians(theta)
    display.set_pen(white)
    display.text(str(p) +" %",100,130,scale=3)  # Percentage value as text
    yn = -int(r * math.sin(theta_rad))
    xn = -int(r * math.cos(theta_rad))
    display.set_pen(red)
    display.line(120,120,120+xn,120+yn)         # Draw the new hand
    display.update()                            # Update screen
    time.sleep(0.1)                             # Delay
    old_xn = xn                                 # Store current hand end corordinates
    old_yn = yn                                 #  for overwriting in next loop pass
    time.sleep(.1)

# Tidy up
display.set_pen(black)
display.clear()
display.update()

UF2 used was from here:

The third from the top.

Much easier than we first thought! Hope this helps.

2 Likes

Tony, only one thing to say… you’re a star!

That is an excellent example, which contains really clear code to study. I hope you you are able to put it within the Pimoroni examples section to allow new users like me to learn from it.

Thank you,

Steve

After the above, now for the next stage if possible! I have mentioned this before in that I would like to load in a graphics file representing the meter scale as drawing the more complex coloured scales in code could be difficult. I use different scales and colours for different types of meter gauges and so need to load in the graphics as appropriate, use then load in another scale as required . My meter scale graphics files are saved in rgb 332 format (3 red bits, 3 blue bits, 2 green bits) to save space and it gives 255 colours, which is good enough,

Q: has Pimoroni implemented directly loading in rgb332 graphics files (or rgb565 files)? I see that they have a jpeg file load, which is good but due to compression the graphics may appear fractionally different, albeit minor.

Once the appropriate scale is loaded in the meter pointer (which will be a long triangular shape) is drawn over the top of the scale and not just the inside of the body of the scale (much like a speedometer). To erase the meter pointer and draw in the next position, I would need to either quickly reload the scale graphic (which would take time and so flicker) or replace the pixel colours of where the last pointer was drawn.

Q: does the Pimoroni uf2 have a facility for reading the pixels from the display buffer to allow me to read/store then replace?

I have enclosed an example of a simple graphic scale I use and and the meter pointer would cross through the scale, which changes colour at different positions.

Meter Scale_160x128_BlueBkg(8bit=0x03)

If the above is not possible then @Tonygo2’s excellent example will be the one to follow and I will not use graphic scales.
Thanks everyone

This is where pure frame buffer in MicroPython has the advantage.
See here:
https://mpython.readthedocs.io/en/master/library/micropython/framebuf.html

FrameBuffer. pixel (x, y [, c ])

If C is not given, the color value of the specified pixel is obtained. If C is given, the specified pixel is set to the given color.

This does what you want to do. It gives the colour at the specified point.

Unfortunately, Pico Graphics have not (yet?) provided this facility. It would be very useful.

I’ve put in a request for it to be added.

Thanks Tony for confirming FrameBuffer. pixel (x, y [, c ]) is not implemented in Pico Graphics and for putting in a request for it to be added. Once done, that section will be sorted.

Improved pointer and text under the pointer. The screen re-draw is so fast that that you do not see any flicker. There is a delay in the loop, which could be reduced.

# Analogue MPH Dial on a Pimoroni Pico Explorer with Pico Graphics

from picographics import PicoGraphics, DISPLAY_LCD_240X240
import math
import time
import random
import machine # for ADC pot

display = PicoGraphics(display=DISPLAY_LCD_240X240)

red = display.create_pen(255,0,0)
white = display.create_pen(255,255,255)
green = display.create_pen(0,255,0)
blue = display.create_pen(0,0,255)
black = display.create_pen(0,0,0)
yellow = display.create_pen(255,255,0)

# =========== Main Program ================
display.set_pen(black)
display.clear()
xc = 120
yc = 120
display.set_pen(blue)
display.circle(xc,yc,100)
r = 103                                      # Tick outer radius
for p in range(0,101,10):                    # White Ticks at 10 % intervals
    theta = p * 2.7 -45                         
    theta_rad = math.radians(theta)          # Angle in radians
    yn = -int(r * math.sin(theta_rad))       # Calculate outer tick coordinates
    xn = -int(r * math.cos(theta_rad))
    display.set_pen(white)
    display.line(120,120,120+xn,120+yn) # Draw the tick from centre
display.set_pen(black)
display.circle(xc,yc,75)           # Overwrite inner tick lines
d=r
display.triangle(xc,yc,xc+d-1,yc+d,xc-d,yc+d)
display.update()

# Counting up in fives
r = 74 # Length of hand
old_xn = 0
old_yn = 0
for p in range(0,101,5): # Percentages at 5 % interval
    theta = p * 2.7 -45
    display.set_pen(black)
    display.line(120,120,120+old_xn,120+old_yn) # Overwrite the old hand
    display.rectangle(0,200,240,25)            # Clear text area
    display.set_pen(green)
    theta_rad = math.radians(theta)
    theta_rad = math.radians(theta)
    display.set_pen(white)
    display.text(str(p),100,200,scale=3)  # Percentage value as text
    yn = -int(r * math.sin(theta_rad))
    xn = -int(r * math.cos(theta_rad))
    display.set_pen(black)
    display.circle(xc,yc,75) # Clear centre
    display.set_pen(white)
    display.text("MPH",100,70,scale=3)
    display.set_pen(red)    
    xnsm = int(xn/10)
    ynsm = int(yn/10)    
    display.triangle(120+xn, 120+yn, 120+ynsm, 120-xnsm, 120-ynsm, 120+xnsm)
    display.circle(xc,yc,6)
    display.update()                            # Update screen
    time.sleep(0.2)                             # Delay
    old_xn = xn                                 # Store current hand end corordinates
    old_yn = yn                                 #  for overwriting in next loop pass
    
display.update()

Thanks again Tony for another fine and clearly coded example.

As you have done, I will keep the pointer in the centre and not let it cross the coloured scales I use, so as to allow easier erasing of the pointer on the solid background. For the time being I will load in my coloured scales via jpeg graphic files until I find out how to load in rgb332 files (which save space). I wrote a PC program to convert bmp to 332 and 565 files and try to use these colour type files as you then know what the restricted colours will look like and the 565 values are the ones which most OLEDs and LCDs use.

This version has the needle passing over the numbers inside the ring. These numbers are re-drawn in each loop but the whole screen updates so quickly that you do not notice. Just not sure how quickly a file screen can be re-drawn.

# Analogue MPH Dial on a Pimoroni Pico Explorer with Pico Graphics
# Tony Goodhew 6 Jan 2023
from picographics import PicoGraphics, DISPLAY_LCD_240X240
import math
import time
import random
import machine # for ADC pot

display = PicoGraphics(display=DISPLAY_LCD_240X240)

red = display.create_pen(255,0,0)
white = display.create_pen(255,255,255)
green = display.create_pen(0,255,0)
blue = display.create_pen(0,0,255)
black = display.create_pen(0,0,0)
yellow = display.create_pen(255,255,0)

# =========== Main Program ================
display.set_font("bitmap8") # Provides Lower Case letters
display.set_pen(black)
display.clear()
# Numbers Outside Ring
xc = 120
yc = 120
display.set_pen(blue)
display.circle(xc,yc,85)
r = 90                                      # Tick outer radius
for p in range(0,101,10):                    # White Ticks at 10 % intervals
    theta = p * 2.7 -45                         
    theta_rad = math.radians(theta)          # Angle in radians
    yn = -int(r * math.sin(theta_rad))       # Calculate outer tick coordinates
    xn = -int(r * math.cos(theta_rad))
    display.set_pen(white)
    display.line(120,120,120+xn,120+yn) # Draw the tick from centre
r = 85                                      # Tick outer radius
for p in range(0,101,5):
    theta = p * 2.7 -45                         
    theta_rad = math.radians(theta)          # Angle in radians
    yn = -int(r * math.sin(theta_rad))       # Calculate outer tick coordinates
    xn = -int(r * math.cos(theta_rad))
    display.set_pen(white)
    display.line(120,120,120+xn,120+yn)
display.set_pen(black)
display.circle(xc,yc,75)           # Overwrite inner tick lines
d=r
display.triangle(xc,yc,xc+d-1,yc+d,xc-d,yc+d)
display.set_pen(white)
display.text("0",45,185,scale=2)
display.text("100",185,185,scale=2)
display.text("10",14,140,scale=2)
display.text("90",210,140,scale=2)
display.text("20",12,98,scale=2)
display.text("80",212,98,scale=2)
display.text("30",23,55,scale=2)
display.text("70",200,55,scale=2)
display.text("40",65,24,scale=2)
display.text("60",155,24,scale=2)
display.text("50",110,9,scale=2)

display.update()

# Counting up in fives
r = 74 # Length of hand
old_xn = 0
old_yn = 0
for p in range(0,101,5): # Percentages at 5 % interval
    theta = p * 2.7 -45
    display.set_pen(black)
    display.line(120,120,120+old_xn,120+old_yn) # Overwrite the old hand
    display.rectangle(50,200,100,25)            # Clear text area
    theta_rad = math.radians(theta)
    theta_rad = math.radians(theta)
    display.set_pen(yellow)
    ts  ="   " + str(p)
    display.text(ts[-3:],100,200,scale=3)  # Percentage value as text
    yn = -int(r * math.sin(theta_rad))
    xn = -int(r * math.cos(theta_rad))
    display.set_pen(black)
    display.circle(xc,yc,75) # Clear centre
    display.set_pen(white)
    display.text("MPH",100,80,scale=3)
    display.set_pen(red)    
    xnsm = int(xn/10)
    ynsm = int(yn/10)    
    display.triangle(120+xn, 120+yn, 120+ynsm, 120-xnsm, 120-ynsm, 120+xnsm)
    display.circle(xc,yc,6)
    display.update()                            # Update screen
    time.sleep(0.2)                             # Delay
    old_xn = xn                                 # Store current hand end corordinates
    old_yn = yn                                 #  for overwriting in next loop pass
    
display.update()

# Numbers Inside Ring
display.set_pen(black)
display.clear()
xc = 120
yc = 120
display.set_pen(blue)
display.circle(xc,yc,116)
r = 118
# Tick outer radius
for p in range(0,101,5):
    theta = p * 2.7 -45                         
    theta_rad = math.radians(theta)          # Angle in radians
    yn = -int(r * math.sin(theta_rad))       # Calculate outer tick coordinates
    xn = -int(r * math.cos(theta_rad))
    display.set_pen(white)
    display.line(120,120,120+xn,120+yn)
display.set_pen(black)
display.circle(xc,yc,100)           # Overwrite inner tick lines
d=r
display.triangle(xc,yc,xc+d-1,yc+d,xc-d,yc+d)
display.set_pen(white)
#display.update()

# Counting up in fives
r = 90 # Length of hand
old_xn = 0
old_yn = 0
for p in range(0,101,1): # Interval of 1 degree
    theta = p * 2.7 -45
    display.set_pen(black)
    display.line(120,120,120+old_xn,120+old_yn) # Overwrite the old hand
    display.rectangle(50,200,100,25)            # Clear text area
    theta_rad = math.radians(theta)
    theta_rad = math.radians(theta)
    
    yn = -int(r * math.sin(theta_rad))
    xn = -int(r * math.cos(theta_rad))
    display.set_pen(black)
    display.circle(xc,yc,90) # Clear centre
    display.set_pen(green)
    display.text("°F",100,80,scale=3)
    display.text("0",60,185,scale=2)
    display.text("100",160,185,scale=2)
    display.text("10",30,140,scale=2)
    display.text("90",195,140,scale=2)
    display.text("20",24,98,scale=2)
    display.text("80",198,98,scale=2)
    display.text("30",43,58,scale=2)
    display.text("70",180,58,scale=2)
    display.text("40",70,35,scale=2)
    display.text("60",153,35,scale=2)
    display.text("50",110,25,scale=2)
    display.set_pen(red)    
    xnsm = int(xn/10)
    ynsm = int(yn/10)    
    display.triangle(120+xn, 120+yn, 120+ynsm, 120-xnsm, 120-ynsm, 120+xnsm)
    display.circle(xc,yc,6)
#    display.update()                            # Update screen
    time.sleep(0.2)                             # Delay
    old_xn = xn                                 # Store current hand end corordinates
    old_yn = yn                                 #  for overwriting in next loop pass
    display.set_pen(yellow)
    ts  ="   " + str(p)
    display.text(ts[-3:],100,200,scale=3)  # Percentage value as text
    display.update()
    time.sleep(0.2)

Have fun.

Thats another good example @Tonygo2. It just gets better and better - thanks! Being new to Python I have learnt more from your clear examples than all the reading I have done these past few weeks.

I have enclosed my simple example using a basic jpg graphic meter scale, which uses varying colours around the scale which I would find difficult to create using just code. When I find how to directly load in raw rgb332 files I will use them rather than jpg files, to save jpg conversion in the pico.

Meter Scale_160x128_BlueBkg(8bit=0x03)

The code is (heavily borrowed from Tony)

# Analogue Dial, Pico Graphics with a DISPLAY_PICO_DISPLAY_2 (320x240)
# Stephen Alsop

from picographics import PicoGraphics, DISPLAY_PICO_DISPLAY_2
import math
import time
import random
import machine # for ADC pot
import jpegdec # for Scale graphic

display = PicoGraphics(display=DISPLAY_PICO_DISPLAY_2)

red = display.create_pen(255,0,0)
white = display.create_pen(255,255,255)
green = display.create_pen(0,255,0)
blue = display.create_pen(0,0,255)
black = display.create_pen(0,0,0)
yellow = display.create_pen(255,255,0)

display.set_font("bitmap8") # allows lower case letters
display.set_pen(black)
display.clear()

# load in meter jpg graphic
j = jpegdec.JPEG(display) # Create a new JPEG decoder for our PicoGraphics
j.open_file("Meter Scale_160x128_BlueBkg(8bit=0x03).jpg") # Open the JPEG/JPG file
j.decode(80, 56, jpegdec.JPEG_SCALE_FULL) # place in screen centre and decode the JPEG

pointer_xc = 160       # pointer centre to match scale graphic position
pointer_yc = 136
pointerradius = 70     # length of pointer
pointwidth = 10        # 5 = thick, 20 = thin
spindleradius = 8

# set scale range to match the graphic
scaledegmin = -30      # where 0 deg is lhs of horizontal line
scaledegmax = 210
scaledegrange = scaledegmax - scaledegmin
scalecentreradius = 67 # meter centre which is cleared to remove pointer

# set input range
inputmin = 0
inputmax = 100
inputrange = inputmax - inputmin

numdegperinput = scaledegrange/inputrange     # calc num of deg per input value

metertitle = "PRESSURE"
# mthalfwidth = int((display.measure_text(metertitle, scale=3, spacing=0))/2)
display.set_pen(white)
display.text(metertitle,100,10,scale=3)        # x y size

def drawpointer(value):
    display.set_pen(black)                     # clear out value text
    display.rectangle(150,210,230,240)         # text area
    display.set_pen(white)
    display.text(str(value),150,210,scale=4)

    # calc position of pointer
    pointer_deg = scaledegmin + (numdegperinput * value) # add in in scale starting deg 
    pointer_rad = math.radians(pointer_deg)              # convert to radians
    yn = -int(pointerradius * math.sin(pointer_rad))     # calc x/y relative to centre of a circle
    xn = -int(pointerradius * math.cos(pointer_rad))     # using length/radius of pointer
    
    display.set_pen(black)                  # wipe out pointer by clearing out
    display.circle(pointer_xc,pointer_yc,scalecentreradius) # meter centre to original colour or black, x y r
 
    display.set_pen(red)                    # pointer colour
    xnsm = int(xn/pointwidth)
    ynsm = int(yn/pointwidth)    
    display.triangle(pointer_xc+xn, pointer_yc+yn, pointer_xc+ynsm, pointer_yc-xnsm, pointer_xc-ynsm, pointer_yc+xnsm) # xy1,xy2,xy3
   
    display.set_pen(green)                              # spindle colour
    display.circle(pointer_xc,pointer_yc,spindleradius) # pointer centre spindle
    
# Count up and down
for p in range(0,101,2): # Percentages at 2% interval, p is value
    drawpointer(p)       # draw the pointer over the scale jpg
    display.update()     # Update screen
    #time.sleep(0.2)      # Delay
for p in range(100,-2,-2): # Percentages at 2% interval, p is value
    drawpointer(p)       # draw the pointer over the scale jpg
    display.update()     # Update screen
    
display.update()

The next step after the rgb332 file loading would be to draw the pointer through the scale rather than just to the inside of the scale. That is for another day.

Thanks

I think you can move away from the JPG file with a bit of maths!

The code is here:

# Drawing different coloured dial sectors
# Tony Goodhew 7th Jan 2023

from picographics import PicoGraphics, DISPLAY_LCD_240X240
import math
import time
import random
import machine # for ADC pot

display = PicoGraphics(display=DISPLAY_LCD_240X240)

red = display.create_pen(255,0,0)
white = display.create_pen(255,255,255)
green = display.create_pen(0,255,0)
blue = display.create_pen(0,0,255)
black = display.create_pen(0,0,0)
yellow = display.create_pen(255,255,0)
cyan = display.create_pen(0,255,255)
magenta = display.create_pen(255,0,255)

# =========== Main Program ================
display.set_font("bitmap8") # Provides Lower Case letters
display.set_pen(black)
display.clear()
xc = 160
yc = 160
display.set_pen(blue)
display.circle(xc,yc,160)
display.update()
display.set_pen(white)
for y in range(240):
    for x in range(161):
        dx = x-xc
        dy = y-yc
        h = math.sqrt(dy*dy + dx*dx)
        if h < 129: # Break out of loop as we are inside the coloured arc
            break
        if (h <= 160) and (h >= 130):
            if dx != 0: # Divide by zero by error trap
            
                theta = math.atan(dy/dx)* 180 / math.pi # angle in degrees
                if (theta <= 0):
                    display.set_pen(red)
                if (theta > 0) and (theta <= 30):
                    display.set_pen(yellow)
                if (theta > 30) and (theta <= 60):
                    display.set_pen(green)
                if theta > 60:
                    display.set_pen(magenta)
                display.pixel(x,y)
                display.pixel(320-x,y)
display.update()


Pretty quick considering how much number crunching this takes. It’s what computers are for! Just Pythagoras and a bit of Trigonometry.

You may want to adjust the bottom corners. I’ve not got a Display 2 but the code should provide the other side.

That’s a good bit of code again. Eventually I will need to add tick marks, etc, but it is very c;ear amd useable.

I am still aiming to use graphic files as the background to allow me to include the scale digits, icons to suit, shading, ticks, borders and then overdraw a pointer. Then to erase the pointer, restore the pixels overwritten, Until that is possible (when framebuffer and memory array drops are available) what you have created and shared is very usable. Again, excellent tuition, thanks Tony for demonstrating it.

I think I ought to rename the post ‘A STUDY ON ANALOG METERS’

Regards, Steve

ps from your knowledge of Python, in order to get max performance due to it being interpretted language, when using lines in loops such as

if (h <= 160) and (h >= 130):

would it be be faster to use a predefined constants at the start of the program, eg
h1 = 160
h2 = 130

then use

if (h <= h1) and (h >= h2):

It would not make any difference in C as the compiler would see the values as constants and fix them, but I am not sure what Python does. It may do the same but to be sure, I thought I would ask.

Pimoroni PicoGraphics Micropython does have a framebuffer (of course!) - but the code they give you interfaces with it behind the scenes. If you want to access it, you can easily turn off their framebuffer and use your own, as below. I needed to do that so that I could READ pixel colours that had been written by the standard functions (so that I could write a cursor block on the display, remember what I’d overwritten,and restore it when the cursor was moved). Here is the code I used:

set up the display

display = PicoGraphics(display=DISPLAY_PICO_EXPLORER, rotate=0, pen_type=PEN_RGB332)
WIDTH, HEIGHT = display.get_bounds() # gets display size, expecting 240, 240

Assign own framebuffer so we can read from it (to be able to restore colours under cursor when it is moved)

display.set_framebuffer(None) # not sure if this is needed
buffer = bytearray(int(WIDTH*HEIGHT))
display.set_framebuffer(buffer)

Example READ FROM buffer

top_pix[i] = buffer[left + i -1 + (top * WIDTH)] # -1 is to allow for corner pixel being stored once

Example WRITE TO buffer

buffer[left0 + i -1 + (top0 * WIDTH)] = top_pix[i]

See my reply below. It’s easy to set the PicoGraphics framebuffer to one you can access for reading or writing:

Assign own framebuffer so we can read from it (to be able to restore colours under cursor when it is moved)

display.set_framebuffer(None)
buffer = bytearray(int(WIDTH*HEIGHT))
display.set_framebuffer(buffer)

Then you can read or write any pixel you like in ‘buffer’

I’m having a bit of trouble with this. Not solved for me.
I change the buffer and draw coloured circles and text on the screen. This works.

I then try to read and store the pixel data from a line of 120 pixels in the ‘MPH’ area of the screen and only get zeros (black? when it is all coloured.)


from picographics import PicoGraphics, DISPLAY_LCD_240X240,PEN_RGB332
import math
import time

display = PicoGraphics(display=DISPLAY_LCD_240X240, rotate=0, pen_type=PEN_RGB332)
WIDTH, HEIGHT = display.get_bounds() # gets display size, expecting 240, 240
print(WIDTH,HEIGHT)

# Assign own framebuffer so we can read from it (to be able to restore colours under cursor when it is moved)
display.set_framebuffer(None) # not sure if this is needed
buffer = bytearray(int(WIDTH*HEIGHT))
display.set_framebuffer(buffer)

'''
# Example READ FROM buffer
top_pix[i] = buffer[left + i -1 + (top * WIDTH)] # -1 is to allow for corner pixel being stored once

# Example WRITE TO buffer
buffer[left0 + i -1 + (top0 * WIDTH)] = top_pix[i]
'''
display = PicoGraphics(display=DISPLAY_LCD_240X240)

red = display.create_pen(255,0,0)
white = display.create_pen(255,255,255)
green = display.create_pen(0,255,0)
blue = display.create_pen(0,0,255)
black = display.create_pen(0,0,0)
yellow = display.create_pen(255,255,0)
cyan = display.create_pen(0,255,255)
magenta = display.create_pen(255,0,255)

# =========== Main Program ================
display.set_font("bitmap8") # Provides Lower Case letters
display.set_pen(black)
display.clear()

xc = 160
yc = 160
display.set_pen(blue)
display.circle(xc,yc,160)
display.set_pen(red)
display.circle(xc,yc,140)
display.set_pen(yellow)
display.circle(xc,yc,120)
display.set_pen(green)
display.text("MPH",2,115,scale=3)
display.update()# set up the display
time.sleep(1)

# Grab a line of 120 pixels
# from 'MPH' area of screen
grabbed = bytearray(int(120))

left = 0
top = 120
for i in range(120):
    grabbed[i] = buffer[left + i -1 + (top * WIDTH)]
    print(grabbed[i])

Can somebody explain what I am doing wrong?

@Tonygo2, you have duplicated the display definition at line 21.

When removed (although I use a DISPLAY_PICO_DISPLAY_2) I get
320 240
0
0
0
28
28
28
28
28
28
3
3
3
28
28
28
28
28
28
3
3
3
28
28
28
3
3
3
224
224
224
28
28
28
224
224
224
28
28
etc.

Thank for the the buffer solution @ExperiMentor. Do you know if there is a ‘hidden’ way to directly load in rgb332 files?

@SteveA , Thanks for finding my blunder.

Here it is working with procedures to grab and replace

# Grab and replace a screen section
from picographics import PicoGraphics, DISPLAY_LCD_240X240,PEN_RGB332
import math
import time

display = PicoGraphics(display=DISPLAY_LCD_240X240, rotate=0, pen_type=PEN_RGB332)
WIDTH, HEIGHT = display.get_bounds() # gets display size, expecting 240, 240
print(WIDTH,HEIGHT)

# Assign own framebuffer so we can read from it (to be able to restore colours under cursor when it is moved)
display.set_framebuffer(None) # not sure if this is needed
buffer = bytearray(int(WIDTH*HEIGHT))
display.set_framebuffer(buffer)

red = display.create_pen(255,0,0)
white = display.create_pen(255,255,255)
green = display.create_pen(0,255,0)
blue = display.create_pen(0,0,255)
black = display.create_pen(0,0,0)
yellow = display.create_pen(255,255,0)
cyan = display.create_pen(0,255,255)
magenta = display.create_pen(255,0,255)


def grab(left, top, w, h):  # Grab a rectangle of screen
    i = 0
    for y in range(top,top + h):
        for x in range(w):
            grabbed[i] = buffer[left + x -1 + (y * WIDTH)]
            i = i+1

def replace(left, top, w, h):  # Replace the save screen rectangle
    i = 0
    for y in range(top,top + h):
        for x in range(w):
            buffer[left + x -1 + (y * WIDTH)] = grabbed[i]            
            i = i+1

# =========== Main Program ================
display.set_font("bitmap8") # Provides Lower Case letters
display.set_pen(black)
display.clear()

xc = 160
yc = 160
display.set_pen(blue)
display.circle(xc,yc,160)
display.set_pen(red)
display.circle(xc,yc,140)
display.set_pen(yellow)
display.circle(xc,yc,120)
display.set_pen(green)
display.text("MPH",2,115,scale=3)
display.update()
time.sleep(1)

# Small screen buffer for saved area
grabbed = bytearray(int(60*60))

# Save screen section round 'MPH'
grab(0,114,55,30)

# Damage part of screen
display.set_pen(black)
display.rectangle(2,115,50,25)
display.update()
time.sleep(1)

# Replace screen section
replace(0,114,55,30)
display.update()

Thank you @ExperiMentor

I have learnt more in the past few weeks and it has improved my graphic handling immensely so I want to say a sincere thanks to all of you for giving your time to contribute and explain everything.
Steve