diff options
Diffstat (limited to 'klippy/extras/display/display.py')
-rw-r--r-- | klippy/extras/display/display.py | 176 |
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) |