aboutsummaryrefslogtreecommitdiffstats
path: root/klippy/extras/display/display.py
diff options
context:
space:
mode:
Diffstat (limited to 'klippy/extras/display/display.py')
-rw-r--r--klippy/extras/display/display.py176
1 files changed, 108 insertions, 68 deletions
diff --git a/klippy/extras/display/display.py b/klippy/extras/display/display.py
index e9ba31d6..c381cf09 100644
--- a/klippy/extras/display/display.py
+++ b/klippy/extras/display/display.py
@@ -14,77 +14,90 @@ REDRAW_TIME = 0.500
REDRAW_MIN_TIME = 0.100
LCD_chips = {
- 'st7920': st7920.ST7920, 'emulated_st7920': st7920.EmulatedST7920,
- 'hd44780': hd44780.HD44780, 'uc1701': uc1701.UC1701,
- 'ssd1306': uc1701.SSD1306, 'sh1106': uc1701.SH1106,
- 'hd44780_spi': hd44780_spi.hd44780_spi,
- 'aip31068_spi':aip31068_spi.aip31068_spi
+ "st7920": st7920.ST7920,
+ "emulated_st7920": st7920.EmulatedST7920,
+ "hd44780": hd44780.HD44780,
+ "uc1701": uc1701.UC1701,
+ "ssd1306": uc1701.SSD1306,
+ "sh1106": uc1701.SH1106,
+ "hd44780_spi": hd44780_spi.hd44780_spi,
+ "aip31068_spi": aip31068_spi.aip31068_spi,
}
+
# Storage of [display_template my_template] config sections
class DisplayTemplate:
def __init__(self, config):
self.printer = config.get_printer()
name_parts = config.get_name().split()
if len(name_parts) != 2:
- raise config.error("Section name '%s' is not valid"
- % (config.get_name(),))
+ raise config.error("Section name '%s' is not valid" % (config.get_name(),))
self.name = name_parts[1]
self.params = {}
- for option in config.get_prefix_options('param_'):
+ for option in config.get_prefix_options("param_"):
try:
self.params[option] = ast.literal_eval(config.get(option))
except ValueError as e:
raise config.error(
- "Option '%s' in section '%s' is not a valid literal" % (
- option, config.get_name()))
- gcode_macro = self.printer.load_object(config, 'gcode_macro')
- self.template = gcode_macro.load_template(config, 'text')
+ "Option '%s' in section '%s' is not a valid literal"
+ % (option, config.get_name())
+ )
+ gcode_macro = self.printer.load_object(config, "gcode_macro")
+ self.template = gcode_macro.load_template(config, "text")
+
def get_params(self):
return self.params
+
def render(self, context, **kwargs):
params = dict(self.params)
params.update(**kwargs)
if len(params) != len(self.params):
raise self.printer.command_error(
- "Invalid parameter to display_template %s" % (self.name,))
+ "Invalid parameter to display_template %s" % (self.name,)
+ )
context = dict(context)
context.update(params)
return self.template.render(context)
+
# Store [display_data my_group my_item] sections (one instance per group name)
class DisplayGroup:
def __init__(self, config, name, data_configs):
# Load and parse the position of display_data items
items = []
for c in data_configs:
- pos = c.get('position')
+ pos = c.get("position")
try:
- row, col = [int(v.strip()) for v in pos.split(',')]
+ row, col = [int(v.strip()) for v in pos.split(",")]
except:
- raise config.error("Unable to parse 'position' in section '%s'"
- % (c.get_name(),))
+ raise config.error(
+ "Unable to parse 'position' in section '%s'" % (c.get_name(),)
+ )
items.append((row, col, c.get_name()))
# Load all templates and store sorted by display position
configs_by_name = {c.get_name(): c for c in data_configs}
printer = config.get_printer()
- gcode_macro = printer.load_object(config, 'gcode_macro')
+ gcode_macro = printer.load_object(config, "gcode_macro")
self.data_items = []
for row, col, name in sorted(items):
c = configs_by_name[name]
- if c.get('text'):
- template = gcode_macro.load_template(c, 'text')
+ if c.get("text"):
+ template = gcode_macro.load_template(c, "text")
self.data_items.append((row, col, template))
+
def show(self, display, templates, eventtime):
context = self.data_items[0][2].create_template_context(eventtime)
- context['draw_progress_bar'] = display.draw_progress_bar
+ context["draw_progress_bar"] = display.draw_progress_bar
+
def render(name, **kwargs):
return templates[name].render(context, **kwargs)
- context['render'] = render
+
+ context["render"] = render
for row, col, template in self.data_items:
text = template.render(context)
- display.draw_text(row, col, text.replace('\n', ''), eventtime)
- context.clear() # Remove circular references for better gc
+ display.draw_text(row, col, text.replace("\n", ""), eventtime)
+ context.clear() # Remove circular references for better gc
+
# Global cache of DisplayTemplate, DisplayGroup, and glyphs
class PrinterDisplayTemplate:
@@ -94,76 +107,89 @@ class PrinterDisplayTemplate:
self.display_data_groups = {}
self.display_glyphs = {}
self.load_config(config)
+
def get_display_templates(self):
return self.display_templates
+
def get_display_data_groups(self):
return self.display_data_groups
+
def get_display_glyphs(self):
return self.display_glyphs
+
def _parse_glyph(self, config, glyph_name, data, width, height):
glyph_data = []
- for line in data.split('\n'):
- line = line.strip().replace('.', '0').replace('*', '1')
+ for line in data.split("\n"):
+ line = line.strip().replace(".", "0").replace("*", "1")
if not line:
continue
- if len(line) != width or line.replace('0', '').replace('1', ''):
+ if len(line) != width or line.replace("0", "").replace("1", ""):
raise config.error("Invalid glyph line in %s" % (glyph_name,))
glyph_data.append(int(line, 2))
if len(glyph_data) != height:
raise config.error("Glyph %s incorrect lines" % (glyph_name,))
return glyph_data
+
def load_config(self, config):
# Load default display config file
- pconfig = self.printer.lookup_object('configfile')
- filename = os.path.join(os.path.dirname(__file__), 'display.cfg')
+ pconfig = self.printer.lookup_object("configfile")
+ filename = os.path.join(os.path.dirname(__file__), "display.cfg")
try:
dconfig = pconfig.read_config(filename)
except Exception:
- raise self.printer.config_error("Cannot load config '%s'"
- % (filename,))
+ raise self.printer.config_error("Cannot load config '%s'" % (filename,))
# Load display_template sections
- dt_main = config.get_prefix_sections('display_template ')
- dt_main_names = { c.get_name(): 1 for c in dt_main }
- dt_def = [c for c in dconfig.get_prefix_sections('display_template ')
- if c.get_name() not in dt_main_names]
+ dt_main = config.get_prefix_sections("display_template ")
+ dt_main_names = {c.get_name(): 1 for c in dt_main}
+ dt_def = [
+ c
+ for c in dconfig.get_prefix_sections("display_template ")
+ if c.get_name() not in dt_main_names
+ ]
for c in dt_main + dt_def:
dt = DisplayTemplate(c)
self.display_templates[dt.name] = dt
# Load display_data sections
- dd_main = config.get_prefix_sections('display_data ')
- dd_main_names = { c.get_name(): 1 for c in dd_main }
- dd_def = [c for c in dconfig.get_prefix_sections('display_data ')
- if c.get_name() not in dd_main_names]
+ dd_main = config.get_prefix_sections("display_data ")
+ dd_main_names = {c.get_name(): 1 for c in dd_main}
+ dd_def = [
+ c
+ for c in dconfig.get_prefix_sections("display_data ")
+ if c.get_name() not in dd_main_names
+ ]
groups = {}
for c in dd_main + dd_def:
name_parts = c.get_name().split()
if len(name_parts) != 3:
- raise config.error("Section name '%s' is not valid"
- % (c.get_name(),))
+ raise config.error("Section name '%s' is not valid" % (c.get_name(),))
groups.setdefault(name_parts[1], []).append(c)
for group_name, data_configs in groups.items():
dg = DisplayGroup(config, group_name, data_configs)
self.display_data_groups[group_name] = dg
# Load display glyphs
- dg_prefix = 'display_glyph '
+ dg_prefix = "display_glyph "
self.display_glyphs = 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]
+ 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):]
- data = dg.get('data', None)
+ glyph_name = dg.get_name()[len(dg_prefix) :]
+ data = dg.get("data", None)
if data is not None:
idata = self._parse_glyph(config, glyph_name, data, 16, 16)
- icon1 = [(bits >> 8) & 0xff for bits in idata]
- icon2 = [bits & 0xff for bits in idata]
- icons.setdefault(glyph_name, {})['icon16x16'] = (icon1, icon2)
- data = dg.get('hd44780_data', None)
+ icon1 = [(bits >> 8) & 0xFF for bits in idata]
+ icon2 = [bits & 0xFF for bits in idata]
+ icons.setdefault(glyph_name, {})["icon16x16"] = (icon1, icon2)
+ data = dg.get("hd44780_data", None)
if data is not None:
- slot = dg.getint('hd44780_slot', minval=0, maxval=7)
+ slot = dg.getint("hd44780_slot", minval=0, maxval=7)
idata = self._parse_glyph(config, glyph_name, data, 5, 8)
- icons.setdefault(glyph_name, {})['icon5x8'] = (slot, idata)
+ icons.setdefault(glyph_name, {})["icon5x8"] = (slot, idata)
+
def lookup_display_templates(config):
printer = config.get_printer()
@@ -173,16 +199,17 @@ def lookup_display_templates(config):
printer.add_object("display_template", dt)
return dt
+
class PrinterLCD:
def __init__(self, config):
self.printer = config.get_printer()
self.reactor = self.printer.get_reactor()
# Load low-level lcd handler
- self.lcd_chip = config.getchoice('lcd_type', LCD_chips)(config)
+ self.lcd_chip = config.getchoice("lcd_type", LCD_chips)(config)
# Load menu and display_status
self.menu = None
name = config.get_name()
- if name == 'display':
+ if name == "display":
# only load menu for primary display
self.menu = menu.MenuManager(config, self)
self.printer.load_object(config, "display_status")
@@ -194,30 +221,37 @@ class PrinterLCD:
dgroup = "_default_16x4"
if self.lcd_chip.get_dimensions()[0] == 20:
dgroup = "_default_20x4"
- dgroup = config.get('display_group', dgroup)
+ dgroup = config.get("display_group", dgroup)
self.show_data_group = self.display_data_groups.get(dgroup)
if self.show_data_group is None:
raise config.error("Unknown display_data group '%s'" % (dgroup,))
# Screen updating
self.printer.register_event_handler("klippy:ready", self.handle_ready)
- self.screen_update_timer = self.reactor.register_timer(
- self.screen_update_event)
+ self.screen_update_timer = self.reactor.register_timer(self.screen_update_event)
self.redraw_request_pending = False
- self.redraw_time = 0.
+ self.redraw_time = 0.0
# Register g-code commands
gcode = self.printer.lookup_object("gcode")
- gcode.register_mux_command('SET_DISPLAY_GROUP', 'DISPLAY', name,
- self.cmd_SET_DISPLAY_GROUP,
- desc=self.cmd_SET_DISPLAY_GROUP_help)
- if name == 'display':
- gcode.register_mux_command('SET_DISPLAY_GROUP', 'DISPLAY', None,
- self.cmd_SET_DISPLAY_GROUP)
+ gcode.register_mux_command(
+ "SET_DISPLAY_GROUP",
+ "DISPLAY",
+ name,
+ self.cmd_SET_DISPLAY_GROUP,
+ desc=self.cmd_SET_DISPLAY_GROUP_help,
+ )
+ if name == "display":
+ gcode.register_mux_command(
+ "SET_DISPLAY_GROUP", "DISPLAY", None, self.cmd_SET_DISPLAY_GROUP
+ )
+
def get_dimensions(self):
return self.lcd_chip.get_dimensions()
+
def handle_ready(self):
self.lcd_chip.init()
# Start screen update timer
self.reactor.update_timer(self.screen_update_timer, self.reactor.NOW)
+
# Screen updating
def screen_update_event(self, eventtime):
if self.redraw_request_pending:
@@ -237,14 +271,16 @@ class PrinterLCD:
logging.exception("Error during display screen update")
self.lcd_chip.flush()
return eventtime + REDRAW_TIME
+
def request_redraw(self):
if self.redraw_request_pending:
return
self.redraw_request_pending = True
self.reactor.update_timer(self.screen_update_timer, self.redraw_time)
+
def draw_text(self, row, col, mixed_text, eventtime):
pos = col
- for i, text in enumerate(mixed_text.split('~')):
+ for i, text in enumerate(mixed_text.split("~")):
if i & 1 == 0:
# write text
self.lcd_chip.write_text(pos, row, text.encode())
@@ -253,20 +289,24 @@ class PrinterLCD:
# write glyph
pos += self.lcd_chip.write_glyph(pos, row, text)
return pos
+
def draw_progress_bar(self, row, col, width, value):
- pixels = -1 << int(width * 8 * (1. - value) + .5)
+ pixels = -1 << int(width * 8 * (1.0 - value) + 0.5)
pixels |= (1 << (width * 8 - 1)) | 1
for i in range(width):
- data = [0xff] + [(pixels >> (i * 8)) & 0xff] * 14 + [0xff]
+ data = [0xFF] + [(pixels >> (i * 8)) & 0xFF] * 14 + [0xFF]
self.lcd_chip.write_graphics(col + width - 1 - i, row, data)
return ""
+
cmd_SET_DISPLAY_GROUP_help = "Set the active display group"
+
def cmd_SET_DISPLAY_GROUP(self, gcmd):
- group = gcmd.get('GROUP')
+ group = gcmd.get("GROUP")
new_dg = self.display_data_groups.get(group)
if new_dg is None:
raise gcmd.error("Unknown display_data group '%s'" % (group,))
self.show_data_group = new_dg
+
def load_config(config):
return PrinterLCD(config)