display: Move glyph definition to printer config

This commit allows to modify the icons (or glyphs) in the displays that
support it. Existing icons can be modified and new icons can be added via
a [display_glyph] section in the config.

Signed-off-by: Florian Heilmann <Florian.Heilmann@gmx.net>
This commit is contained in:
Florian Heilmann 2020-06-07 16:25:19 +00:00 committed by KevinOConnor
parent 722770f62f
commit 3dcac1308e
7 changed files with 196 additions and 173 deletions

View File

@ -1903,6 +1903,17 @@
# template. This field is evaluated using command templates (see # template. This field is evaluated using command templates (see
# docs/Command_Templates.md). This parameter must be provided. # docs/Command_Templates.md). This parameter must be provided.
# Display a custom glyph on displays that support it. The given name
# will be assigned the given display data which can then be referenced
# in the display templates by their name surrounded by two "tilde" symbols
# i.e. ~my_display_glyph~
#[display_glyph my_display_glyph]
#data:
# The display data, stored as 16 lines consisting of 16 bits (1 per pixel)
# e.g. 1111111111111111 to display a solid horizontal line. Put each display
# line into a separate config line. The glyph must consist of exactly 16
# lines with 16 bits each.
# If a primary [display] section has been defined in printer.cfg as shown # If a primary [display] section has been defined in printer.cfg as shown
# above it is possible to define multiple auxilary displays. Note that # above it is possible to define multiple auxilary displays. Note that
# auxilary displays do not currently support menu functionality, thus they # auxilary displays do not currently support menu functionality, thus they

View File

@ -13,7 +13,8 @@ text:
# Show glyph # Show glyph
{% if param_heater_name == "heater_bed" %} {% if param_heater_name == "heater_bed" %}
{% if heater.target %} {% if heater.target %}
~animated_bed~ {% set frame = (printer.toolhead.estimated_print_time|int % 2) + 1 %}
~bed_heat{frame}~
{% else %} {% else %}
~bed~ ~bed~
{% endif %} {% endif %}
@ -35,9 +36,10 @@ text:
{% if 'fan' in printer %} {% if 'fan' in printer %}
{% set speed = printer.fan.speed %} {% set speed = printer.fan.speed %}
{% if speed %} {% if speed %}
~animated_fan~ {% set frame = (printer.toolhead.estimated_print_time|int % 2) + 1 %}
~fan{frame}~
{% else %} {% else %}
~fan~ ~fan1~
{% endif %} {% endif %}
{ "{:>4.0%}".format(speed) } { "{:>4.0%}".format(speed) }
{% endif %} {% endif %}
@ -183,3 +185,140 @@ text:
[display_data _default_20x4 print_status] [display_data _default_20x4 print_status]
position: 3, 0 position: 3, 0
text: { render("_print_status") } text: { render("_print_status") }
######################################################################
# Default display glyphs
######################################################################
[display_glyph extruder]
data:
0000000000000000
0000000000000000
0011111111111100
0000011111100000
0011111111111100
0000011111100000
0011111111111100
0000000000000000
0000111111110000
0000111111010000
0000111111110000
0000000000000000
0000001111000000
0000000110000000
0000000000000000
0000000000000000
[display_glyph bed]
data:
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0001111111110000
0010000000001000
0111111111111100
0000000000000000
0000000000000000
[display_glyph bed_heat1]
data:
0000000000000000
0000000000000000
0010000100001000
0100001000010000
0010000100001000
0001000010000100
0010000100001000
0100001000010000
0010000100001000
0000000000000000
0000000000000000
0001111111110000
0010000000001000
0111111111111100
0000000000000000
0000000000000000
[display_glyph bed_heat2]
data:
0000000000000000
0000000000000000
0010000100001000
0001000010000100
0010000100001000
0100001000010000
0010000100001000
0001000010000100
0010000100001000
0000000000000000
0000000000000000
0001111111110000
0010000000001000
0111111111111100
0000000000000000
0000000000000000
[display_glyph fan1]
data:
0000000000000000
0000000000000000
0000111000000000
0001111000011000
0001111000111100
0000111001111100
0000010000111100
0000000110000000
0000000110000000
0011110000100000
0011111001110000
0011110001111000
0001100001111000
0000000001110000
0000000000000000
0000000000000000
[display_glyph fan2]
data:
0000000000000000
0000000000000000
0000000111100000
0000000111100000
0000000111000000
0011000110000000
0011100000000000
0011110110111100
0011110110111100
0000000000011100
0000000110001100
0000001110000000
0000011110000000
0000011110000000
0000000000000000
0000000000000000
[display_glyph feedrate]
data:
0000000000000000
0000000000000000
1110111011101100
1000100010001010
1100110011001010
1000100010001010
1000111011101100
0000000000000000
1100010011101110
1010101001001000
1100111001001100
1010101001001000
1010101001001110
0000000000000000
0000000000000000
0000000000000000

View File

@ -100,9 +100,6 @@ class PrinterLCD:
self.show_data_group = self.display_data_groups.get(dgroup) self.show_data_group = self.display_data_groups.get(dgroup)
if self.show_data_group is None: if self.show_data_group is None:
raise config.error("Unknown display_data group '%s'" % (dgroup,)) raise config.error("Unknown display_data group '%s'" % (dgroup,))
# Screen updating
self.glyph_helpers = { 'animated_bed': self.animate_bed,
'animated_fan': self.animate_fan }
self.printer.register_event_handler("klippy:ready", self.handle_ready) self.printer.register_event_handler("klippy:ready", self.handle_ready)
self.screen_update_timer = self.reactor.register_timer( self.screen_update_timer = self.reactor.register_timer(
self.screen_update_event) self.screen_update_event)
@ -139,6 +136,28 @@ class PrinterLCD:
for group_name, data_configs in groups.items(): for group_name, data_configs in groups.items():
dg = DisplayGroup(config, group_name, data_configs) dg = DisplayGroup(config, group_name, data_configs)
self.display_data_groups[group_name] = dg self.display_data_groups[group_name] = dg
# Load display glyphs
dg_prefix = 'display_glyph '
icons = {}
dg_main = config.get_prefix_sections(dg_prefix)
dg_main_names = {c.get_name(): 1 for c in dg_main}
dg_def = [c for c in dconfig.get_prefix_sections(dg_prefix)
if c.get_name() not in dg_main_names]
for dg in dg_main + dg_def:
glyph_name = dg.get_name()[len(dg_prefix):]
glyph_data = []
for line in dg.get('data').split('\n'):
if line:
line_val = int(line, 2)
if line_val > 65535:
raise config.error("Glyph line out of range for " + \
"glyph %s maximum is 65535" % (glyph_name,))
glyph_data.append(line_val)
if len(glyph_data) < 16:
raise config.error("Not enough lines for" + \
"glyph %s, 16 lines are needed" % (glyph_name,))
icons[dg.get_name()[len(dg_prefix):]] = glyph_data
self.lcd_chip.set_glyphs(icons)
# Initialization # Initialization
def handle_ready(self): def handle_ready(self):
self.lcd_chip.init() self.lcd_chip.init()
@ -159,13 +178,6 @@ class PrinterLCD:
logging.exception("Error during display screen update") logging.exception("Error during display screen update")
self.lcd_chip.flush() self.lcd_chip.flush()
return eventtime + .500 return eventtime + .500
# Rendering helpers
def animate_bed(self, row, col, eventtime):
frame = int(eventtime) & 1
return self.lcd_chip.write_glyph(col, row, 'bed_heat%d' % (frame + 1,))
def animate_fan(self, row, col, eventtime):
frame = int(eventtime) & 1
return self.lcd_chip.write_glyph(col, row, 'fan%d' % (frame + 1,))
def draw_text(self, row, col, mixed_text, eventtime): def draw_text(self, row, col, mixed_text, eventtime):
pos = col pos = col
for i, text in enumerate(mixed_text.split('~')): for i, text in enumerate(mixed_text.split('~')):
@ -173,8 +185,6 @@ class PrinterLCD:
# write text # write text
self.lcd_chip.write_text(pos, row, text) self.lcd_chip.write_text(pos, row, text)
pos += len(text) pos += len(text)
elif text in self.glyph_helpers:
pos += self.glyph_helpers[text](row, pos, eventtime)
else: else:
# write glyph # write glyph
pos += self.lcd_chip.write_glyph(pos, row, text) pos += self.lcd_chip.write_glyph(pos, row, text)

View File

@ -95,6 +95,8 @@ class HD44780:
data = data[:20 - min(x, 20)] data = data[:20 - min(x, 20)]
pos = x + ((y & 0x02) >> 1) * 20 pos = x + ((y & 0x02) >> 1) * 20
self.text_framebuffers[y & 1][pos:pos+len(data)] = data self.text_framebuffers[y & 1][pos:pos+len(data)] = data
def set_glyphs(self, glyphs):
pass
def write_glyph(self, x, y, glyph_name): def write_glyph(self, x, y, glyph_name):
char = TextGlyphs.get(glyph_name) char = TextGlyphs.get(glyph_name)
if char is not None: if char is not None:

View File

@ -1,146 +0,0 @@
# Common LCD icons
#
# Copyright (C) 2018 Aleph Objects, Inc <marcio@alephobjects.com>
# Copyright (C) 2018 Alexander Fadeev <alfsoft@gmail.com>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
extruder_icon = [
0b0000000000000000,
0b0000000000000000,
0b0011111111111100,
0b0000011111100000,
0b0011111111111100,
0b0000011111100000,
0b0011111111111100,
0b0000000000000000,
0b0000111111110000,
0b0000111111010000,
0b0000111111110000,
0b0000000000000000,
0b0000001111000000,
0b0000000110000000,
0b0000000000000000,
0b0000000000000000
]
bed_icon = [
0b0000000000000000,
0b0000000000000000,
0b0000000000000000,
0b0000000000000000,
0b0000000000000000,
0b0000000000000000,
0b0000000000000000,
0b0000000000000000,
0b0000000000000000,
0b0000000000000000,
0b0000000000000000,
0b0001111111110000,
0b0010000000001000,
0b0111111111111100,
0b0000000000000000,
0b0000000000000000
]
bed_heat1_icon = [
0b0000000000000000,
0b0000000000000000,
0b0010000100001000,
0b0100001000010000,
0b0010000100001000,
0b0001000010000100,
0b0010000100001000,
0b0100001000010000,
0b0010000100001000,
0b0000000000000000,
0b0000000000000000,
0b0001111111110000,
0b0010000000001000,
0b0111111111111100,
0b0000000000000000,
0b0000000000000000
]
bed_heat2_icon = [
0b0000000000000000,
0b0000000000000000,
0b0010000100001000,
0b0001000010000100,
0b0010000100001000,
0b0100001000010000,
0b0010000100001000,
0b0001000010000100,
0b0010000100001000,
0b0000000000000000,
0b0000000000000000,
0b0001111111110000,
0b0010000000001000,
0b0111111111111100,
0b0000000000000000,
0b0000000000000000
]
fan1_icon = [
0b0000000000000000,
0b0000000000000000,
0b0000111000000000,
0b0001111000011000,
0b0001111000111100,
0b0000111001111100,
0b0000010000111100,
0b0000000110000000,
0b0000000110000000,
0b0011110000100000,
0b0011111001110000,
0b0011110001111000,
0b0001100001111000,
0b0000000001110000,
0b0000000000000000,
0b0000000000000000
]
fan2_icon = [
0b0000000000000000,
0b0000000000000000,
0b0000000111100000,
0b0000000111100000,
0b0000000111000000,
0b0011000110000000,
0b0011100000000000,
0b0011110110111100,
0b0011110110111100,
0b0000000000011100,
0b0000000110001100,
0b0000001110000000,
0b0000011110000000,
0b0000011110000000,
0b0000000000000000,
0b0000000000000000
]
feedrate_icon = [
0b0000000000000000,
0b0000000000000000,
0b1110111011101100,
0b1000100010001010,
0b1100110011001010,
0b1000100010001010,
0b1000111011101100,
0b0000000000000000,
0b1100010011101110,
0b1010101001001000,
0b1100111001001100,
0b1010101001001000,
0b1010101001001110,
0b0000000000000000,
0b0000000000000000,
0b0000000000000000
]
Icons16x16 = {
'extruder': extruder_icon,
'bed': bed_icon, 'bed_heat1': bed_heat1_icon, 'bed_heat2': bed_heat2_icon,
'fan': fan1_icon, 'fan1': fan1_icon, 'fan2': fan2_icon,
'feedrate': feedrate_icon,
}

View File

@ -4,7 +4,7 @@
# #
# This file may be distributed under the terms of the GNU GPLv3 license. # This file may be distributed under the terms of the GNU GPLv3 license.
import logging import logging
import icons, font8x14 import font8x14
BACKGROUND_PRIORITY_CLOCK = 0x7fffffff00000000 BACKGROUND_PRIORITY_CLOCK = 0x7fffffff00000000
@ -46,6 +46,7 @@ class ST7920:
] + [(self.graphics_framebuffers[i], bytearray('~'*32), i) ] + [(self.graphics_framebuffers[i], bytearray('~'*32), i)
for i in range(32)] for i in range(32)]
self.cached_glyphs = {} self.cached_glyphs = {}
self.icons = {}
def build_config(self): def build_config(self):
self.mcu.add_config_cmd( self.mcu.add_config_cmd(
"config_st7920 oid=%u cs_pin=%s sclk_pin=%s sid_pin=%s" "config_st7920 oid=%u cs_pin=%s sclk_pin=%s sid_pin=%s"
@ -108,13 +109,12 @@ class ST7920:
0x06, # Set positive update direction 0x06, # Set positive update direction
0x0c] # Enable display and hide cursor 0x0c] # Enable display and hide cursor
self.send(cmds) self.send(cmds)
# Setup animated glyphs
self.cache_glyph('fan2', 'fan1', 0)
self.cache_glyph('bed_heat2', 'bed_heat1', 1)
self.flush() self.flush()
def cache_glyph(self, glyph_name, base_glyph_name, glyph_id): def cache_glyph(self, glyph_name, base_glyph_name, glyph_id):
icon = icons.Icons16x16[glyph_name] icon = self.icons.get(glyph_name)
base_icon = icons.Icons16x16[base_glyph_name] base_icon = self.icons.get(base_glyph_name)
if icon is None or base_icon is None:
return
for i, (bits, base_bits) in enumerate(zip(icon, base_icon)): for i, (bits, base_bits) in enumerate(zip(icon, base_icon)):
pos = glyph_id*32 + i*2 pos = glyph_id*32 + i*2
b1, b2 = (bits >> 8) & 0xff, bits & 0xff b1, b2 = (bits >> 8) & 0xff, bits & 0xff
@ -135,13 +135,19 @@ class ST7920:
gfx_fb -= 32 gfx_fb -= 32
x += 16 x += 16
self.graphics_framebuffers[gfx_fb][x:x+len(data)] = data self.graphics_framebuffers[gfx_fb][x:x+len(data)] = data
def set_glyphs(self, glyphs):
for glyph_name, glyph_data in glyphs.items():
self.icons[glyph_name] = glyph_data
# Setup animated glyphs
self.cache_glyph('fan2', 'fan1', 0)
self.cache_glyph('bed_heat2', 'bed_heat1', 1)
def write_glyph(self, x, y, glyph_name): def write_glyph(self, x, y, glyph_name):
glyph_id = self.cached_glyphs.get(glyph_name) glyph_id = self.cached_glyphs.get(glyph_name)
if glyph_id is not None and x & 1 == 0: if glyph_id is not None and x & 1 == 0:
# Render cached icon using character generator # Render cached icon using character generator
glyph_name = glyph_id[0] glyph_name = glyph_id[0]
self.write_text(x, y, glyph_id[1]) self.write_text(x, y, glyph_id[1])
icon = icons.Icons16x16.get(glyph_name) icon = self.icons.get(glyph_name)
if icon is not None: if icon is not None:
# Draw icon in graphics mode # Draw icon in graphics mode
for i, bits in enumerate(icon): for i, bits in enumerate(icon):

View File

@ -5,7 +5,7 @@
# #
# This file may be distributed under the terms of the GNU GPLv3 license. # This file may be distributed under the terms of the GNU GPLv3 license.
import logging import logging
import icons, font8x14, extras.bus import font8x14, extras.bus
BACKGROUND_PRIORITY_CLOCK = 0x7fffffff00000000 BACKGROUND_PRIORITY_CLOCK = 0x7fffffff00000000
@ -23,10 +23,6 @@ class DisplayBase:
self.font = [self._swizzle_bits(bytearray(c)) self.font = [self._swizzle_bits(bytearray(c))
for c in font8x14.VGA_FONT] for c in font8x14.VGA_FONT]
self.icons = {} self.icons = {}
for name, icon in icons.Icons16x16.items():
top1, bot1 = self._swizzle_bits([d >> 8 for d in icon])
top2, bot2 = self._swizzle_bits(icon)
self.icons[name] = (top1 + top2, bot1 + bot2)
def flush(self): def flush(self):
# Find all differences in the framebuffers and send them to the chip # Find all differences in the framebuffers and send them to the chip
for new_data, old_data, page in self.all_framebuffers: for new_data, old_data, page in self.all_framebuffers:
@ -83,6 +79,11 @@ class DisplayBase:
if (bits << col) & 0x80: if (bits << col) & 0x80:
page[pix_x] ^= bit page[pix_x] ^= bit
pix_x += 1 pix_x += 1
def set_glyphs(self, glyphs):
for glyph_name, glyph_data in glyphs.items():
top1, bot1 = self._swizzle_bits([d >> 8 for d in glyph_data])
top2, bot2 = self._swizzle_bits(glyph_data)
self.icons[glyph_name] = (top1 + top2, bot1 + bot2)
def write_glyph(self, x, y, glyph_name): def write_glyph(self, x, y, glyph_name):
icon = self.icons.get(glyph_name) icon = self.icons.get(glyph_name)
if icon is not None and x < 15: if icon is not None and x < 15: