在终端/dev/fb0每秒显示当前时间。
#隐藏控制台光标
# 开启
echo 1 > /sys/class/graphics/fbcon/cursor_blink
# 关闭
echo 0 > /sys/class/graphics/fbcon/cursor_blink
#text2fb0.py
import time
import datetime
import pytz
from PIL import Image, ImageDraw, ImageFont
import numpy
#pip3 install pillow pytz numpy
def get_time():
# 获取当前时间
tz = pytz.timezone('Asia/Shanghai')
t = datetime.datetime.now(tz)
return t
def get_time_str():
# 获取当前时间字符串
t = get_time()
return t.strftime('%Y-%m-%d %H:%M:%S')
def draw_text_on_framebuffer(text):
# 创建一个空白图像
fb = Framebuffer(0)
image = Image.new('RGB',size=fb.size, color=(0, 0, 0))
draw = ImageDraw.Draw(image)
# 在图像上绘制文本
text_position = (10, 10) # 文本位置
text_color = (255, 255, 255) # 白色文本
font_size = 48 # 字体大小
font = ImageFont.truetype("/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc", font_size) # 使用指定大小的字体,fc-list查看本机字体
draw.text(text_position, text, font=font, fill=text_color)
#cx = fb.size[0] // 2
#cy = fb.size[1] // 2
#draw.rectangle((cx - 10, cy -10, cx + 10, cy + 10), "white")
fb.show(image)
def _read_and_convert_to_ints(filename):
with open(filename, "r") as fp:
content = fp.read()
tokens = content.strip().split(",")
return [int(t) for t in tokens if t]
def _converter_argb(image: Image):
return bytes([x for r, g, b in image.getdata() for x in (255, r, g, b)])
def _converter_rgb565(image: Image):
return bytes([x for r, g, b in image.getdata()
for x in ((g & 0x1c) << 3 | (b >> 3), r & 0xf8 | (g >> 3))])
def _converter_1_argb(image: Image):
return bytes([x for p in image.getdata()
for x in (255, p, p, p)])
def _converter_1_rgb(image: Image):
return bytes([x for p in image.getdata()
for x in (p, p, p)])
def _converter_1_rgb565(image: Image):
return bytes([(255 if x else 0) for p in image.getdata()
for x in (p, p)])
def _converter_rgba_rgb565_numpy(image: Image):
flat = numpy.frombuffer(image.tobytes(), dtype=numpy.uint32)
# note, this is assumes little endian byteorder and results in
# the following packing of an integer:
# bits 0-7: red, 8-15: green, 16-23: blue, 24-31: alpha
flat = ((flat & 0xf8) << 8) | ((flat & 0xfc00) >> 5) | ((flat & 0xf80000) >> 19)
return flat.astype(numpy.uint16).tobytes()
def _converter_no_change(image: Image):
return image.tobytes()
# anything that does not use numpy is hopelessly slow
_CONVERTER = {
("RGBA", 16): _converter_rgba_rgb565_numpy,
("RGB", 16): _converter_rgb565,
("RGB", 24): _converter_no_change,
("RGB", 32): _converter_argb,
("RGBA", 32): _converter_no_change,
# note numpy does not work well with mode="1" images as
# image.tobytes() loses pixel color info
("1", 16): _converter_1_rgb565,
("1", 24): _converter_1_rgb,
("1", 32): _converter_1_argb,
}
class Framebuffer(object):
def __init__(self, device_no: int):
self.path = f"/dev/fb{device_no}"
config_dir = f"/sys/class/graphics/fb{device_no}"
self.size = tuple(_read_and_convert_to_ints(
config_dir + "/virtual_size"))
self.stride = _read_and_convert_to_ints(config_dir + "/stride")[0]
self.bits_per_pixel = _read_and_convert_to_ints(
config_dir + "/bits_per_pixel")[0]
assert self.stride == self.bits_per_pixel // 8 * self.size[0]
def __str__(self):
args = (self.path, self.size, self.stride, self.bits_per_pixel)
return "%s size:%s stride:%s bits_per_pixel:%s" % args
# Note: performance is terrible even for medium resolutions
def show(self, image: Image):
converter = _CONVERTER[(image.mode, self.bits_per_pixel)]
assert image.size == self.size
out = converter(image)
with open(self.path, "wb") as fp:
fp.write(out)
def on(self):
pass
def off(self):
pass
#主程序
if __name__ == "__main__":
# 循环输出时间到/dev/fb0。每秒一次
while True:
draw_text_on_framebuffer(get_time_str())
time.sleep(1)
#参考地址
https://raspi.muth.org/framebuffer.html