I have a SH1106 display connected to my Raspberry Pi that I'm controlling using luma.oled.
I can display all kind of content in different fonts, which is great. However, I can't figure out how to add something to what's currently being displayed without refreshing the whole display. My code is like this:
from os import system
import serial
from time import sleep
from luma.core.interface.serial import i2c
from luma.core.render import canvas
from luma.oled.device import sh1106
from PIL import ImageFont
# config display
device = sh1106(i2c(port=1, address=0x3C), rotate=0)
device.clear()
FA_solid = ImageFont.truetype('/home/pi/Desktop/tests/fa-solid-900.ttf', 16)
FA_regular = ImageFont.truetype('/home/pi/Desktop/tests/fa-regular-400.ttf', 16)
text_large = ImageFont.truetype('/home/pi/Desktop/tests/coolvetica condensed rg.ttf', 48)
text_small = ImageFont.truetype('/home/pi/Desktop/tests/coolvetica condensed rg.ttf', 16)
# display things
def show_icon(code):
with canvas(device) as draw:
draw.text((112, 0), text=code, font=FA_solid, fill="white")
def large_text(content, paddingleft =0, paddingtop =0):
with canvas(device) as draw:
draw.text((0, 0), text=content, font=text_large, fill="white")
def small_text(content, paddingleft =0, paddingtop =0):
with canvas(device) as draw:
draw.text((0, 0), text=content, font=text_small, fill="white")
show_icon("\uf124")
sleep(2)
large_text("Hi ;)")
sleep(10)
device.clear()
This display an icon from fontawesome in the upper right corner, then clears the screen and displays Hi. How can I change this to display the icon + hi? Ideally I'd have "zones" on the screen where I can change the icon zone while keeping the text displayed and vice versa. Thanks!
EDIT --------------------
Here's my code, adapted from Mark's answer below. Better but still not there yet. The Zones 1 and 3 stay the same while 2 is updated but when I redraw the screen, it is blank for half a second and then updates, which I don't want.
def UpdateDisplay(z1,z2,z3):
"""Pass in the three zones and they will be sent to the screen"""
device = sh1106(i2c(port=1, address=0x3C), rotate=0)
# Make a black canvas the size of the entire screen
whole = Image.new("1", (128,64))
# Now paste in the 3 zones to form the whole
whole.paste(z1, (2,2)) # zone1 at top-left
whole.paste(z2, (66,2)) # zone2 at top-right
whole.paste(z3, (2,34)) # zone3 across the bottom
# I save the image here, but you would write it to the screen with "device.display()"
device.display(whole)
return
# Make zone1 dark grey and annotate it
z1 = Image.new("1", (60,30))
z1draw = ImageDraw.Draw(z1)
z1draw.text((10,10),"Zone1", fill="white")
# Make zone2 mid-grey and annotate it
z2 = Image.new("1", (60,30))
z2draw = ImageDraw.Draw(z2)
z2draw.text((10,10),"Zone2", fill="white")
# Make zone3 light grey and annotate it
z3 = Image.new("1", (124,28))
z3draw = ImageDraw.Draw(z3)
z3draw.text((10,10),"Zone3", fill="white")
# Blit all zones to display
UpdateDisplay(z1,z2,z3)
sleep(5)
# Make zone2 mid-grey and annotate it
z2 = Image.new("1", (60,30))
z2draw = ImageDraw.Draw(z2)
z2draw.text((10,10),"Zone2 changed", fill="white")
UpdateDisplay(z1,z2,z3)
ok so I mostly figured it out:
from luma.core.interface.serial import i2c
from luma.core.render import canvas
from luma.oled.device import sh1106
from PIL import ImageFont, Image, ImageDraw
### setting up display using LUMA oled
device = sh1106(i2c(port=1, address=0x3C), rotate=0)
device.clear()
### Initialize drawing zone (aka entire screen)
output = Image.new("1", (128,64))
add_to_image = ImageDraw.Draw(output)
### I have the exterior temp and altitude I want to display. Each has an assigned zone for the icon (FontAwesome) and the data
# temp_ext
temp_zone = [(14,44), (36,64)]
temp_start = (14,44)
temp_icon_zone = [(0,48), (15,64)]
temp_icon_start = (3,48)
add_to_image.text(temp_icon_start, "\uf2c9", font=FA_solid, fill="white")
### every time I have a new reading, I basically draw a black rectangle over what I had and the rewrite the text
add_to_image.rectangle(temp_zone, fill="black", outline = "black")
add_to_image.text(temp_start, str(temp_c), font=text_medium, fill="white")
device.display(output)
This enables me to only update the part of the screen I want, leaving the rest as is and, crucially, not having a blank screen for half a second when rewriting info. Feel free to suggest optimizations!
I still need to look into memory usage, it feels kinda sluggish when the different zones are updating at once. But it works!