Erstveröffentlichung Weihnachten 2024
This commit is contained in:
36
pages/__init__.py
Normal file
36
pages/__init__.py
Normal 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
52
pages/anchor.py
Normal 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
16
pages/apparentwind.py
Normal 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
102
pages/autobahn.py
Normal 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
366
pages/barograph.py
Normal 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
66
pages/battery.py
Normal 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
90
pages/battery2.py
Normal 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
55
pages/bme280.py
Normal 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
188
pages/clock.py
Normal 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
40
pages/dst810.py
Normal 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
58
pages/exhaust.py
Normal 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
137
pages/fluid.py
Normal 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
75
pages/fourvalues.py
Normal 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
77
pages/fourvalues2.py
Normal 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
44
pages/generator.py
Normal 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
114
pages/keel.py
Normal 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
26
pages/onegraph.py
Normal 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
31
pages/onevalue.py
Normal 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
202
pages/page.py
Normal 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
13
pages/rollpitch.py
Normal 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
103
pages/rudder.py
Normal 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
85
pages/skyview.py
Normal 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
40
pages/solar.py
Normal 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
107
pages/system.py
Normal 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
47
pages/threevalues.py
Normal 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
14
pages/twographs.py
Normal 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
67
pages/twovalues.py
Normal 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
216
pages/voltage.py
Normal 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
119
pages/windrose.py
Normal 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()
|
||||
Reference in New Issue
Block a user