Erstveröffentlichung Weihnachten 2024

This commit is contained in:
2024-12-24 09:36:04 +01:00
commit 71b5f84a6f
81 changed files with 5728 additions and 0 deletions

36
pages/__init__.py Normal file
View File

@@ -0,0 +1,36 @@
# Displayseiten
from .system import System
# Generische Seiten
from .onevalue import OneValue
from .twovalues import TwoValues
from .threevalues import ThreeValues
from .fourvalues import FourValues
from .fourvalues2 import FourValues2
# Graphen
from .onegraph import OneGraph
from .twographs import TwoGraphs
from .exhaust import Exhaust
# Analoginstrumente
from .clock import Clock
from .fluid import Fluid
# Spezialseiten
from .anchor import Anchor
from .apparentwind import ApparentWind
from .autobahn import Autobahn
from .barograph import Barograph
from .battery import Battery
from .battery2 import Battery2
from .bme280 import BME280
from .dst810 import DST810
from .keel import Keel
from .rollpitch import RollPitch
from .skyview import SkyView
from .solar import Solar
from .rudder import Rudder
from .voltage import Voltage
from .windrose import WindRose

52
pages/anchor.py Normal file
View File

@@ -0,0 +1,52 @@
"""
Ankerinfo / -alarm
"""
import os
import cairo
import math
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] = 'DEC'
self.buttonlabel[2] = 'INC'
self.buttonlabel[5] = 'SET'
def draw(self, ctx):
# 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(320, 50)
ctx.show_text("Chain")
ctx.set_font_size(16)
ctx.move_to(2, 70)
ctx.show_text("Alarm: off")
ctx.move_to(320, 70)
ctx.show_text("45 m")
ctx.stroke()
# Spezialseite
cx = 200
cy = 150
r = 125
ctx.set_line_width(1.5)
ctx.arc(cx, cy, r, 0, 2*math.pi)
ctx.stroke()
ctx.save()
ctx.set_source_surface(self.sym_anchor, cx-8, cy-8)
ctx.paint()
ctx.restore()

16
pages/apparentwind.py Normal file
View File

@@ -0,0 +1,16 @@
import cairo
import math
from .page import Page
class ApparentWind(Page):
def __init__(self, pageno, cfg, boatdata):
super().__init__(pageno, cfg, boatdata)
def draw(self, ctx):
# Name
ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD)
ctx.set_font_size(60)
ctx.move_to(20, 100)
ctx.show_text("Apparent Wind")

102
pages/autobahn.py Normal file
View File

@@ -0,0 +1,102 @@
"""
3D-View angelehnt an die NASA Clipper GPS-Darstellung
"""
import os
import cairo
import math
from .page import Page
class Autobahn(Page):
def __init__(self, pageno, cfg, boatdata):
super().__init__(pageno, cfg, boatdata)
self.xte = self.bd.getRef("XTE")
self.cog = self.bd.getRef("COG")
self.btw = self.bd.getRef("BTW")
self.dtw = self.bd.getRef("DTW")
self.wpname = "no data"
self.symbol = cairo.ImageSurface.create_from_png(os.path.join(cfg['imgpath'], "ship.png"))
def draw(self, ctx):
# Beschriftung unter den Werten
ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD)
ctx.set_font_size(16)
ctx.move_to(50, 188);
ctx.show_text("Cross-track error")
ctx.move_to(270, 188)
ctx.show_text("Track")
ctx.move_to(45, 275);
ctx.show_text("Distance to waypoint")
ctx.move_to(260, 275);
ctx.show_text("Bearing")
ctx.stroke()
# Meßwerte
ctx.select_font_face("DSEG7 Classic")
ctx.set_font_size(60)
ctx.move_to(40, 170)
#ctx.show_text(self.xte.format())
ctx.show_text("2.3")
ctx.move_to(220, 170)
#ctx.show_text(self.cog.format())
ctx.show_text("253")
ctx.move_to(40, 257)
#ctx.show_text(self.dtw.format())
ctx.show_text("5.8")
ctx.move_to(220, 257)
#ctx.show_text(self.btw.format())
ctx.show_text("248")
# 3D-Ansicht oben
# TODO Schiffssymbol
ctx.save()
ctx.set_source_surface(self.symbol, 186, 68)
ctx.paint()
ctx.restore()
# Segmente: 2 1 0 3 4 5
seg = [True] * 6
points = {
2: ((0, 54), (46, 24), (75, 24), (0, 90)),
1: ((0, 100), (82, 24), (112, 24), (50, 100)),
0: ((60, 100), (117, 24), (147, 24), (110, 100)),
3: ((340, 100), (283, 24), (253, 24), (290, 100)),
4: ((399, 100), (318, 24), (289, 24), (350, 100)),
5: ((399, 54), (354, 24), (325, 24), (399, 90))
}
# Winkeldifferenz
diff = (self.cog.value or 0) - (self.btw.value or 0)
if diff < -180:
diff += 360
elif diff > 180:
diff -= 360
if diff > 0:
order = (3, 4, 5, 0, 1, 2)
else:
order = (0, 1, 2, 3, 4, 5)
# Anzahl aktiver Segmente
seg_step = math.radians(3)
nseg = min(abs(diff) / seg_step, 5)
i = 0
while nseg > 0:
seg[order[i]] = False
i += 1
nseg -= 1
# Segmente zeichnen
for p in range(6):
ctx.move_to(*points[p][0])
ctx.line_to(*points[p][1])
ctx.line_to(*points[p][2])
ctx.line_to(*points[p][3])
if seg[p]:
ctx.fill()
else:
ctx.stroke()

366
pages/barograph.py Normal file
View File

@@ -0,0 +1,366 @@
"""
Siehe auch: Steamrock Digital Barometer
Meßwert alls 15 Minuten:
Es wird in hPa gemessen mit einer Nachkommastelle
84 Stunden * 4 Werte je Stunde = 336 Meßwerte
Tendenzwert über 3 Stunden
Je Zoomstufe wird ein eigener Buffer vorgehalten um ein sauberes
Diagramm zu erhalten. Überall gilt: Je Pixel ein Meßwert.
Drucktendenz:
- 1 hour tendency
- 3 hour tendency
Verschiedene Datenquellen auswählbar:
- intern (BME280, BMP280)
- N2K generisch
- Historie von
- Yacht devices
- Capteurs?
Das Diagramm wird mit Ursprung rechts unten (x0, y0) gezeichnet,
da die Werte in der Vergangenhait liegen, also links vom Ursprung.
Damit eine saubere Skala auf der Y-Achse erreicht wird, gibt einige
feste Skalierungen.
Standard: 20hPa von unten nach oben, z.B. 1015, 1020, 1025, 1030, 1035
"""
import time
import cairo
from .page import Page
class Barograph(Page):
def __init__(self, pageno, cfg, boatdata):
super().__init__(pageno, cfg, boatdata)
# Meßwert alle 15 Minuten:
# 84 Stunden * 4 Werte je Stunde = 336 Meßwerte
self.bd = boatdata
self.source = 'I' # (I)ntern, e(X)tern
self.zoom = (1, 2, 3, 6, 12)
self.zoomindex = 4
self.series = (75, 150, 300, 600, 900)
# Y-Axis
self.vmin = 0
self.vmax = 0
self.scalemin = 1000
self.scalemax = 1020
self.scalestep = 5
# Tendenzwert über 3 Stunden
self.hist3 = None
self.hist1 = None
self.buttonlabel[1] = '+'
self.buttonlabel[2] = '-'
self.buttonlabel[5] = 'SRC'
self.refresh = time.time() - 30
def handle_key(self, buttonid):
# TODO Serie auswählen aufgrund Zoomlevel
if buttonid == 1:
# Zoom in
if self.zoomindex > 0:
self.zoomindex -= 1
self.refresh = time.time() - 30
elif buttonid == 2:
# Zoom out
if self.zoomindex < len(self.zoom) - 1:
self.zoomindex += 1
self.refresh = time.time() - 30
if buttonid == 5:
# Source
if self.source == 'I':
self.source = 'X'
else:
self.source = 'I'
# Testausgabe der Datenerfassung
data = []
vmin = data[0]
vmax = data[0]
i = self.series[self.zoomindex]
for value in self.bd.history['press'].series[i].get():
v = value / 10
data.append(v)
if v < vmin and v != 0:
vmin = v
elif v > vmax and v != 0:
vmax = v
print(f"Werte: vmin={vmin}, vmax={vmax}")
ymin, ymax, step = self.getYScale(vmin, vmax)
print(f"Skala: ymin={ymin}, ymax={ymax}, step={step}")
print(f"zoomindex={self.zoomindex}, series={self.series[self.zoomindex]}")
hist1a = self.bd.history['press'].series[i].getvalue(3600)
hist1b = self.bd.history['press'].series[i].getvalue3(3600)
trend1 = data[0] - hist1b
print(f"{hist1a} / {hist1b} -> Trend1: {trend1:.1f}")
def loadData(self):
"""
Transfer data from history to page buffer
set y-axis according to data
"""
self.data = []
self.vmin = 9999
self.vmax = 0
i = self.series[self.zoomindex]
for value in self.bd.history['press'].series[i].get():
v = value / 10
self.data.append(v)
if v < self.vmin and v != 0:
self.vmin = v
elif v > self.vmax and v != 0:
self.vmax = v
self.scalemin, self.scalemax, self.scalestep = self.getYScale(self.vmin, self.vmax)
return True
def drawTrend(self, ctx, code, x, y, w):
"""
One hour Trend
0: Stationary <= 1 hPa
1: Rising >1 and <= 2 hPa
2: Rising fast >2 and <= 3 hPa
3: Rising very fast >3 hPa
-1: Falling
-2: Falling fast
-3: Falling very fast
"""
trend1map = {
-3: "Falling_Very_Fast.png", # > 3 hPa
-2: "Falling_Fast.png", # > 2 and <= 3 hPa
-1: "Falling.png", # > 1 and <= 2 hPa
0: "Stationary.png", # <= +/- 1 hPa
1: "Rising.png", # < -1 and >= -2 hPa
2: "Rising_Fast.png", # < -2 and >= -3 hPa
3: "Rising_Very_Fast.png" # < -3 hPa
}
if code == 0:
# Pfeil horizontal rechts
ctx.move_to(x, y - w / 2)
ctx.line_to(x + w, y - w / 2)
ctx.draw()
# Position merken
ctx.line_to(x - w / 4, y - w)
ctx.line_to(x - w / 4, y)
ctx.line_to(x + w, y - w / 2)
ctx.fill()
elif code == 1:
# Pfeil schräg nach oben
pass
elif code == 2:
# Pfeil gerade nach oben
pass
elif code == 3:
# Doppelpfeil nach oben
pass
elif code == -1:
# Pfeil schräg nach unten
pass
elif code == -2:
# Pfeil gerade nach unten
pass
elif code == -3:
# Doppelpfeil nach unten
pass
def drawWMOCode(self, ctx, code, x, y, w):
"""
Three hour code
Code 0 to 8:
0: Increasing, then decreasing; athmospheric pressure the same
as or higher than three hours ago
1: Increasing then steady; or increasing, then increasing more
slowly; athmospheric pressure now higher than three hours ago
2: Increasing (steadily or unsteadily); athmospheric pressure
now higher than three hours ago
3: Decreasing or steady, then increasing; or increasing then
increasing more rapidly; athmospheric pressure now higher
than three hours ago
4: Steady; athmospheric pressure is the same as three hours ago
5: Decreasing, then increasing; athmospheric pressure now is the
same as or lower than three hours ago
6:
7:
8:
"""
pass
def getYScale(self, vmin, vmax):
# Y-Achse aufgrund Meßwerten einstellen
diff = vmax - vmin
if diff < 20:
step = 5
elif diff <= 40:
step = 10
else:
step = 15
vmin = int(vmin - (vmin % step)) # Nächstes Vielfaches nach oben
vmax = int(vmax + step - (vmax % step)) # Nächstes Vielfaches nach unten
return (vmin, vmax, step)
def draw(self, ctx):
"""
Darstellung angelehnt an klassisches Gerät
Daten werden im nichtflüchtigen Speicher gehalten
Da sich die Daten langsam verändern, reicht es, diese z.B. nur alle
30 Sekunden oder langsamer zu laden.
Der aktuelle Wert oben ist natürlich nicht alt.
Datenreihen
- 1 Woche, stündlich: 7 * 24 = 168 Meßwerte
- 1 Tag, alle 10 min: 24 * 6 = 144 Meßwerte
Der Druck wird in zwei Bytes abgespeichert. Es wird eine Nachkommastelle
verwendet. Um ohne Fließkommazahlen auszukommen wird der Maßwert einfach
mit 10 multipliziert.
Darstellung wie Steamrock:
1 Pixel entspricht einem Meßwert alle 15min.
1 Tag hat dementsprechend eine Breite von 48px
"""
timestamp = time.time()
if timestamp - self.refresh >= 30:
self.refresh = timestamp
self.loadData()
ctx.set_source_rgb(0, 0, 0)
ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD)
# Datenquelle rechts oben
ctx.set_font_size(16)
ctx.move_to(330, 50)
if self.source == 'I':
ctx.show_text("BMP280")
else:
ctx.show_text("N2K Bus")
ctx.stroke()
# Zoomstufe
datastep = self.series[self.zoomindex]
if datastep > 120:
if datastep % 60 == 0:
fmt = "{:.0f} min"
else:
fmt = "{:.1f} min"
datastep /= 60
else:
fmt = '{} s'
self.draw_text_center(ctx, 360, 62, fmt.format(datastep))
# Aktueller Luftdruck hPa
ctx.set_font_size(32)
self.draw_text_center(ctx, 200, 40, self.bd.pressure.format())
#self.draw_text_center(ctx, 200, 40, "1019.2")
ctx.set_font_size(16)
self.draw_text_center(ctx, 200, 62, "hPa")
# Trend
ctx.set_font_size(16)
# TODO Trend linie
#trend =
self.draw_text_center(ctx, 295, 62, "0.0")
# min/max
ctx.move_to(10, 38)
ctx.show_text(f"min: {self.vmin}")
ctx.move_to(10, 50)
ctx.show_text(f"max: {self.vmax}")
# Alarm
self.draw_text_center(ctx, 70, 62, "Alarm Off")
# Hintergrundrahmen
ctx.set_line_width(2)
ctx.move_to(0, 75)
ctx.line_to(400, 75)
ctx.move_to(130, 20)
ctx.line_to(130, 75)
ctx.move_to(270, 20)
ctx.line_to(270, 75)
ctx.move_to(325, 20)
ctx.line_to(325, 75)
ctx.stroke()
# Diagramm
# --------
ymin = self.scalemin
ymax = self.scalemax
yn = self.scalestep
ystep = (ymax - ymin) / yn
xstep = 48
# Ursprung ist rechts unten
x0 = 350
y0 = 270
w = 7 * 48
h = 180
ctx.set_line_width(1)
ctx.rectangle(x0 - w + 0.5, y0 - h + 0.5, w, h)
ctx.stroke()
# X-Achse sind Stunden
xn = 0
for xt in [x * -1 * self.zoom[self.zoomindex] for x in range(1,7)]:
xn += 1
ctx.move_to(x0 - xn * xstep + 0.5, y0)
ctx.line_to(x0 - xn * xstep + 0.5, y0 - h)
ctx.stroke()
self.draw_text_center(ctx, x0 - xn * xstep + 0.5, y0 - 8, str(xt), fill=True)
ctx.stroke()
#for x in (1, 2, 3, 4, 5, 6):
# ctx.move_to(x0 - x * 48 + 0.5, y0 + 0.5)
# ctx.line_to(x0 - x * 48 + 0.5, y0 - h + 0.5)
#ctx.stroke()
# Y-Achse
ctx.move_to(x0 + 5.5, y0 + 0.5)
ctx.line_to(x0 + 5.5, y0 - h)
ctx.move_to(x0 - w - 5.5, y0 + 0.5)
ctx.line_to(x0 - w - 5.5, y0 -h )
ctx.stroke()
dy = 9 # Pixel je hPa
ysmin = self.scalemin
ysmax = self.scalemax
y = y0 + 0.5
ystep = self.scalestep
ys = ysmin
while y >= y0 - h:
if ys % ystep == 0:
ctx.move_to(x0 + 10, y + 5.5)
ctx.show_text(str(ys))
ctx.move_to(x0 - w - 5, y)
ctx.line_to(x0 + 5, y)
else:
ctx.move_to(x0, y)
ctx.line_to(x0 + 5, y)
ctx.move_to(x0 - w - 5, y)
ctx.line_to(x0 - w, y)
y -= dy
ys += 1
ctx.stroke()
# Meßdaten
for v in self.data:
x0 -= 1
if v > 0:
ctx.rectangle(x0, y0 - (v - ysmin) * dy, 1.5, 1.5)
ctx.fill()

66
pages/battery.py Normal file
View File

@@ -0,0 +1,66 @@
"""
Batteriewerte eines INA219 oder INA226 Sensors
Ähnlich ThreeValue
"""
import cairo
from .page import Page
class Battery(Page):
avg = (1, 10, 60, 300);
def __init__(self, pageno, cfg, boatdata):
super().__init__(pageno, cfg, boatdata)
self.avgindex = 0
self.buttonlabel[1] = 'AVG'
def handle_key(self, buttonid):
if buttonid == 1:
if self.avgindex < len(self.avg) -1:
self.avgindex += 1
else:
self.avgindex = 0
return True
return False
def draw(self, ctx):
# Aufteilung in 3 Bereiche durch 2 Linien
ctx.rectangle(0, 105, 400, 3);
ctx.rectangle(0, 195, 400, 3);
ctx.fill()
# Name
ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD)
ctx.set_font_size(40)
ctx.move_to(20, 55)
ctx.show_text("VBat")
ctx.move_to(20, 145)
ctx.show_text("IBat")
ctx.move_to(20, 235)
ctx.show_text("PBat")
ctx.stroke()
# Einheit
ctx.set_font_size(24)
ctx.move_to(20, 90)
ctx.show_text("V")
ctx.move_to(20, 180)
ctx.show_text("A")
ctx.move_to(20, 270)
ctx.show_text("W")
# Werte
ctx.select_font_face("DSEG7 Classic")
ctx.set_font_size(60)
ctx.move_to(180, 90)
ctx.show_text("12.3")
ctx.move_to(180, 180)
ctx.show_text("3.2")
ctx.move_to(180, 270)
ctx.show_text("39.4")

90
pages/battery2.py Normal file
View File

@@ -0,0 +1,90 @@
"""
Komplexe Batterieübersichtsseite
"""
import cairo
from .page import Page
class Battery2(Page):
def draw_battery(self, ctx, x, y, w, h, level):
'''
Das Rechteck ist das komplett umschließende
Level ist der prozentuale Füllstand
'''
pass
'''
// Battery graphic with fill level
void batteryGraphic(uint x, uint y, float percent, int pcolor, int bcolor){
// Show battery
int xb = x; // X position
int yb = y; // Y position
int t = 4; // Line thickness
// Percent limits
if(percent < 0){
percent = 0;
}
if(percent > 99){
percent = 99;
}
// Battery corpus 100x80 with fill level
int level = int((100.0 - percent) * (80-(2*t)) / 100.0);
getdisplay().fillRect(xb, yb, 100, 80, pcolor);
if(percent < 99){
getdisplay().fillRect(xb+t, yb+t, 100-(2*t), level, bcolor);
}
// Plus pol 20x15
int xp = xb + 20;
int yp = yb - 15 + t;
getdisplay().fillRect(xp, yp, 20, 15, pcolor);
getdisplay().fillRect(xp+t, yp+t, 20-(2*t), 15-(2*t), bcolor);
// Minus pol 20x15
int xm = xb + 60;
int ym = yb -15 + t;
getdisplay().fillRect(xm, ym, 20, 15, pcolor);
getdisplay().fillRect(xm+t, ym+t, 20-(2*t), 15-(2*t), bcolor);
'''
def draw(self, ctx):
ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD)
ctx.set_font_size(40)
ctx.move_to(10, 65)
ctx.show_text("Bat.")
# Batterietyp
ctx.move_to(90, 65)
ctx.show_text("AGM")
# Kapazität
ctx.move_to(10, 200)
ctx.select_font_face("DSEG7 Classic")
ctx.set_font_size(40)
ctx.show_text("12")
ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD)
ctx.set_font_size(16)
ctx.show_text("Ah")
ctx.move_to(10, 235)
ctx.show_text("Installed")
ctx.move_to(10, 255)
ctx.show_text("Battery Type")
# Batteriegraphik
# Rechteck mit Füllstand 100x80, oben zwei Pole
ctx.rectangle(150, 100, 100, 80)
ctx.stroke()
# Füllstand
# Pole
'''
ctx.line_to(2.5, 1.5)
ctx.set_line_width(0.06)
'''

55
pages/bme280.py Normal file
View File

@@ -0,0 +1,55 @@
"""
Werte eines lokal angeschlossenen BME280/BMP280
"""
import cairo
from .page import Page
class BME280(Page):
def __init__(self, pageno, cfg, boatdata):
super().__init__(pageno, cfg, boatdata)
#self.ref1 = self.bd.getRef(boatvalue1)
#self.ref2 = self.bd.getRef(boatvalue2)
#self.ref3 = self.bd.getRef(boatvalue3)
def draw(self, ctx):
# Bildschirmunterteilung mit Linien
ctx.rectangle(0, 105, 399, 3)
ctx.rectangle(0, 195, 399, 3)
ctx.fill()
# Beschriftung
ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD)
ctx.set_font_size(40)
# Titel
ctx.move_to(20,55)
ctx.show_text("Temp")
ctx.move_to(20,145)
ctx.show_text("Humid")
ctx.move_to(20, 235)
ctx.show_text("Press")
# Einheit
ctx.set_font_size(24)
ctx.move_to(20, 90)
ctx.show_text("Deg C")
ctx.move_to(20, 180)
ctx.show_text("%")
ctx.move_to(20, 270)
ctx.show_text("hPa")
# Meßwerte
ctx.select_font_face("DSEG7 Classic")
ctx.set_font_size(60)
# Temperatur °C
ctx.move_to(180, 90)
ctx.show_text("{:.1f}".format(self.bd.temp_air))
# Feuchte %
ctx.move_to(180, 180)
ctx.show_text("{}".format(int(self.bd.humidity)))
# Luftdruck hPa
ctx.move_to(180, 270)
ctx.show_text("{}".format(int(self.bd.pressure)))

188
pages/clock.py Normal file
View File

@@ -0,0 +1,188 @@
"""
Uhr
TODO: Zeitzone anzeigen. Abhängig von Lat, Lon
Es sollen verschiedene Modi unterstützt werden
- Analoguhr
- Digitaluhr
- Regattauhr / -timer
"""
import cairo
import math
from .page import Page
from datetime import datetime
import astral
class Clock(Page):
def __init__(self, pageno, cfg, boatdata):
super().__init__(pageno, cfg, boatdata)
self.buttonlabel[1] = 'MODE'
self.buttonlabel[2] = 'TZ'
self.mode = ('A', 'D', 'T') # (A)nalog (D)igital (T)imer
self.modeindex = 1
self.utc = True
self.location = astral.Location(('Norderstedt', 'Germany', 53.710105, 10.0574378, 'UTC'))
self.location.astral = astral.Astral()
def handle_key(self, buttonid):
if buttonid == 1:
if self.modeindex < len(self.mode):
self.modeindex += 1
else:
self.modeindex = 0
return True
if buttonid == 2:
self.utc = not self.utc
return True
return False
def draw(self, ctx):
ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD)
# akuellen Modus anzeigen
if mode[modeindex] == 'A':
self.draw_analog(ctx)
if mode[modeindex] == 'D':
self.draw_digital(ctx)
else:
self.draw_timer(ctx)
def draw_digital(self, ctx):
ctx.set_font_size(24)
ctx.move_to(10, 220)
ctx.show_text("Digital clock")
def draw_timer(self, ctx):
ctx.set_font_size(24)
ctx.move_to(10, 220)
ctx.show_text("Timer")
def draw_analog(self, ctx):
ts = datetime.now()
sunrise = self.location.sunrise(ts)
sunset = self.location.sunset(ts)
#print(sunrise)
#print(sunset)
# Datum und Uhrzeit
# Sonnenaufgang
# Sonnenuntergang
# Wochentag
# ts.strftime('%a')
# Werte in den Ecken der Uhr
ctx.set_font_size(24)
ctx.move_to(10, 220)
ctx.show_text("Time")
ctx.move_to(10, 95)
ctx.show_text("Date")
ctx.move_to(335, 95)
ctx.show_text("SunR")
ctx.move_to(335, 220)
ctx.show_text("SunS")
ctx.stroke()
ctx.set_font_size(16)
ctx.move_to(10, 65)
ctx.show_text(ts.strftime("%d.%m.%Y"))
ctx.move_to(10, 250)
ctx.show_text(ts.strftime("%H:%M"))
ctx.move_to(335, 65)
ctx.show_text(sunrise.strftime("%H:%M"))
ctx.move_to(335, 250)
ctx.show_text(sunset.strftime("%H:%M"))
ctx.stroke()
# Horizontal separators
ctx.rectangle(0, 149, 60, 3)
ctx.rectangle(340, 149, 60, 3)
ctx.fill()
# Uhr
cx = 200
cy = 150
r = 110
ctx.arc(cx, cy, r + 10, 0, 2*math.pi)
ctx.arc(cx, cy, r + 7, 0, 2*math.pi)
ctx.stroke()
ctx.set_font_size(20)
self.draw_text_center(ctx, cx, cy-40, 'UTC' if self.utc else 'LOT')
ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD)
ctx.set_font_size(24)
for i in range(360):
x = cx + (r - 30) * math.sin(i/180*math.pi)
y = cy - (r - 30) * math.cos(i/180*math.pi)
char = ""
if i == 0:
char = "12"
elif i == 90:
char = "3"
elif i == 180:
char = "6"
elif i == 270:
char = "9"
if i % 90 == 0:
#ctx.move_to(x, y)
self.draw_text_center(ctx, x, y, char)
#ctx.stroke()
ctx.set_line_width(3.0)
if i % 6 == 0:
if i % 30 == 0:
x0 = cx + (r - 10) * math.sin(i/180*math.pi)
y0 = cy - (r - 10) * math.cos(i/180*math.pi)
x1 = cx + (r + 10) * math.sin(i/180*math.pi)
y1 = cy - (r + 10) * math.cos(i/180*math.pi)
ctx.move_to(x0, y0)
ctx.line_to(x1, y1)
ctx.stroke()
else:
x = cx + r * math.sin(i/180*math.pi)
y = cy - r * math.cos(i/180*math.pi)
ctx.arc(x, y, 2, 0, 2*math.pi)
ctx.fill()
# Stundenzeiger
p = ((cx - 2, cy - (r - 50)), (cx + 2, cy - (r - 50)), (cx + 6, cy + 16), (cx - 6, cy + 16))
angle_h = (ts.hour % 12 + ts.minute / 60) * 30
zeiger = self.rotate((cx, cy), p, angle_h)
ctx.move_to(*zeiger[0])
for point in zeiger[1:]:
ctx.line_to(*point)
ctx.fill()
# Minutenzeiger
p = ((cx - 1, cy - (r - 15)), (cx + 1, cy - (r - 15)), (cx + 6, cy + 20), (cx - 6, cy + 20))
angle_m = ts.minute * 6
zeiger = self.rotate((cx, cy), p, angle_m)
ctx.move_to(*zeiger[0])
for point in zeiger[1:]:
ctx.line_to(*point)
ctx.fill()
# Zentraler Kreis
ctx.set_source_rgb(0, 0, 0)
ctx.arc(cx, cy, 12, 0, 2*math.pi)
ctx.fill()
# Wozu dieses?
ctx.set_source_rgb(0.86, 0.86, 0.86)
ctx.arc(cx, cy, 10, 0, 2*math.pi)
ctx.fill()
ctx.set_source_rgb(0, 0, 0)
ctx.arc(cx, cy, 2, 0, 2*math.pi)
ctx.fill()

40
pages/dst810.py Normal file
View File

@@ -0,0 +1,40 @@
import cairo
from .page import Page
class DST810(Page):
# DBT, STW, Log, WTemp
def draw(self, ctx):
# Layout
ctx.rectangle(0, 105, 400, 3)
ctx.rectangle(0, 195, 400, 3)
ctx.rectangle(200, 195, 3, 75)
ctx.fill()
ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD)
ctx.set_font_size(40)
# titel
ctx.move_to(20, 55)
ctx.show_text("Depth")
ctx.move_to(20, 145)
ctx.show_text("Speed")
ctx.move_to(20, 220)
ctx.show_text("Log")
ctx.move_to(220, 220)
ctx.show_text("Temp")
# Einheiten
ctx.set_font_size(24)
ctx.move_to(20, 90)
ctx.show_text("m")
# Meßwerte
ctx.select_font_face("DSEG7 Classic")
ctx.set_font_size(60)
ctx.move_to(180, 90)
ctx.show_text("m")
ctx.set_font_size(40)

58
pages/exhaust.py Normal file
View File

@@ -0,0 +1,58 @@
"""
XY-Graphik der Abgastemperatur
"""
import cairo
import math
from .page import Page
class Exhaust(Page):
def draw(self, ctx):
# Title
ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD)
ctx.set_font_size(24)
ctx.move_to(10, 45)
ctx.show_text("Exhaust Temperature")
# Graph anzeigen X/Y
x0 = 55
y0 = 255
w = 300
h = 200
# X-Achse
ctx.move_to(x0 - 20, y0)
ctx.line_to(x0 + w, y0)
# Y-Achse
ctx.move_to(x0, y0 + 20)
ctx.line_to(x0, y0 - h)
ctx.stroke()
# Pfeispitze X
ctx.move_to(x0-4, y0 - h + 12)
ctx.line_to(x0, y0 - h)
ctx.line_to(x0 + 4, y0 - h + 12)
ctx.fill()
# Pfeilspitze Y
ctx.move_to(x0 + w -12, y0 - 4)
ctx.line_to(x0 + w, y0)
ctx.line_to(x0 + w - 12, y0 + 4)
ctx.fill()
# Achsenbeschriftung
ctx.set_font_size(16)
ctx.move_to(x0 - 30, y0 - h + 20)
ctx.show_text("°C")
ctx.move_to(x0 + w - 10, y0 + 15)
ctx.show_text("min")
# Hier wird eine Reihe von Meßwerten erwartet
# Aufgrund min und max kann die Y-Achse skaliert werden
# Die X-Achse ist die Zeit
self.draw_text_center(ctx, x0 - 30, y0 - h / 2, "Temperature", True, False)
# Einzeichnen von zwei Warnschwellen als horizontale
# Linie (gestrichelt)

137
pages/fluid.py Normal file
View File

@@ -0,0 +1,137 @@
"""
Füllstandsanzeige Tank
0: "Fuel",
1: "Water",
2: "Gray Water",
3: "Live Well",
4: "Oil",
5: "Black Water",
6: "Fuel Gasoline",
14: "Error",
15: "Unavailable"
"""
import os
import cairo
import math
from .page import Page
import nmea2000.lookup
class Fluid(Page):
def __init__(self, pageno, cfg, boatdata, fluidtype):
super().__init__(pageno, cfg, boatdata)
self.fluidtype = int(fluidtype)
if self.fluidtype == 0:
self.symbol = cairo.ImageSurface.create_from_png(os.path.join(cfg['imgpath'], "fuelpump.png"))
else:
self.symbol = None
def draw(self, ctx):
# Zentrum Instrument
cx = 200
cy = 150
# Radius
r = 110
# Füllstand von 0 - 100%
# 0 = -120°, 100 = +120°
level = self.bd.tank[0].volume or 0
angle = -120 + level * 2.4
# Rahmen
ctx.set_source_rgb(*self.fgcolor)
ctx.set_line_width(3)
ctx.arc(cx, cy, r, 0, 2*math.pi)
ctx.stroke()
# Symbol, sofern vorhanden
if self.symbol:
ctx.save()
ctx.set_source_surface(self.symbol, cx - 8, cy - 50)
ctx.paint()
ctx.restore()
# Fluidtype
ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD)
ctx.set_font_size(24)
ctx.move_to(20, 60)
ctx.show_text(nmea2000.lookup.fluidtype[self.fluidtype])
ctx.stroke()
# Zeigerrahmen im Zentrum
ctx.arc(cx, cy, 8, 0, 2*math.pi)
ctx.stroke()
# Zeiger in Nullposition
# Variante 1, einseitig
#p = ((cx - 1, cy - (r - 20)), (cx + 1, cy - (r - 20)), (cx + 4, cy), (cx - 4, cy))
# Variante 2, überstehend
p = ((cx - 1, cy - (r - 20)), (cx + 1, cy - (r - 20)), (cx + 6, cy + 15), (cx - 6, cy + 15))
# Zeiger für aktuellen Meßwert
zeiger = self.rotate((cx, cy), p, angle)
# Zeiger zeichnen
ctx.move_to(*zeiger[0])
for point in zeiger[1:]:
ctx.line_to(*point)
ctx.fill()
# Lösche das Zentrum heraus
ctx.set_source_rgb(*self.bgcolor)
ctx.arc(cx, cy, 6, 0, 2*math.pi)
ctx.fill()
ctx.set_source_rgb(*self.fgcolor)
# Simple Skala direkt zeichnen
# 50%-Wert oben in der Mitte
ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD)
ctx.set_font_size(16)
ctx.move_to(cx, cy -r)
ctx.line_to(cx, cy -r + 16)
# linker Anschlag 0%
self.draw_text_center(ctx, cx, cy - r + 30, "1/2")
l = self.rotate((cx, cy), ((cx, cy - r + 16), (cx, cy - r)), -120)
ctx.move_to(*l[0])
ctx.line_to(*l[1])
# rechter Anschlag 100%
l = self.rotate((cx, cy), ((cx, cy - r + 16), (cx, cy - r)), 120)
ctx.move_to(*l[0])
ctx.line_to(*l[1])
# 25%
l = self.rotate((cx, cy), ((cx, cy - r + 16), (cx, cy - r)), -60)
ctx.move_to(*l[0])
ctx.line_to(*l[1])
tx, ty = self.rotate((cx, cy), ((cx, cy - r + 30),), -60)[0]
self.draw_text_center(ctx, tx, ty, "1/4")
# 75%
l = self.rotate((cx, cy), ((cx, cy - r + 16), (cx, cy - r)), 60)
ctx.move_to(*l[0])
ctx.line_to(*l[1])
tx, ty = self.rotate((cx, cy), ((cx, cy - r + 30),), 60)[0]
self.draw_text_center(ctx, tx, ty, "3/4")
ctx.set_font_size(24)
tx, ty = self.rotate((cx, cy), ((cx, cy - r + 30),), -130)[0]
self.draw_text_center(ctx, tx, ty, "E")
tx, ty = self.rotate((cx, cy), ((cx, cy - r + 30),), 130)[0]
self.draw_text_center(ctx, tx, ty, "F")
ctx.stroke()
self.draw_text_center(ctx, cx, cy + r - 20, f"{level:.0f}%")
ctx.stroke()
# Skalenpunkte
# Alle 5% ein Punkt aber nur da wo noch kein Strich ist
for angle in [x for x in range(-120, 120, 12) if x not in (-120, -60, 0, 60, 120)]:
x, y = self.rotate((cx, cy), ((cx, cy - r + 10),), angle)[0]
ctx.arc(x, y, 2, 0, 2*math.pi)
ctx.fill()

75
pages/fourvalues.py Normal file
View File

@@ -0,0 +1,75 @@
"""
Vier frei wählbare Meßwerte
Layout
+--------------------+
| 1 |
+--------------------+
| 2 |
+--------------------+
| 3 |
+--------------------+
| 4 |
+--------------------+
"""
import cairo
from .page import Page
class FourValues(Page):
def __init__(self, pageno, cfg, boatdata, boatvalue1, boatvalue2, boatvalue3, boatvalue4):
super().__init__(pageno, cfg, boatdata)
self.value1 = boatvalue1
self.value2 = boatvalue2
self.value3 = boatvalue3
self.value4 = boatvalue4
def draw(self, ctx):
# Seitenunterteilung
ctx.rectangle(0, 80, 400, 3)
ctx.rectangle(0, 146, 400, 3)
ctx.rectangle(0, 214, 400, 3)
ctx.fill()
#
ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD)
ctx.set_font_size(32)
ctx.move_to(20, 45)
ctx.show_text("AWA")
ctx.move_to(20, 113)
ctx.show_text("AWS")
ctx.move_to(20, 181)
ctx.show_text("COG")
ctx.move_to(20, 249)
ctx.show_text("STW")
ctx.stroke()
# Units
ctx.set_font_size(16)
ctx.move_to(20, 65)
ctx.show_text("Deg")
ctx.move_to(20, 133)
ctx.show_text("kn")
ctx.move_to(20, 201)
ctx.show_text("Deg")
ctx.move_to(20, 269)
ctx.show_text("kn")
ctx.stroke()
# Meßwerte
ctx.select_font_face("DSEG7 Classic")
ctx.set_font_size(40)
ctx.move_to(180, 65)
ctx.show_text("150")
ctx.move_to(180, 133)
ctx.show_text("25.3")
ctx.move_to(180, 201)
ctx.show_text("146")
ctx.move_to(180, 269)
ctx.show_text("56.4")

77
pages/fourvalues2.py Normal file
View File

@@ -0,0 +1,77 @@
"""
Vier frei auswählbare Meßwerte
Layout
+--------------------+
| 1 |
+--------------------+
| 2 |
+--------------------+
| 3 | 4 |
+--------------------+
"""
import cairo
from .page import Page
class FourValues2(Page):
def __init__(self, pageno, cfg, boatdata, boatvalue1, boatvalue2, boatvalue3, boatvalue4):
super().__init__(pageno, cfg, boatdata)
self.value1 = boatvalue1
self.value2 = boatvalue2
self.value3 = boatvalue3
self.value4 = boatvalue4
def draw(self, ctx):
# Seitenunterteilung
ctx.rectangle(0, 105, 400, 3)
ctx.rectangle(0, 195, 400, 3)
ctx.rectangle(200, 195, 3, 75)
ctx.fill()
ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD)
# Titel
ctx.set_font_size(40)
ctx.move_to(20, 55)
ctx.show_text("AWA")
ctx.move_to(20, 145)
ctx.show_text("AWS")
ctx.set_font_size(24)
ctx.move_to(20, 220)
ctx.show_text("COG")
ctx.move_to(220, 220)
ctx.show_text("STW")
# Einheiten
ctx.set_font_size(16)
ctx.move_to(20, 90)
ctx.show_text("Deg")
ctx.move_to(20, 180)
ctx.show_text("kn")
ctx.set_font_size(16)
ctx.move_to(20, 240)
ctx.show_text("Deg")
ctx.move_to(220, 240)
ctx.show_text("kn")
# Meßwerte
ctx.select_font_face("DSEG7 Classic")
ctx.set_font_size(60)
ctx.move_to(180, 90)
ctx.show_text("150")
ctx.move_to(180, 180)
ctx.show_text("33.0")
ctx.set_font_size(40)
ctx.move_to(80, 270)
ctx.show_text("146")
ctx.move_to(280, 270)
ctx.show_text("50.5")
ctx.stroke()

44
pages/generator.py Normal file
View File

@@ -0,0 +1,44 @@
"""
WIP Mangels Generator keine Überprüfung möglich
Dies ist im Prinzip ein Platzhalter
"""
import cairo
from .page import Page
import math
class Generator(Page):
def draw_generator(self, ctx, x, y, r):
ctx.set_line_width(4.0)
ctx.arc(x, y, r)
ctx.set_font_size(60)
self.draw_text_center(ctx, x, y, "G")
def draw(self, ctx):
# Name
ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD)
ctx.set_font_size(40)
ctx.move_to(10, 65)
ctx.show_text("Power")
ctx.move_to(12, 82)
ctx.show_text("Generator")
# Voltage type
ctx.select_font_face("DSEG7 Classic")
ctx.move_to(10, 140)
# 12 or 24
ctx.show_text("12V")
# Generator power
# kW or W
# Show load level in percent
# Show sensor type info
# INA219, INA226
# Current, A
# Consumption, W

114
pages/keel.py Normal file
View File

@@ -0,0 +1,114 @@
"""
Rotationssensor AS5600 mit Funktion "Kiel"
WIP
Idee:
- Zusätzlich Anzeigemöglichkeit für die Tiefe eines variablen Kiels
- Mode-Taste
"""
import cairo
import math
from .page import Page
class Keel(Page):
def __init__(self, pageno, cfg, boatdata):
super().__init__(pageno, cfg, boatdata)
# Wert für Kielrotation
self.valref = self.bd.getRef("xdrRotK")
def draw(self, ctx):
# Mitte oben Instrument (Halbkreis)
cx = 200
cy = 150
# Radius Kielposition
r = 110
# Titel
ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD)
ctx.set_font_size(32)
ctx.move_to(100, 70)
ctx.show_text("Keel Position")
ctx.set_font_size(24)
ctx.move_to(175, 110)
ctx.show_text(self.valref.unit)
ctx.stroke()
# Halbkreis für Skala
ctx.set_source_rgb(*self.fgcolor)
ctx.set_line_width(3)
ctx.arc(cx, cy, r + 10, 0, math.pi)
ctx.stroke()
# Skala mit Strichen, Punkten und Beschriftung
char = {
90: "45",
120: "30",
150: "15",
180: "0",
210: "15",
240: "30",
270: "45"
}
# Zeichnen in 10°-Schritten
ctx.set_font_size(16)
for i in range(90, 271, 10):
fx = math.sin(i / 180 * math.pi)
fy = math.cos(i / 180 * math.pi)
if i in char:
x = cx + (r - 30) * fx
y = cy - (r - 30) * fy
self.draw_text_center(ctx, x, y, char[i])
ctx.stroke()
if i % 30 == 0:
ctx.move_to(cx + (r - 10) * fx, cy - (r - 10) * fy)
ctx.line_to(cx + (r + 10) * fx, cy - (r + 10) * fy)
ctx.stroke()
else:
x = cx + r * fx
y = cy - r * fy
ctx.arc(x, y, 2, 0, 2*math.pi)
ctx.fill()
# Boot und Wasserlinie
ctx.arc(cx, cy - 10, 28, 0, math.pi)
ctx.fill()
ctx.set_line_width(4)
ctx.move_to(150, cy)
ctx.line_to(250, cy)
ctx.stroke()
#ctx.arc(200, 150, r + 10, 0, 2*math.pi)
#ctx.fill()
#ctx.set_source_rgb(*self.bgcolor)
#ctx.arc(200, 150, r + 7, 0, 2* math.pi)
#ctx.rectangle(0, 30, 299, 122)
#ctx.fill()
angle = -15
#angle = self.valref.value
#TODO Angle limits to +/-45°
if angle < -45:
angle = -45
elif angle > 45:
angle = 45
angle *= 2 # stretched scale
# Kiel
p = ((cx - 6, cy), (cx + 6, cy), (cx + 2, cy + r - 50), (cx - 2, cy + r - 50))
keel = self.rotate((cx, cy), p, angle)
ctx.move_to(*keel[0])
for point in keel[1:]:
ctx.line_to(*point)
ctx.fill()
# Kiel-Bombe
x, y = self.rotate((cx, cy), ((cx, cy + r -50),), angle)[0]
ctx.arc(x, y, 5, 0, 2*math.pi)
ctx.fill()

26
pages/onegraph.py Normal file
View File

@@ -0,0 +1,26 @@
"""
Frei auswählbaren Meßwert als Graphen anzeigen
YDGS01
History Request PGN 61184
History Data PGN 130816
"""
import cairo
from .page import Page
class OneGraph(Page):
def draw(self, ctx):
# Name
ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD)
ctx.set_font_size(60)
ctx.move_to(20, 100)
ctx.show_text("One Graph")
# Graph anzeigen X/Y

31
pages/onevalue.py Normal file
View File

@@ -0,0 +1,31 @@
import cairo
from .page import Page
class OneValue(Page):
def __init__(self, pageno, cfg, boatdata, boatvalue):
super().__init__(pageno, cfg, boatdata)
self.ref1 = self.bd.getRef(boatvalue)
def draw(self, ctx):
# Bezeichnung
ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD)
ctx.set_font_size(60)
ctx.move_to(20, 100)
ctx.show_text(self.ref1.valname)
# Einheit
ctx.set_font_size(40)
ctx.move_to(270, 100)
ctx.show_text(self.ref1.unit)
# Meßwert
ctx.select_font_face("DSEG7 Classic")
ctx.set_font_size(100)
ctx.move_to(40, 240)
if self.ref1.value:
ctx.show_text(self.ref1.format())
else:
ctx.show_text(self.placeholder)
ctx.stroke()

202
pages/page.py Normal file
View File

@@ -0,0 +1,202 @@
"""
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):
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
hf = ext.height + 4
ctx.rectangle(xf, yf, wf, hf)
ctx.fill()
ctx.set_source_rgb(*self.fgcolor)
if rotate:
if baseline:
ctx.move_to(x - ext[3] / 2.0, y)
else:
ctx.move_to(x - ext[3] / 2.0, y + ext[2] / 2.0)
ctx.save()
ctx.rotate(1.5 * math.pi)
ctx.show_text(content)
ctx.restore()
else:
if baseline:
ctx.move_to(x - ext[2] / 2.0, y)
else:
ctx.move_to(x - ext[2] / 2.0, y + ext[3] / 2.0)
ctx.show_text(content)
ctx.stroke()
def draw_text_ralign(self, ctx, x, y, content):
ext = ctx.text_extents(content)
ctx.move_to(x - ext[2], y)
ctx.show_text(content)
ctx.stroke()

13
pages/rollpitch.py Normal file
View File

@@ -0,0 +1,13 @@
import cairo
from .page import Page
class RollPitch(Page):
def draw(self, ctx):
# Name
ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD)
ctx.set_font_size(60)
ctx.move_to(20, 100)
ctx.show_text("RollPitch")
# Graph anzeigen X/Y

103
pages/rudder.py Normal file
View File

@@ -0,0 +1,103 @@
import cairo
import math
from .page import Page
class Rudder(Page):
def __init__(self, pageno, cfg, boatdata):
super().__init__(pageno, cfg, boatdata)
self.buttonlabel[1] = 'MODE'
self.mode = 'P'
# Werte für Ruderausschlag
self.valpri = self.bd.getRef("RPOS") # Primäres Ruder
self.valsec = self.bd.getRef("PRPOS") # Sekundäres Ruder
def handle_key(self, buttonid):
if buttonid == 1:
if self.mode == 'P':
self.mode = 'S'
else:
self.mode = 'P'
return True
return False
def draw(self, ctx):
# Ruder auswählen
if self.mode == 'P':
valref = self.valpri
else:
valref = self.valsec
# Rotationszentrum
cx = 200
cy = 150
# Radius des Instruments
r = 110
# Titel
ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD)
ctx.set_font_size(32)
ctx.move_to(80, 70)
ctx.show_text("Rudder Position")
ctx.set_font_size(24)
if valref.valid:
ctx.move_to(175, 110)
ctx.show_text(valref.unit)
else:
ctx.move_to(110, 110)
ctx.show_text("no data available")
ctx.stroke()
# Debug
angle = 5
# Rahmen
ctx.set_source_rgb(*self.fgcolor)
ctx.set_line_width(3)
ctx.arc(cx, cy, r + 10, 0, math.pi)
ctx.stroke()
# Zentrum
ctx.arc(cx, cy, 8, 0, 2*math.pi)
ctx.fill()
# Skala mit Strichen, Punkten und Beschriftung
char = {
90: "45",
120: "30",
150: "15",
180: "0",
210: "15",
240: "30",
270: "45"
}
# Zeichnen in 10°-Schritten
ctx.set_font_size(16)
for i in range(90, 271, 10):
fx = math.sin(i / 180 * math.pi)
fy = math.cos(i / 180 * math.pi)
if i in char:
x = cx + (r - 30) * fx
y = cy - (r - 30) * fy
self.draw_text_center(ctx, x, y, char[i])
ctx.stroke()
if i % 30 == 0:
ctx.move_to(cx + (r - 10) * fx, cy - (r - 10) * fy)
ctx.line_to(cx + (r + 10) * fx, cy - (r + 10) * fy)
ctx.stroke()
else:
x = cx + r * fx
y = cy - r * fy
ctx.arc(x, y, 2, 0, 2*math.pi)
ctx.fill()
if valref.valid:
p = ((cx - 6, cy), (cx + 6, cy), (cx + 2, cy + r - 50), (cx - 2, cy + r - 50))
rudder = self.rotate((cx, cy), p, angle)
ctx.move_to(*rudder[0])
for point in rudder[1:]:
ctx.line_to(*point)
ctx.fill()

85
pages/skyview.py Normal file
View File

@@ -0,0 +1,85 @@
"""
Satelliteninfos
- Sat mit Fix: ausgefüllter Kreis
- Sat ohne fix: leerer Kreis
- Slots für 12 SNR-Balken
"""
import cairo
import math
from .page import Page
class SkyView(Page):
def __init__(self, pageno, cfg, boatdata):
super().__init__(pageno, cfg, boatdata)
def pol2cart(azimuth, elevation):
'''
Polar to Cartesian coordinates within the horizon circle.
azimuth in radians
x = math.sin(azimuth) * elevation * self.radius
y = math.cos(azimuth) * elevation * self.radius
'''
pass
# (x, y) = self.pol2cart(sat.az, sat.el)
def draw(self, ctx):
# Name
ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD)
#ctx.set_font_size(32)
#self.draw_text_center(ctx, 200, 40, "Satellite Info")
# Spezialseite
cx = 130
cy = 150
r = 125
r1 = r / 2
ctx.set_line_width(1.5)
ctx.arc(cx, cy, r, 0, 2*math.pi)
ctx.stroke()
ctx.arc(cx, cy, r1, 0, 2*math.pi)
ctx.stroke()
ctx.set_dash([4, 4], 0)
ctx.move_to(cx, cy - r)
ctx.line_to(cx, cy + r)
ctx.move_to(cx - r, cy)
ctx.line_to(cx + r, cy)
ctx.stroke()
ctx.set_dash([], 0)
# Signal/Noise Balken
ctx.set_font_size(16)
ctx.move_to(325, 34)
ctx.show_text("SNR")
ctx.stroke()
ctx.set_line_width(1.5)
ctx.rectangle(270, 20, 125, 257)
# Beispieldaten
ctx.set_line_width(0.5)
for s in range(12):
y = 30 + (s + 1) * 20
ctx.move_to(275, y)
ctx.show_text(f"{s:02d}")
ctx.rectangle(305, y-12, 85, 14)
ctx.stroke()
ctx.set_line_width(1.0)
for s in self.bd.sat.values():
x = cx + math.sin(s.azimuth) * s.elevation * r
y = cy + math.cos(s.azimuth) * s.elevation * r
ctx.arc(x, y, 4, 0, 2*math.pi)
ctx.move_to(x+4, y+4)
ctx.show_text(f"{s.prn_num}")
ctx.stroke()
# Satellitenliste mit SNR-Balken sortiert nach nummer
for prn_num in sorted(self.bd.sat):
print(prn_num)

40
pages/solar.py Normal file
View File

@@ -0,0 +1,40 @@
import cairo
from .page import Page
class Solar(Page):
def draw_solar(self, ctx, x, y, w, h):
pass
"""
// Solar graphic with fill level
void solarGraphic(uint x, uint y, int pcolor, int bcolor){
// Show solar modul
int xb = x; // X position
int yb = y; // Y position
int t = 4; // Line thickness
int percent = 0;
// Solar corpus 100x80
int level = int((100.0 - percent) * (80-(2*t)) / 100.0);
getdisplay().fillRect(xb, yb, 100, 80, pcolor);
if(percent < 99){
getdisplay().fillRect(xb+t, yb+t, 100-(2*t), level, bcolor);
}
// Draw horizontel lines
getdisplay().fillRect(xb, yb+28-t, 100, t, pcolor);
getdisplay().fillRect(xb, yb+54-t, 100, t, pcolor);
// Draw vertical lines
getdisplay().fillRect(xb+19+t, yb, t, 80, pcolor);
getdisplay().fillRect(xb+39+2*t, yb, t, 80, pcolor);
getdisplay().fillRect(xb+59+3*t, yb, t, 80, pcolor);
}
"""
def draw(self, ctx):
# Name
ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD)
ctx.set_font_size(60)
ctx.move_to(20, 100)
ctx.show_text("Solar")

107
pages/system.py Normal file
View File

@@ -0,0 +1,107 @@
import cairo
from .page import Page
import datetime
class System(Page):
def __init__(self, pageno, cfg, boatdata):
super().__init__(pageno, cfg, boatdata)
self.buttonlabel[1] = 'MODE'
self.buttonlabel[2] = 'STBY'
self.mode = ('I', 'N') # (I)nformation (N)MEA2000 Device List
self.modeindex = 1
self.standby = False
def handle_key(self, buttonid):
if self.standby and buttonid != 1:
return True
if buttonid == 1:
if self.standby:
self.standby = False
self.buttonlabel[1] = 'MODE'
self.buttonlabel[2] = 'STBY'
self.header = True
self.footer = True
else:
if self.modeindex < len(self.mode):
self.modeindex += 1
else:
self.modeindex = 0
return True
if buttonid == 2:
self.buttonlabel[1] = None
self.buttonlabel[2] = None
self.header = False
self.footer = False
self.standby = True
return False
def draw_stby(self, ctx):
# Standby
# TODO Kopf und Fußzeile ausschalten
# Ein Klick auf die Mode-Taste weckt das System wieder auf
pass
def draw_info(self, ctx):
# Bezeichnung
ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD)
ctx.set_font_size(32)
self.draw_text_center(ctx, 200, 40 , "System Info")
ctx.set_font_size(16)
# System name
# Software version
# Linke Seite
ctx.move_to(2, 80)
ctx.show_text("Simulation: ")
ctx.move_to(140, 80)
ctx.show_text('On' if self.cfg['simulation'] else 'Off')
ctx.move_to(2, 100)
ctx.show_text("BME280/BMP280: ")
ctx.move_to(140, 100)
ctx.show_text('On' if self.cfg['bme280'] else 'Off')
ctx.move_to(2, 120)
ctx.show_text("GPS: ")
ctx.move_to(140, 120)
ctx.show_text('On' if self.cfg['gps'] else 'Off')
# Rechte Seite
ctx.move_to(202, 80)
ctx.show_text("Wifi: ")
ctx.move_to(340, 80)
ctx.show_text('On')
ctx.move_to(202, 100)
ctx.show_text("Buzzer: ")
ctx.move_to(340, 100)
ctx.show_text('60%')
ctx.move_to(202, 120)
ctx.show_text("Timezone: ")
ctx.move_to(340, 120)
ctx.show_text(datetime.datetime.now().astimezone().tzname())
ctx.stroke()
# Geräteliste
ctx.move_to(2, 150)
ctx.show_text("NMEA2000 Device List")
ctx.set_line_width(1.5)
ctx.rectangle(2, 155, 394, 100)
ctx.stroke()
def draw_devlist(self, ctx):
# NMEA2000 Geräteliste, Vollbild
# scrollen mit Up/Down
pass
def draw(self, ctx):
if self.standby:
return
self.draw_info(ctx)

47
pages/threevalues.py Normal file
View File

@@ -0,0 +1,47 @@
import cairo
from .page import Page
class ThreeValues(Page):
def __init__(self, pageno, cfg, boatdata, boatvalue1, boatvalue2, boatvalue3):
super().__init__(pageno, cfg, boatdata)
self.ref1 = self.bd.getRef(boatvalue1)
self.ref2 = self.bd.getRef(boatvalue2)
self.ref3 = self.bd.getRef(boatvalue3)
def draw(self, ctx):
# Seitenlayout
ctx.rectangle(0, 105, 400, 3)
ctx.rectangle(0, 195, 400, 3)
ctx.fill()
ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD)
# Titel
ctx.set_font_size(40)
ctx.move_to(20, 55)
ctx.show_text(self.ref1.valname)
ctx.move_to(20, 145)
ctx.show_text(self.ref2.valname)
ctx.move_to(20, 235)
ctx.show_text(self.ref3.valname)
# Einheiten
ctx.set_font_size(24)
ctx.move_to(20, 90)
ctx.show_text(self.ref1.unit)
ctx.move_to(20, 180)
ctx.show_text(self.ref2.unit)
ctx.move_to(20, 270)
ctx.show_text(self.ref3.unit)
ctx.select_font_face("DSEG7 Classic")
ctx.set_font_size(60)
ctx.move_to(180, 90)
ctx.show_text(self.ref1.format())
ctx.move_to(180, 180)
ctx.show_text(self.ref2.format())
ctx.move_to(180, 270)
ctx.show_text(self.ref3.format())
ctx.stroke()

14
pages/twographs.py Normal file
View File

@@ -0,0 +1,14 @@
import cairo
from .page import Page
class TwoGraphs(Page):
def draw(self, ctx):
# Name
ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD)
ctx.set_font_size(60)
ctx.move_to(20, 100)
ctx.show_text("Two Graphs")
# Zwei Graphen anzeigen X/Y nebeneinander

67
pages/twovalues.py Normal file
View File

@@ -0,0 +1,67 @@
"""
Anzeige von zwei frei definierbaren Werten
Layout
+--------------------+
| 1 |
+--------------------+
| 2 |
+--------------------+
"""
import cairo
from .page import Page
class TwoValues(Page):
def __init__(self, pageno, cfg, boatdata, boatvalue1, boatvalue2):
super().__init__(pageno, cfg, boatdata)
self.ref1 = self.bd.getRef(boatvalue1)
self.ref2 = self.bd.getRef(boatvalue2)
#print(self.ref1.valname)
def draw(self, ctx):
# Seitenunterteilung
ctx.rectangle(0, 145, 400, 3)
ctx.fill()
# Name
ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD)
ctx.set_font_size(40)
ctx.move_to(20, 80)
ctx.show_text(self.ref1.valname)
ctx.move_to(20, 190)
ctx.show_text(self.ref2.valname)
# Einheit
ctx.set_font_size(24)
ctx.move_to(20, 130)
ctx.show_text(self.ref1.unit)
ctx.move_to(20, 240)
ctx.show_text(self.ref2.unit)
# Meßwerte
if type(self.ref1 == 'BoatValueGeo'):
ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD)
ctx.set_font_size(40)
ctx.move_to(140, 100)
else:
ctx.select_font_face("DSEG7 Classic")
ctx.set_font_size(84)
ctx.move_to(180, 130)
ctx.show_text(self.ref1.format())
if type(self.ref2 == 'BoatValueGeo'):
ctx.select_font_face("Ubuntu")
ctx.set_font_size(40)
ctx.move_to(140, 210)
else:
ctx.select_font_face("DSEG7 Classic")
ctx.set_font_size(84)
ctx.move_to(180, 240)
ctx.show_text(self.ref2.format())
ctx.stroke()

216
pages/voltage.py Normal file
View File

@@ -0,0 +1,216 @@
"""
Integrierte Spannungsmessung
Ideen:
- Umschaltung Datenquelle: intern, N2K
- Umschaltung analog / digital / Graphik
- Historische Werte vom YD-Batteriemonitor
"""
import cairo
import math
from .page import Page
class Voltage(Page):
avg = (1, 10, 60, 300);
def __init__(self, pageno, cfg, boatdata):
super().__init__(pageno, cfg, boatdata)
self.trend = True
self.mode = 'A'
self.avgindex = 0
self.buttonlabel[1] = 'AVG'
self.buttonlabel[2] = 'MODE'
self.buttonlabel[5] = 'TRD'
self.lastvalue = self.bd.voltage.value
def handle_key(self, buttonid):
if buttonid == 1:
if self.avgindex < len(self.avg) -1:
self.avgindex += 1
else:
self.avgindex = 0
elif buttonid == 2:
if self.mode == 'A':
self.mode = 'D'
else:
self.mode = 'A'
elif buttonid == 5:
self.trend = not self.trend
def setBoatValue(self, boatvalue):
# Einstellen welcher Wert dargestellt werden soll
self.value1 = boatvalue
def draw(self, ctx):
ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD)
if self.mode == 'A':
self.draw_analog(ctx)
else:
self.draw_digital(ctx)
def draw_analog(self, ctx):
# TODO schönes Viertelkreis-Instrument
# Skala von 9V bis 15V
# d.h. 90° entsprechend unterteilen in 6 Stücke je 15°
# Beschriftung 10, 11, 12, 13, 14
# Datenquelle
ctx.set_font_size(16)
ctx.move_to(300, 40)
ctx.show_text("Source:")
ctx.move_to(300, 60)
ctx.show_text("VBat")
# Batterietyp
ctx.move_to(300, 90)
ctx.show_text("Type:")
ctx.move_to(300, 110)
ctx.show_text("AGM")
# Glättung
ctx.move_to(300, 140)
ctx.show_text("Avg:")
ctx.move_to(300, 160)
ctx.show_text("1s")
ctx.stroke()
# Gleichstromsymbol
ctx.set_font_size(32)
ctx.move_to(20, 80)
ctx.show_text("V")
ctx.set_line_width(3)
ctx.move_to(20, 86.5) # obere Linie
ctx.line_to(40, 86.5)
ctx.move_to(20, 91.5) # untere drei kurzen Linien
ctx.line_to(25, 91.5)
ctx.move_to(27, 91.5)
ctx.line_to(33, 91.5)
ctx.move_to(35, 91.5)
ctx.line_to(40, 91.5)
ctx.stroke()
# Kreis-segment 90°
# Rotationszentrum
cx = 260
cy = 270
# Radius des Instruments
r = 240
ctx.set_source_rgb(*self.fgcolor)
ctx.set_line_width(2)
ctx.arc(cx, cy, r, math.pi, 1.5*math.pi)
ctx.stroke()
# Beschriftung
ctx.set_font_size(20)
label = {285: "10", 300: "11", 315: "12", 330: "13", 345: "14"}
for angle in label:
x, y = self.rotate((cx, cy), ((cx, cy - r + 30),), angle)[0]
self.draw_text_center(ctx, x, y, label[angle])
# grobe Skala
p = ((cx, cy-r), (cx, cy - r + 12))
ctx.set_line_width(2)
for angle in label:
line = self.rotate((cx, cy), p, angle)
ctx.move_to(*line[0])
ctx.line_to(*line[1])
ctx.stroke()
# feine Skala
p = ((cx, cy-r), (cx, cy - r + 5))
ctx.set_line_width(1)
for angle in [x for x in range(273, 360, 3)]:
if angle in label:
continue
line = self.rotate((cx, cy), p, angle)
ctx.move_to(*line[0])
ctx.line_to(*line[1])
ctx.stroke()
# Zeiger auf 0-Position
val = float(self.bd.voltage.format())
if not val:
angle = -0.5
elif val > 15:
angle = 91
elif val <= 9:
angle = -0.5
else:
angle = (val - 9) * 15
p = ((cx - 2, cy + 4),
(cx - r + 35, cy + 2),
(cx - r + 35, cy + 1),
(cx - r + 5, cy + 1),
(cx - r + 5, cy - 1),
(cx - r + 35, cy - 1),
(cx - r + 35, cy - 2),
(cx - 2, cy - 4))
zeiger = self.rotate((cx, cy), p, angle)
# Zeiger zeichnen
ctx.set_line_width(1)
ctx.move_to(*zeiger[0])
for point in zeiger[1:]:
ctx.line_to(*point)
ctx.fill()
# Zeigerbasis
ctx.arc(cx, cy, 6, 0, 2*math.pi)
ctx.set_source_rgb(*self.bgcolor)
ctx.fill_preserve()
ctx.set_line_width(2)
ctx.set_source_rgb(*self.fgcolor)
ctx.stroke()
def draw_digital(self, ctx):
# Name
ctx.set_font_size(60)
ctx.move_to(20, 100)
ctx.show_text("VBat")
# Unit
ctx.set_font_size(40)
ctx.move_to(270, 100)
ctx.show_text("V")
# Battery type
ctx.set_font_size(16)
ctx.move_to(294, 100)
ctx.show_text("AGM")
# Mittelwert
ctx.move_to(320, 84)
ctx.show_text("Avg: {}s".format(self.avg[self.avgindex]))
ctx.stroke()
ctx.select_font_face("DSEG7 Classic")
ctx.set_font_size(100)
ctx.move_to(40, 240)
ctx.show_text(self.bd.voltage.format())
ctx.stroke()
# Trendanzeige
if self.trend and self.bd.voltage.value and self.lastvalue:
ctx.rectangle(315, 183, 35, 4)
ctx.fill()
size = 11
if self.lastvalue < self.bd.voltage.value:
ctx.move_to(320, 174)
ctx.line_to(320+size*2, 174)
ctx.line_to(320+size, 174-size*2)
ctx.line_to(320, 174)
ctx.fill()
elif self.lastvalue > self.bd.voltage.value:
ctx.move_to(320, 195)
ctx.line_to(320+size*2, 195)
ctx.line_to(320+size, 195+size*2)
ctx.line_to(320, 195)
ctx.fill()
self.lastvalue = self.bd.voltage.value

119
pages/windrose.py Normal file
View File

@@ -0,0 +1,119 @@
"""
Windrose und Windroseflex
Benötigt 6 Werte
Hauptwerte: AWA, AWS, TWD, TWS
Nebenwerte: DBT, STW, oder COG, SOG
"""
import cairo
import math
from .page import Page
class WindRose(Page):
def draw(self, ctx):
# Rahmen
cx = 200
cy = 150
r = 110
ctx.set_line_width(3)
ctx.arc(cx, cy, r + 9, 0, 2*math.pi)
ctx.stroke()
ctx.arc(cx, cy, r - 11, 0, 2*math.pi)
ctx.stroke()
for angle in range(0, 360, 10):
if angle % 30 != 0:
x, y = self.rotate((cx, cy), ((cx, cy - r),), angle)[0]
ctx.arc(x, y, 2, 0, 2*math.pi)
ctx.fill()
else:
p = ((cx, cy - r + 10), (cx, cy - r - 10), (cx, cy - r + 30))
pr = self.rotate((cx, cy), p, angle)
ctx.move_to(*pr[0])
ctx.line_to(*pr[1])
ctx.stroke()
self.draw_text_center(ctx, *pr[2], str(angle))
ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD)
# Namen
ctx.set_font_size(24)
ctx.move_to(10, 95) # links oben
ctx.show_text("AWA")
ctx.move_to(335, 95) # rechts oben
ctx.show_text("TWD")
ctx.move_to(10, 220) # links unten
ctx.show_text("AWS")
ctx.move_to(335, 220) # rechts unten
ctx.show_text("TWS")
# Units
ctx.set_font_size(16)
ctx.move_to(10, 115) # links oben
ctx.show_text("Deg")
ctx.move_to(335, 115) # rechts oben
ctx.show_text("Deg")
ctx.move_to(10, 190) # links unten
ctx.show_text("kn")
ctx.move_to(335, 190) # rechts unten
ctx.show_text("kn")
# Horiz. Trennlinien
#ctx.rectangle(0, 149, 60, 3)
#ctx.fill()
ctx.set_line_width(3)
# links
ctx.move_to(0, 149)
ctx.line_to(60, 149)
# rechts
ctx.move_to(340, 149)
ctx.line_to(400, 149)
ctx.stroke()
# Meßwerte
ctx.select_font_face("DSEG7 Classic")
ctx.set_font_size(40)
# links oben
ctx.move_to(10, 65)
ctx.show_text("148")
# rechts oben
ctx.move_to(295, 65)
ctx.show_text("---")
# links unten
ctx.move_to(10, 270)
ctx.show_text("46.7")
# rechts unten
ctx.move_to(295, 270)
ctx.show_text("77.8")
ctx.set_font_size(32)
# innen oben
ctx.move_to(160, 130)
ctx.show_text("38.9")
# innen unten
ctx.move_to(160, 200)
ctx.show_text("19.9")
ctx.stroke()
# Zeiger
angle = 148
p = ((cx - 1, cy - (r - 15)), (cx + 1, cy - (r - 15)), (cx + 4, cy), (cx - 4, cy))
zeiger = self.rotate((cx, cy), p, angle)
ctx.move_to(*zeiger[0])
for point in zeiger[1:]:
ctx.line_to(*point)
ctx.fill()
ctx.set_source_rgb(*self.bgcolor)
ctx.arc(cx, cy, 8, 0, 2*math.pi)
ctx.fill()
ctx.set_source_rgb(*self.fgcolor)
ctx.arc(cx, cy, 7, 0, 2*math.pi)
ctx.fill()