aboutsummaryrefslogtreecommitdiffstats
path: root/klippy/extras/display/menu.py
diff options
context:
space:
mode:
Diffstat (limited to 'klippy/extras/display/menu.py')
-rw-r--r--klippy/extras/display/menu.py137
1 files changed, 111 insertions, 26 deletions
diff --git a/klippy/extras/display/menu.py b/klippy/extras/display/menu.py
index 07f3f3b8..c4db2baf 100644
--- a/klippy/extras/display/menu.py
+++ b/klippy/extras/display/menu.py
@@ -59,6 +59,13 @@ class MenuElement(object):
# override
def is_enabled(self):
+ return self.eval_enable()
+
+ # override
+ def reset_editing(self):
+ pass
+
+ def eval_enable(self):
return self._parse_bool(self._enable)
def init(self):
@@ -246,6 +253,11 @@ class MenuContainer(MenuElement):
def is_editing(self):
return any([item.is_editing() for item in self._items])
+ def reset_editing(self):
+ for item in self._items:
+ if item.is_editing():
+ item.reset_editing()
+
def _lookup_item(self, item):
if isinstance(item, str):
s = item.strip()
@@ -514,6 +526,9 @@ class MenuInput(MenuCommand):
def is_editing(self):
return self._input_value is not None
+ def reset_editing(self):
+ self.reset_value()
+
def _onchange(self):
self._manager.queue_gcode(self.get_gcode())
@@ -573,6 +588,7 @@ class MenuGroup(MenuContainer):
self._sep = sep
self._show_back = False
self.selected = None
+ self.use_cursor = self._asbool(config.get('use_cursor', 'false'))
self.items = config.get('items', '')
def is_accepted(self, item):
@@ -599,9 +615,20 @@ class MenuGroup(MenuContainer):
def _render_item(self, item, selected=False, scroll=False):
name = "%s" % str(item.render(scroll))
if selected and not self.is_editing():
- name = name if self._manager.blink_slow_state else ' '*len(name)
+ if self.use_cursor:
+ name = (item.cursor if isinstance(item, MenuElement)
+ else MenuCursor.SELECT) + name
+ else:
+ name = (name if self._manager.blink_slow_state
+ else ' '*len(name))
elif selected and self.is_editing():
- name = name if self._manager.blink_fast_state else ' '*len(name)
+ if self.use_cursor:
+ name = MenuCursor.EDIT + name
+ else:
+ name = (name if self._manager.blink_fast_state
+ else ' '*len(name))
+ elif self.use_cursor:
+ name = MenuCursor.NONE + name
return name
def _render(self):
@@ -626,6 +653,9 @@ class MenuGroup(MenuContainer):
logging.exception("Call selected error")
return res
+ def reset_editing(self):
+ return self._call_selected('reset_editing')
+
def is_editing(self):
return self._call_selected('is_editing')
@@ -799,6 +829,8 @@ class MenuCard(MenuGroup):
def __init__(self, manager, config, namespace=''):
super(MenuCard, self).__init__(manager, config, namespace)
self.content = config.get('content')
+ self._allow_without_selection = self._asbool(
+ config.get('allow_without_selection', 'true'))
if not self.items:
self.content = self._parse_content_items(self.content)
@@ -852,6 +884,8 @@ class MenuCard(MenuGroup):
if self.selected is not None:
self.selected = (
(self.selected % len(self)) if len(self) > 0 else None)
+ if self._allow_without_selection is False and self.selected is None:
+ self.selected = 0 if len(self) > 0 else None
items = []
for i, item in enumerate(self):
@@ -947,6 +981,7 @@ class MenuManager:
self.timeout_idx = 0
self.lcd_chip = lcd_chip
self.printer = config.get_printer()
+ self.pconfig = self.printer.lookup_object('configfile')
self.gcode = self.printer.lookup_object('gcode')
self.gcode_queue = []
self.parameters = {}
@@ -1002,21 +1037,12 @@ class MenuManager:
self.gcode.register_mux_command("MENU", "DO", 'dump', self.cmd_DO_DUMP,
desc=self.cmd_DO_help)
- # Parse local config file in same directory as current module
- pconfig = self.printer.lookup_object('configfile')
- localname = os.path.join(os.path.dirname(__file__), 'menu.cfg')
- localconfig = pconfig.read_config(localname)
-
- # Load items from local config
- self.load_menuitems(localconfig)
+ # Load local config file in same directory as current module
+ self.load_config(os.path.dirname(__file__), 'menu.cfg')
# Load items from main config
self.load_menuitems(config)
-
# Load menu root
- if self._root is not None:
- self.root = self.lookup_menuitem(self._root)
- if isinstance(self.root, MenuDeck):
- self._autorun = True
+ self.load_root()
def printer_state(self, state):
if state == 'ready':
@@ -1071,6 +1097,45 @@ class MenuManager:
return (self._autorun is True and self.root is not None
and self.stack_peek() is self.root and self.selected == 0)
+ def restart_root(self, root=None, force_exit=True):
+ if self.is_running():
+ self.exit(force_exit)
+ self.load_root(root)
+
+ def load_root(self, root=None, autorun=False):
+ root = self._root if root is None else root
+ if root is not None:
+ self.root = self.lookup_menuitem(root)
+ if isinstance(self.root, MenuDeck):
+ self._autorun = True
+ else:
+ self._autorun = autorun
+
+ def register_object(self, obj, name=None, override=False):
+ """Register an object with a "get_status" callback"""
+ if obj is not None:
+ if name is None:
+ name = obj.__class__.__name__
+ if override or name not in self.objs:
+ self.objs[name] = obj
+
+ def unregister_object(self, name):
+ """Unregister an object from "get_status" callback list"""
+ if name is not None:
+ if not isinstance(name, str):
+ name = name.__class__.__name__
+ if name in self.objs:
+ self.objs.pop(name)
+
+ def after(self, timeout, callback, *args):
+ """Helper method for reactor.register_callback.
+ The callback will be executed once after given timeout (sec)."""
+ def callit(eventtime):
+ callback(eventtime, *args)
+ reactor = self.printer.get_reactor()
+ starttime = reactor.monotonic() + max(0., float(timeout))
+ reactor.register_callback(callit, starttime)
+
def is_running(self):
return self.running
@@ -1106,15 +1171,16 @@ class MenuManager:
def update_parameters(self, eventtime):
self.parameters = {}
+ objs = dict(self.objs)
# getting info this way is more like hack
# all modules should have special reporting method (maybe get_status)
# for available parameters
# Only 2 level dot notation
- for name in self.objs.keys():
+ for name in objs.keys():
try:
- if self.objs[name] is not None:
- class_name = str(self.objs[name].__class__.__name__)
- get_status = getattr(self.objs[name], "get_status", None)
+ if objs[name] is not None:
+ class_name = str(objs[name].__class__.__name__)
+ get_status = getattr(objs[name], "get_status", None)
if callable(get_status):
self.parameters[name] = get_status(eventtime)
else:
@@ -1123,7 +1189,7 @@ class MenuManager:
self.parameters[name].update({'is_enabled': True})
# get additional info
if class_name == 'ToolHead':
- pos = self.objs[name].get_position()
+ pos = objs[name].get_position()
self.parameters[name].update({
'xpos': pos[0],
'ypos': pos[1],
@@ -1139,21 +1205,21 @@ class MenuManager:
self.parameters[name]['status'] == "Idle")
})
elif class_name == 'PrinterExtruder':
- info = self.objs[name].get_heater().get_status(
+ info = objs[name].get_heater().get_status(
eventtime)
self.parameters[name].update(info)
elif class_name == 'PrinterLCD':
self.parameters[name].update({
- 'progress': self.objs[name].progress or 0,
- 'message': self.objs[name].message or '',
+ 'progress': objs[name].progress or 0,
+ 'message': objs[name].message or '',
'is_enabled': True
})
elif class_name == 'PrinterHeaterFan':
- info = self.objs[name].fan.get_status(eventtime)
+ info = objs[name].fan.get_status(eventtime)
self.parameters[name].update(info)
elif class_name in ('PrinterOutputPin', 'PrinterServo'):
self.parameters[name].update({
- 'value': self.objs[name].last_value
+ 'value': objs[name].last_value
})
else:
self.parameters[name] = {'is_enabled': False}
@@ -1216,14 +1282,14 @@ class MenuManager:
container = self.stack_peek()
if self.running and isinstance(container, MenuContainer):
container.heartbeat(eventtime)
+ if(isinstance(container, MenuDeck) and not container.is_editing()):
+ container.update_items()
# clamps
self.top_row = max(0, min(
self.top_row, len(container) - self.rows))
self.selected = max(0, min(
self.selected, len(container) - 1))
if isinstance(container, MenuDeck):
- if not container.is_editing():
- container.update_items()
container[self.selected].heartbeat(eventtime)
lines = container[self.selected].render_content(eventtime)
else:
@@ -1392,6 +1458,13 @@ class MenuManager:
self.exit()
elif action == 'respond':
self.gcode.respond_info("{}".format(' '.join(map(str, args))))
+ elif action == 'event' and len(args) > 0:
+ if len(str(args[0])) > 0:
+ self.printer.send_event(
+ "menu:action:" + str(args[0]), *args[1:])
+ else:
+ logging.error("Malformed event call: {} {}".format(
+ action, ' '.join(map(str, args))))
else:
logging.error("Unknown action %s" % (action))
except Exception:
@@ -1429,6 +1502,18 @@ class MenuManager:
"Unknown menuitem '%s'" % (name,))
return self.menuitems[name]
+ def load_config(self, *args):
+ cfg = None
+ filename = os.path.join(*args)
+ try:
+ cfg = self.pconfig.read_config(filename)
+ except Exception:
+ raise self.printer.config_error(
+ "Cannot load config '%s'" % (filename,))
+ if cfg:
+ self.load_menuitems(cfg)
+ return cfg
+
def load_menuitems(self, config):
for cfg in config.get_prefix_sections('menu '):
name = " ".join(cfg.get_name().split()[1:])