From dafcfaca6b1a489303e544d7986dc4d7ed0554bb Mon Sep 17 00:00:00 2001 From: Thomas Hooge Date: Tue, 22 Jul 2025 13:12:00 +0200 Subject: [PATCH] =?UTF-8?q?Ankerseite:=20Werte=20=C3=BCber=20Tastatur=20?= =?UTF-8?q?=C3=A4nderbar?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cfgmenu.py | 110 +++++++++++++++++++++++++++++++++++------------- pages/anchor.py | 100 +++++++++++++++++++++++++++---------------- pages/page.py | 13 +++--- 3 files changed, 151 insertions(+), 72 deletions(-) diff --git a/cfgmenu.py b/cfgmenu.py index 6cd2cf2..1e5eab2 100644 --- a/cfgmenu.py +++ b/cfgmenu.py @@ -5,44 +5,94 @@ Menüsystem für Konfiguration(en) class MenuItem(): - def __init__(self, itmname): - self.name = itmname - self.label = None - self.value = None - self.steps = (1,) + def __init__(self, valtype, label, value=None, unit=''): + validtypes = ('int', 'bool') + valtype = valtype.lower() # Groß- und Kleinschreibung lassen wir gemischt zu + if valtype not in validtypes: + raise TypeError(f"Invalid value type: '{valtype}'. Only supported: {validtypes}") + self.label = label # Anzeigetext des Menüeintrag + self.value = value # Zugeordneter Wert für diesen Eintrag + self.unit = unit + self._type = valtype + self._min = 0 + self._max = 99999 + self.steps = (1, 10, 100, 1000) # Sprungmöglichkeiten für +/- Tasten + self.step = 0 # index into tuple above + self.position = None # Menüposition gezählt von 0 an + + def setRange(self, valmin, valmax, steps): + self.min = valmin + self.max = valmax + self.steps = steps def setValue(self, val): - self.value = val + if self._type == 'int': + if val >= self._min and val <= self._max: + self.value = val + return True + elif self.type == 'bool': + self.value = val + return True + return False class Menu(): - def __init__(self): - title = None - x = 0 - y = 0 - w = 100 - h = 20 - items = [] - itm_active = -1 # nothing activated - self._index = -1 - - def addItem(self, label): - itm = MenuItem(label) - items.append(itm) - -class MenuIter(): - - def __init__(self, menu): - self._items = menu.items - self._class_size = len(self._items) - self._index = 0 + def __init__(self, title, x, y): + self.title = title + self.items = {} # Items über Schlüssel zugreifbar + self.activeitem = -1 # Noch nichts aktiv + self._x = x + self._y = y + self._w = 100 + self._h = 20 + self._index = [] # Mapping zwischen Index(Position) und Schlüssel + self._iter_index = 0 def __iter__(self): return self def __next__(self): - if self._index < self._class_size: - itm = items[self._index] - self._index += 1 + if self._iter_index < len(self.items): + itm = self.items[self._index[self._iter_index]] + self._iter_index += 1 return itm - raise StopIteration + self._iter_index = 0 + raise StopIteration + + def addItem(self, key, label, valtype, value=None, unit=''): + if key in self.items.keys(): + raise KeyError(f"Duplicate menu item key: '{key}'") + itm = MenuItem(valtype, label, value, unit) + self.items[key] = itm + self._index.append(key) + itm.position = self._index.index(key) + return itm + + def setItemActive(self, key): + self.activeitem = self._index.index(key) + + def getActiveItem(self): + return self.items[self._index[self.activeitem]] + + def getItemByIndex(self, index): + return self.items[self._index[index]] + + def getItemByKey(self, key): + return self.items[key] + + def getItemCount(self): + return len(self.items) + + def setItemDimension(self, w, h): + self._w = w + self._h = h + + def getXY(self): + return (self._x, self._y) + + def getRect(self): + return (self._x, self._y, self._w, self._h * len(self.items)) + + def getItemRect(self, index): + y = self._y + index * self._h + return (self._x, y, self._w, self._h) diff --git a/pages/anchor.py b/pages/anchor.py index 28edec3..14ee7c0 100644 --- a/pages/anchor.py +++ b/pages/anchor.py @@ -69,21 +69,19 @@ class Anchor(Page): self.wind_angle = -1 # Menüsteuerung für Konfiguration - self._mnu = { - # Lfd. Name, curr unit steps type: numeric, on/off - # TODO Das sollte eine Klasse werden! - 'chain': [1, "Chain out", 0, "m", (1, 5, 10), (0, 200)], - 'chainmax': [2, "Chain max", 0, "m", (1, 5, 10), (0, 200)], - 'zoom': [3, "Zoom", 50, "", (1,), (1, 8)], - 'range': [4, "Alarm range", 35, "m", (1, 5, 10), (0, 200)] - } - self._mnu_title = "Options" - self._mnu_x = 20 - self._mnu_y = 80 - self._mnu_w = 120 - self._mnu_h = 20 - self._mnu_sel = 1 - self.mnu_step = 1 + self._menu = Menu("Options", 20, 80) + self._menu.setItemDimension(120, 20) + newitem = self._menu.addItem("chain", "Chain out", "int", 0, "m") + newitem.setRange(0, 200, (1, 5, 10)) + newitem = self._menu.addItem("chainmax", "Chain max", "int", self.chain_length, "m") + newitem.setRange(0, 200, (1, 5, 10)) + newitem = self._menu.addItem("zoom", "Zoom", "int", 2) + newitem.setRange(1, 8, (1,)) + newitem = self._menu.addItem("range", "Alarm range", "int", 40, "m") + newitem.setRange(1, 200, (1, 5, 10)) + self._menu.setItemActive("chain") + + self._test = 0 def handle_key(self, buttonid): if buttonid == 1: @@ -91,8 +89,10 @@ class Anchor(Page): self.mode = 'C' self.buttonlabel[2] = '#UP' self.buttonlabel[3] = '#DOWN' - self.buttonlabel[4] = '[ - ]' - self.buttonlabel[5] = '[ + ]' + itm = self._menu.getActiveItem() + stepwidth = itm.steps[itm.step] + self.buttonlabel[4] = f"-{stepwidth}" + self.buttonlabel[5] = f"+{stepwidth}" self.buttonlabel[6] = 'STEP' else: self.mode = 'N' @@ -134,16 +134,35 @@ class Anchor(Page): return True else: # Konfiguration + itm = self._menu.getActiveItem() if buttonid == 2: - if self._mnu_sel == 1: - self._mnu_sel = len(self._mnu) + if self._menu.activeitem == 0: + self._menu.activeitem = self._menu.getItemCount() - 1 else: - self._mnu_sel -= 1 + self._menu.activeitem -= 1 elif buttonid == 3: - if self._mnu_sel == len(self._mnu): - self._mnu_sel = 1 + if self._menu.activeitem == self._menu.getItemCount() - 1: + self._menu.activeitem = 0 else: - self._mnu_sel += 1 + self._menu.activeitem += 1 + elif buttonid == 4: + # decrease value by step + stepwidth = itm.steps[itm.step] + itm.setValue(itm.value - stepwidth) + elif buttonid == 5: + # increase value by step + stepwidth = itm.steps[itm.step] + itm.setValue(itm.value + stepwidth) + elif buttonid == 6: + ns = len(itm.steps) + if ns > 1: + if itm.step < ns - 1: + itm.step += 1 + else: + itm.step = 0 + stepwidth = itm.steps[itm.step] + self.buttonlabel[4] = f"-{stepwidth}" + self.buttonlabel[5] = f"+{stepwidth}" return True return False @@ -260,22 +279,31 @@ class Anchor(Page): ctx.move_to(2, 50) ctx.show_text("Anchor configuration") - # Menüwerte initialisieren - self._mnu["chain"][2] = self.chain - self._mnu["chainmax"][2] = self.chain_length - # Menü zeichnen ctx.save() ctx.set_font_size(16) - #ctx.rectangle(100, 100, 50, 50) - #ctx.clip() - for m in self._mnu.items(): - #ctx.move_to(self._mnu_x, self._mnu_y + 24 + m[1][0] * 16) - #ctx.show_text(m[1][1]) - inverted = (m[1][0] == self._mnu_sel) - self.draw_text_boxed(ctx, self._mnu_x, self._mnu_y + self._mnu_h * (m[1][0] - 1), self._mnu_w, self._mnu_h, m[1][1], inverted) - ctx.move_to(self._mnu_x + self._mnu_w + 20 , self._mnu_y + self._mnu_h * (m[1][0] - 1)) - ctx.show_text(str(m[1][2]) + m[1][3]) + x, y, w, h = self._menu.getRect() + ctx.set_line_width(1) + x += 0.5 # Cairo-Fix for single pixel line + y += 0.5 + ctx.save() + ctx.rectangle(x, y, w, h) + ctx.clip_preserve() + ctx.stroke() + for m in self._menu: + ix, iy, iw, ih = self._menu.getItemRect(m.position) + inverted = (m.position == self._menu.activeitem) + self.draw_text_boxed(ctx, ix, iy, iw, ih, m.label, inverted, False) + ctx.stroke() + # Werte neben dem Menü + ctx.restore() + ctx.rectangle(0, 20, 400, 360) # new clipping + ctx.clip() + self._test += 1 + for m in self._menu: + ix, iy, iw, ih = self._menu.getItemRect(m.position) + ctx.move_to(ix + iw + 20, iy + ih - 4) # 5 für Unterlängen + ctx.show_text(f"{m.value} {m.unit}") ctx.stroke() ctx.restore() diff --git a/pages/page.py b/pages/page.py index e7ae495..427aa8f 100644 --- a/pages/page.py +++ b/pages/page.py @@ -279,17 +279,18 @@ class Page(): ctx.show_text(content) ctx.stroke() - def draw_text_boxed(self, ctx, x, y, w, h, content, inverted=False): + def draw_text_boxed(self, ctx, x, y, w, h, content, inverted=False, border=False): ctx.set_line_width(1) + # Background fill + ctx.set_source_rgb(*self.fgcolor) if inverted: - ctx.set_source_rgb(*self.fgcolor) ctx.rectangle(x, y + 0.5, w, h) ctx.fill() else: - ctx.set_source_rgb(*self.bgcolor) - ctx.rectangle(x, y + 0.5, w, h) - ctx.stroke() - + if border: + ctx.rectangle(x + 0.5, y + 0.5, w, h) + ctx.stroke() + # Text if inverted: ctx.set_source_rgb(*self.bgcolor) else: