328 lines
7.7 KiB
Python
328 lines
7.7 KiB
Python
import os
|
|
import enum
|
|
import cairo
|
|
|
|
from typing import Optional
|
|
|
|
from hexagram.gauge import Gauge
|
|
from hexagram.icon import Icon
|
|
|
|
class StatusIcon(Icon):
|
|
TYPE = enum.Enum
|
|
|
|
def __init__(self, width: float, height: float):
|
|
super().__init__(self.NAME, width, height, self.VARIANTS, self.STYLE)
|
|
|
|
class CautionStatus(enum.Enum):
|
|
OFF = 0
|
|
ON = 1
|
|
WARNING = 2
|
|
|
|
class CollisionStatus(enum.Enum):
|
|
OK = 0
|
|
DETECTED = 1
|
|
|
|
class CollisionIcon(StatusIcon):
|
|
TYPE = CollisionStatus
|
|
NAME = 'collision'
|
|
STYLE = 'path {stroke-width: 10px; stroke: %(s)s}'
|
|
VARIANTS = {
|
|
CollisionStatus.DETECTED: '#f00'
|
|
}
|
|
|
|
class FuelStatus(enum.Enum):
|
|
OK = 0
|
|
LOW = 1
|
|
FAULT = 2
|
|
|
|
class FuelIcon(StatusIcon):
|
|
TYPE = FuelStatus
|
|
NAME = 'fuel'
|
|
STYLE = '#g9 * {stroke: %(s)s} #path9 {fill: %(s)s}'
|
|
VARIANTS = {
|
|
FuelStatus.LOW: '#fa0',
|
|
FuelStatus.FAULT: '#f00'
|
|
}
|
|
|
|
class SeatBeltStatus(enum.Enum):
|
|
OK = 0
|
|
UNFASTENED = 1
|
|
FAULT = 2
|
|
|
|
class SeatBeltIcon(StatusIcon):
|
|
TYPE = SeatBeltStatus
|
|
NAME = 'belt'
|
|
STYLE = 'path {fill: %(s)s}'
|
|
VARIANTS = {
|
|
SeatBeltStatus.UNFASTENED: '#f00'
|
|
}
|
|
|
|
class CoolantStatus(enum.Enum):
|
|
OK = 0
|
|
LOW = 1
|
|
OVERHEAT = 2
|
|
FAULT = 3
|
|
|
|
class CoolantIcon(StatusIcon):
|
|
TYPE = CoolantStatus
|
|
NAME = 'coolant'
|
|
STYLE = 'path, circle, rect {fill: %(s)s} #g9 path {stroke: %(s)s}'
|
|
VARIANTS = {
|
|
CoolantStatus.LOW: '#fa0',
|
|
CoolantStatus.OVERHEAT: '#f00'
|
|
}
|
|
|
|
class OilStatus(enum.Enum):
|
|
OK = 0
|
|
LOW = 1
|
|
OVERHEAT = 2
|
|
FAULT = 3
|
|
|
|
class OilIcon(StatusIcon):
|
|
TYPE = OilStatus
|
|
NAME = 'oil'
|
|
STYLE ='path {fill: %(s)s; stroke: %(s)s}'
|
|
VARIANTS = {
|
|
OilStatus.LOW: '#fa0',
|
|
OilStatus.OVERHEAT: '#f00'
|
|
}
|
|
|
|
class ParkingBrakeStatus(enum.Enum):
|
|
OFF = 0
|
|
ON = 1
|
|
FAULT = 2
|
|
|
|
class ParkingBrakeIcon(StatusIcon):
|
|
TYPE = ParkingBrakeStatus
|
|
NAME = 'parking'
|
|
STYLE = '#path6, #path5 {stroke: %(s)s} circle {stroke: %(s)s} #path7 {fill: %(s)s}'
|
|
VARIANTS = {
|
|
ParkingBrakeStatus.ON: '#fa0'
|
|
}
|
|
|
|
class WiperWasherStatus(enum.Enum):
|
|
OK = 0
|
|
LOW = 1
|
|
FAULT = 2
|
|
|
|
class WiperWasherIcon(StatusIcon):
|
|
TYPE = WiperWasherStatus
|
|
NAME = 'wiper-washer'
|
|
STYLE = 'rect,path,circle{stroke:%(s)s;}'
|
|
VARIANTS = {
|
|
WiperWasherStatus.LOW: '#fa0',
|
|
WiperWasherStatus.FAULT: '#f00'
|
|
}
|
|
|
|
class LaneKeepStatus(enum.Enum):
|
|
OFF = 0
|
|
ON = 1
|
|
FAULT = 2
|
|
|
|
class LaneKeepIcon(StatusIcon):
|
|
TYPE = LaneKeepStatus
|
|
NAME = 'lane-departure'
|
|
STYLE = 'path {stroke: %(s)s; stroke-width: 8}'
|
|
VARIANTS = {
|
|
LaneKeepStatus.OFF: '#fa0',
|
|
LaneKeepStatus.ON: '#0f0',
|
|
LaneKeepStatus.FAULT: '#f00'
|
|
}
|
|
|
|
class StabilityControlStatus(enum.Enum):
|
|
OFF = 0
|
|
ON = 1
|
|
FAULT = 2
|
|
|
|
class StabilityControlIcon(StatusIcon):
|
|
TYPE = StabilityControlStatus
|
|
NAME = 'stability'
|
|
STYLE = '#path9, #path8, #path7, #path6, #path5 {fill: %(s)s} #path10 {stroke: %(s)s}'
|
|
VARIANTS = {
|
|
StabilityControlStatus.OFF: '#fa0',
|
|
StabilityControlStatus.ON: '#0f0',
|
|
StabilityControlStatus.FAULT: '#f00'
|
|
}
|
|
|
|
class TractionControlFault(enum.Enum):
|
|
OFF = 0
|
|
ON = 1
|
|
FAULT = 2
|
|
|
|
class TPMSStatus(enum.Enum):
|
|
OK = 0
|
|
WARNING = 1
|
|
FAULT = 2
|
|
|
|
class TPMSIcon(StatusIcon):
|
|
TYPE = TPMSStatus
|
|
NAME = 'tpms'
|
|
STYLE = 'path, circle {fill: %(s)s}'
|
|
VARIANTS = {
|
|
TPMSStatus.WARNING: '#fa0',
|
|
TPMSStatus.FAULT: '#f00'
|
|
}
|
|
|
|
class ABSStatus(enum.Enum):
|
|
OFF = 0
|
|
ON = 1
|
|
FAULT = 2
|
|
|
|
class AirbagStatus(enum.Enum):
|
|
OFF = 0
|
|
ON = 1
|
|
FAULT = 2
|
|
|
|
class BatteryStatus(enum.Enum):
|
|
OK = 0
|
|
FAULT = 1
|
|
|
|
class BatteryIcon(StatusIcon):
|
|
TYPE = BatteryStatus
|
|
NAME = 'battery',
|
|
STYLE = '#rect5, #rect6 {fill: %(s)s} #rect4, path {stroke: %(s)s}'
|
|
VARIANTS = {
|
|
BatteryStatus.FAULT: '#f00'
|
|
}
|
|
|
|
class EngineStatus(enum.Enum):
|
|
OK = 0
|
|
FAULT = 1
|
|
|
|
class FogBeamStatus(enum.Enum):
|
|
OFF = 0
|
|
ON = 1
|
|
FAULT = 2
|
|
|
|
class FogBeamIcon(StatusIcon):
|
|
TYPE = FogBeamStatus
|
|
NAME = 'beams-fog'
|
|
STYLE = '#g9 path {fill: %(s)s} #g5 path {stroke: %(s)s}'
|
|
VARIANTS = {
|
|
FogBeamStatus.ON: '#0f0',
|
|
FogBeamStatus.FAULT: '#f00'
|
|
}
|
|
|
|
class ParkingBeamStatus(enum.Enum):
|
|
OFF = 0
|
|
ON = 1
|
|
FAULT = 2
|
|
|
|
class ParkingBeamIcon(StatusIcon):
|
|
TYPE = ParkingBeamStatus
|
|
NAME = 'beams-parking'
|
|
STYLE = 'path {stroke: %(s)s; stroke-width: 12}'
|
|
VARIANTS = {
|
|
ParkingBeamStatus.ON: '#ff0',
|
|
ParkingBeamStatus.FAULT: '#f00'
|
|
}
|
|
|
|
class LowBeamStatus(enum.Enum):
|
|
OFF = 0
|
|
ON = 1
|
|
FAULT = 2
|
|
|
|
class LowBeamIcon(StatusIcon):
|
|
TYPE = LowBeamStatus
|
|
NAME = 'beams-low'
|
|
STYLE = 'path {fill: %(s)s; stroke: %(s)s}'
|
|
VARIANTS = {
|
|
LowBeamStatus.ON: '#0f0',
|
|
LowBeamStatus.FAULT: '#f00'
|
|
}
|
|
|
|
class HighBeamStatus(enum.Enum):
|
|
OFF = 0
|
|
ON = 1
|
|
FAULT = 2
|
|
|
|
class HighBeamIcon(StatusIcon):
|
|
TYPE = HighBeamStatus
|
|
NAME = 'beams-high'
|
|
STYLE = 'path {fill: %(s)s; stroke: %(s)s}'
|
|
VARIANTS = {
|
|
HighBeamStatus.ON: '#00f',
|
|
HighBeamStatus.FAULT: '#f00'
|
|
}
|
|
|
|
class ColdStatus(enum.Enum):
|
|
OFF = 0
|
|
ON = 1
|
|
|
|
class ColdIcon(StatusIcon):
|
|
TYPE = ColdStatus
|
|
NAME = 'cold'
|
|
STYLE = 'path {stroke: %(s)s;stroke-width: 8}'
|
|
VARIANTS = {
|
|
ColdStatus.ON: '#fff'
|
|
}
|
|
|
|
class StatusIconBox(Gauge):
|
|
__slots__ = 'x', 'y', 'width', 'height', 'surface', 'statuses', 'icons', \
|
|
'_redraw'
|
|
|
|
ICON_WIDTH = 48
|
|
ICON_HEIGHT = 48
|
|
|
|
ICONS = (
|
|
SeatBeltIcon, ParkingBeamIcon, LowBeamIcon, HighBeamIcon, FogBeamIcon, StabilityControlIcon, LaneKeepIcon,
|
|
ParkingBrakeIcon, FuelIcon, WiperWasherIcon, CoolantIcon, OilIcon, TPMSIcon, CollisionIcon
|
|
)
|
|
|
|
def __init__(self, x: float, y: float, width: float, height: float):
|
|
self.x = x
|
|
self.y = y
|
|
self.width = width
|
|
self.height = height
|
|
self.surface = cairo.ImageSurface(cairo.FORMAT_ARGB32,int(width), int(height))
|
|
self._redraw = False
|
|
|
|
self.statuses: dict[enum.EnumType, enum.Enum] = dict()
|
|
self.icons: dict[enum.EnumType, StatusIcon] = dict()
|
|
|
|
for icon_type in self.ICONS:
|
|
self.icons[icon_type.TYPE] = icon_type(self.ICON_WIDTH, self.ICON_HEIGHT)
|
|
|
|
def __del__(self):
|
|
self.surface.finish()
|
|
|
|
def draw_bg(self, cr: cairo.Context):
|
|
pass
|
|
|
|
def _update_surface(self):
|
|
cr = cairo.Context(self.surface)
|
|
cr.set_source_rgba(0, 0, 0, 0)
|
|
cr.set_operator(cairo.OPERATOR_SOURCE)
|
|
cr.rectangle(0, 0, self.width, self.height)
|
|
cr.fill()
|
|
|
|
x, y = 0, self.height - self.ICON_HEIGHT
|
|
|
|
for icon_type in self.ICONS:
|
|
typeof = icon_type.TYPE
|
|
status = self.statuses.get(typeof)
|
|
|
|
if status is not None:
|
|
icon = self.icons[typeof]
|
|
|
|
if icon.drawable(status):
|
|
icon.draw(cr, x, y, status)
|
|
|
|
x += self.ICON_WIDTH
|
|
|
|
if x >= self.width:
|
|
x = 0
|
|
y -= self.ICON_HEIGHT
|
|
|
|
def draw_fg(self, cr: cairo.Context):
|
|
if self._redraw:
|
|
self._update_surface()
|
|
self._redraw = False
|
|
|
|
cr.set_source_surface(self.surface, self.x, self.y)
|
|
cr.rectangle(self.x, self.y, self.width, self.height)
|
|
cr.fill()
|
|
|
|
def set(self, status: enum.Enum):
|
|
self.statuses[type(status)] = status
|
|
self._redraw = True
|