""" Basisklasse für alle Darstellungsseiten Hinweise zu Cairo: Das Koordinatensystem geht von (0, 0) links oben bis (400, 300) rechts unten. Um exakte Pixel zu treffen müssen Koordinaten mit Offset 0.5 verwendet werden. """ import os import cairo import math from datetime import datetime class Page(): pageno = 1 # Nummer der aktuell sichtbaren Seite backlight = False color_normal = "dcdcdc" # Standardhintergrund color_lighted = "d89090" # Hintergrund im Nachtmodus bgcolor = (0.86, 0.86, 0.86) fgcolor = (0, 0, 0) @staticmethod def hexcolor(hexstr): if (len(hexstr) != 6) or (not all(c.lower in '0123456789abcdef' for c in hexstr)): raise ValueError('Not a valid RGB Hexstring') else: return(int(hexstr[0:2], 16) / 255.0, int(hexstr[2:4], 16) / 255.0, int(hexstr[4:6], 16) / 255.0) @staticmethod def rotate (origin, points, angle): # operates on tuples, angle in degrees ox, oy = origin phi = math.radians(angle) fs = math.sin(phi) fc = math.cos(phi) rotated = [] for x, y in points: dx = x - ox dy = y - oy rotated.append((ox + fc * dx - fs * dy, oy + fs * dx + fc * dy)) return rotated def __init__(self, pageno, cfg, boatdata): self.pageno = pageno self.cfg = cfg self.bd = boatdata self.header = True self.footer = True self.hbled = False # Heartbeat LED self.hbfreq = 1000 # Heartbeat Frequenz in ms self.keylock = False self.icon = {} self.icon['PREV'] = cairo.ImageSurface.create_from_png(os.path.join(cfg['imgpath'], "arrow_l1.png")) self.icon['NEXT'] = cairo.ImageSurface.create_from_png(os.path.join(cfg['imgpath'], "arrow_r1.png")) self.icon['ILUM'] = cairo.ImageSurface.create_from_png(os.path.join(cfg['imgpath'], "lighton.png")) self.sym_lock = cairo.ImageSurface.create_from_png(os.path.join(cfg['imgpath'], "lock.png")) self.sym_swipe = cairo.ImageSurface.create_from_png(os.path.join(cfg['imgpath'], "swipe.png")) self.buttonlabel = { 1: '', 2: '', 3: '#PREV', 4: '#NEXT', 5: '', 6: '#ILUM' } def handle_key(self, buttonid): """ Diese Methode sollte in der Detailseite überladen werden """ print(f"Button no. {buttonid} ignored") return False def heartbeat(self, ctx): """ Wie ausschalten bei Seitenwechsel? """ ctx.save() if self.hbled: ctx.set_source_rgb(0, 0, 0) else: ctx.set_source_rgb(0.86, 0.86, 0.86) # 0xdcdcdc ctx.arc(210, 9, 6, 0, math.pi*2) ctx.fill() ctx.restore() self.hbled = not self.hbled def draw_header(self, ctx): """ Mögliche Zeichen für aktivierte Funktionen AP - Accesspoint ist aktiv WIFI - WIFI-Client TCP N2K - NMEA2000 183 USB GPS - GPS Fix vorhanden # TODO Umstellung auf Symbole je 16 Pixel zum Platz sparen Neu: Nummer der aktiven Seite (1 - 10) """ ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD) ctx.set_font_size(16) ctx.move_to(0.5, 14.5) ctx.show_text(f"N2K GPS") ctx.stroke() # Seitennummer neue Darstellung ctx.set_line_width(1) ctx.move_to(170.5, 1.5) ctx.line_to(190.5, 1.5) ctx.line_to(190.5, 16.5) ctx.line_to(170.5, 16.5) ctx.line_to(170.5, 1.5) ctx.stroke() self.draw_text_center(ctx, 180, 9.5, str(self.pageno)) # Tastenstatus ctx.save() if self.keylock: ctx.set_source_surface(self.sym_lock, 150, 1) else: ctx.set_source_surface(self.sym_swipe, 150, 1) ctx.paint() ctx.restore() # Heartbeat self.heartbeat(ctx) # Datum und Uhrzeit ctx.move_to(230, 14.5) ctx.show_text(datetime.today().strftime('%H:%M %Y-%m-%d LOT')) ctx.stroke() def draw_footer(self, ctx): """ Nur Belegung der Buttons (label[1] bis label[6]) """ ctx.select_font_face("AtariST8x16SystemFont") #ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD) ctx.set_font_size(16) x = (35, 101, 167, 233, 299, 365) y = 294 for i in range(6): if len(self.buttonlabel[i+1]) > 0 : if self.buttonlabel[i+1][0] == "#": # Symbol verwenden 16x16 Pixel ctx.save() key = self.buttonlabel[i+1][1:] ctx.set_source_surface(self.icon[key], x[i]-8, y-13) ctx.paint() ctx.restore() else: text = "[ {} ]".format(self.buttonlabel[i+1]) w = ctx.text_extents(text).width ctx.move_to(x[i] - w/2, y) ctx.show_text(text) ctx.stroke() def clear(self): ctx.set_source_rgb(1, 1, 1) ctx.rectangle(0, 0, 399, 299) ctx.fill() ctx.set_source_rgb(0, 0, 0) def draw_text_center(self, ctx, x, y, content, rotate=False, baseline=False, fill=False, fix1=False): """ Korrektur für DSEG7: Die Breite der 1 ist gleich 0.289 * Breite der anderen Ziffern Da der Leerraum bei der Ausgabe mit berücksichtigt wird, muß die tatsächliche Ausgabeposition links von der Mitte sein um (1 - 0.289) * Breite (=0.711) Zusätzlich muß der Abstand zwischen der 1 und dem nachfolgenden Zeichen berücksichtigt werden """ if fix1 and content[0] == '1': print("Fix1") ext1 = ctx.text_extents('1') w1 = 0.289 * ext1.width dx = ext1.width - w1 ext = ctx.text_extents(content[1:]) else: ext = ctx.text_extents(content) if fill: ctx.set_source_rgb(*self.bgcolor) xf = x + ext.x_bearing - 2 yf = y + ext.height / 2 + ext.y_bearing - 2 wf = ext.width + 4 if fix1: wf += w1 hf = ext.height + 4 ctx.rectangle(xf, yf, wf, hf) ctx.fill() ctx.set_source_rgb(*self.fgcolor) if rotate: w = ext[2] if fix1 and content[0] == '1': w += w1 x = x - dx if baseline: ctx.move_to(x - w / 2.0, y) else: ctx.move_to(x - w / 2.0, y + ext[2] / 2.0) ctx.save() ctx.rotate(1.5 * math.pi) ctx.show_text(content) ctx.restore() else: w = ext.width if fix1 and content[0] == '1': w += w1 x = x - dx if baseline: ctx.move_to(x - w / 2.0, y) else: ctx.move_to(x - w / 2.0, y + ext[3] / 2.0) ctx.show_text(content) ctx.stroke() def draw_text_ralign(self, ctx, x, y, content, fix1=False): if fix1 and content[0] == '1': w1 = ctx.text_extents('1')[2] * 0.289 ext = ctx.text_extents(content[1:]) w = ext[2] + w1 x = x - dx else: ext = ctx.text_extents(content) w = ext[2] ctx.move_to(x - w, y) ctx.show_text(content) ctx.stroke()