""" Ankerinfo / -alarm Daten - Position des Ankers - Wassertiefe beim Ankern - Gesteckte Kettenlänge - aktuelle Position des Schiffs - aktuelle Wassertiefe an Schiffsposition - aktuelle Schiffsausrichtung bearing/heading - aktuelle Windrichtung (wahr) - aktuelle Windstärke (wahr) - Alarm aktiv J/N - Alarmradius - GPS Abweichung/ Fehler in der Horizontalen - Zeitpunkt des Ankeraus Darstellung: - Modi: Normal / Konfiguration - Anker oben / unten - Nord ist oben Es gibt verschiedene Unterseiten - Normalansicht - Konfigurationsseite """ import os import cairo import math import time from cfgmenu import Menu from .page import Page class Anchor(Page): def __init__(self, pageno, cfg, boatdata): super().__init__(pageno, cfg, boatdata) self.sym_anchor = cairo.ImageSurface.create_from_png(os.path.join(cfg['imgpath'], "anchor.png")) self.buttonlabel[1] = 'MODE' self.buttonlabel[2] = 'DROP' self.buttonlabel[5] = '' # ALARM erst möglich wenn der Anker unten ist self.mode = 'N' # (N)ormal, (C)onfiguration self.scale = 50 # Radius of display circle in meter self._bd = boatdata # Der sinnvolle Abstand ist abhängig von der Länge der gesteckten Kette # Die initial eingegebene Position des Ankers sollte nactträglich justiert # werden können self.chain_length = 60 # maximale Länge die ausgesteckt werden kann self.chain = 35 # aktuell gesteckte Länge self.anchor_set = False self.anchor_lat = 0 self.anchor_lon = 0 self.anchor_depth = -1 self.anchor_ts = None # Timestamp of dropped anchor self.lat = 0 self.lon = 0 self.heading = -1 self.depth = -1 self.alarm_range = 20 self.alarm_enabled = False self.alarm = False # Alarm ist ausgelöst und aktiv self.wind_angle = -1 # Menüsteuerung für Konfiguration 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: if self.mode == 'N': self.mode = 'C' self.buttonlabel[2] = '#UP' self.buttonlabel[3] = '#DOWN' 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' self.buttonlabel[2] = 'RISE' if self.anchor_set else 'DROP' self.buttonlabel[3] = '#PREV' self.buttonlabel[4] = '#NEXT' self.buttonlabel[5] = 'ALARM' if not self.alarm_enabled else 'OFF' return True if self.mode == 'N': # Normal if buttonid == 2: if not self.anchor_set: self.anchor_lat = self._bd.lat self.anchor_lon = self._bd.lon self.anchor_set = True self.anchor_ts = time.time() self.buttonlabel[2] = 'RISE' self.buttonlabel[5] = 'ALARM' else: self.anchor_set = False self.alarm = False self.alarm_enabled = False self.anchor_ts = None self.buttonlabel[2] = 'DROP' self.buttonlabel[5] = '' return True elif buttonid == 5: # Bei aktivem Alarm kann mit dieser Taste der Alarm zurückgesetzt # werden. Die Tastenbeschriftung wechselt zwischen ALARM und OFF. if self.alarm: self.alarm = False self.buttonlabel[5] = 'ALARM' if self.alarm_enabled: self.alarm_enabled = False self.buttonlabel[5] = 'ALARM' else: self.alarm_enabled = True self.buttonlabel[5] = 'OFF' return True else: # Konfiguration itm = self._menu.getActiveItem() if buttonid == 2: if self._menu.activeitem == 0: self._menu.activeitem = self._menu.getItemCount() - 1 else: self._menu.activeitem -= 1 elif buttonid == 3: if self._menu.activeitem == self._menu.getItemCount() - 1: self._menu.activeitem = 0 else: 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 def draw_normal(self, ctx): """ value1 = LAT value2 = LON value3 = HDOP value4 = DBS value5 = TWD value6 = TWS """ # self.anchor_lat = # Name ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD) ctx.set_font_size(20) ctx.move_to(2, 50) ctx.show_text("Anchor") ctx.move_to(2, 200) ctx.show_text("Depth") ctx.move_to(320, 50) ctx.show_text("Chain") ctx.set_font_size(16) ctx.move_to(2, 70) ctx.show_text("Alarm: ") if self.alarm_enabled: ctx.show_text("On") else: ctx.show_text("Off") ctx.move_to(320, 70) ctx.show_text(f"{self.chain} m") ctx.move_to(10, 220) if self._bd.dbs.valid: ctx.show_text(self._bd.dbs.format()) ctx.stroke() # Spezialseite cx = 200 cy = 150 r = 125 # Skala ctx.set_line_width(1) ctx.arc(cx, cy, r, 0, 2*math.pi) ctx.move_to(cx + 10, cy + 0.5) ctx.line_to(cx + r - 4, cy + 0.5) ctx.move_to(cx + r / 2, cy + 20) # Pfeil links ctx.move_to(cx + 10, cy + 0.5) ctx.line_to(cx + 16, cy - 4 + 0.5) ctx.move_to(cx + 10, cy + 0.5) ctx.line_to(cx + 16, cy + 4 + 0.5) # Pfeil rechts ctx.move_to(cx + r - 4, cy + 0.5) ctx.line_to(cx + r - 10, cy - 4 + 0.5) ctx.move_to(cx + r - 4, cy + 0.5) ctx.line_to(cx + r - 10, cy + 4 + 0.5) ctx.set_font_size(16) self.draw_text_center(ctx, cx + r / 2, cy + 8, str(self.scale) + " m") ctx.stroke() ctx.set_line_width(1.5) # Ankersymbol falls Anker fallen gelassen wurde # Ansonsten ist das Boot-Symbol im Mittelpunkt if self.anchor_set: ctx.save() ctx.set_source_surface(self.sym_anchor, cx-8, cy-8) ctx.paint() ctx.restore() # Boot zeichnen # Heading beachten # Wir arbeiten mit einer Kopie, weil bx/by im Laufe der Zeit von # cx/cy abweichen werden if self.anchor_set: bx = cx by = cy + 30 else: bx = cx by = cy + 8 p = ((bx - 5, by), (bx - 5, by - 10), (bx, by - 16), (bx + 5, by - 10), (bx + 5, by), (bx - 6, by)) if self._bd.hdt.value: # Rotiere Boot-Symbol um eigenes Zentrum boat = self.rotate((bx, by - 8), p, self._bd.hdt.value) else: # Kein Heading, Boot immer zeichnen boat = p ctx.move_to(*boat[0]) for point in boat[1:]: ctx.line_to(*point) ctx.stroke() # Windpfeil zeichnen if self._bd.awa.value: p = ((cx, cy - r + 25), (cx - 12, cy - r - 4), (cx, cy - r + 6), (cx + 12, cy - r - 4), (cx, cy - r + 25)) wind = self.rotate((cx, cy), p, self._bd.awa.value) ctx.move_to(*wind[0]) for point in wind[1:]: ctx.line_to(*point) ctx.fill() def draw_config(self, ctx): ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD) ctx.set_font_size(20) ctx.move_to(2, 50) ctx.show_text("Anchor configuration") # Menü zeichnen ctx.save() ctx.set_font_size(16) 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() def draw(self, ctx): if self.mode == 'N': self.draw_normal(ctx) else: self.draw_config(ctx)