Man this refactor has been hurting my head
This commit is contained in:
		
							parent
							
								
									a86461ac6b
								
							
						
					
					
						commit
						dac34a46a2
					
				
					 4 changed files with 329 additions and 141 deletions
				
			
		| 
						 | 
				
			
			@ -11,7 +11,7 @@ from hexagram.speedo  import Speedo
 | 
			
		|||
from hexagram.tacho   import Tacho
 | 
			
		||||
from hexagram.fuel    import FuelGauge
 | 
			
		||||
from hexagram.thermo  import Thermometer
 | 
			
		||||
from hexagram.status  import VehicleStatus, StatusIcon, StatusIconBox
 | 
			
		||||
from hexagram.status  import StatusIconBox, ColdStatus, ColdIcon
 | 
			
		||||
 | 
			
		||||
class ShiftIndicator(Gauge):
 | 
			
		||||
    __slots__ = 'x', 'y', 'rpm_min', 'rpm_redline', 'rpm_max',
 | 
			
		||||
| 
						 | 
				
			
			@ -100,7 +100,6 @@ class AmbientTemp(TextGauge):
 | 
			
		|||
    ICON_WIDTH   = 40
 | 
			
		||||
    ICON_HEIGHT  = 40
 | 
			
		||||
    ICON_PADDING = 16
 | 
			
		||||
    ICON_COLOR   = '#fff'
 | 
			
		||||
 | 
			
		||||
    COLD_THRESHOLD = 4.0 # °C
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -108,12 +107,7 @@ class AmbientTemp(TextGauge):
 | 
			
		|||
        super().__init__(x, y, -100, 100, align)
 | 
			
		||||
 | 
			
		||||
        self.value = 0
 | 
			
		||||
        self.icon  = StatusIcon(VehicleStatus.COLD,
 | 
			
		||||
                                'cold',
 | 
			
		||||
                                self.ICON_WIDTH,
 | 
			
		||||
                                self.ICON_HEIGHT,
 | 
			
		||||
                                self.ICON_COLOR,
 | 
			
		||||
                                'path {stroke: %(s)s;stroke-width: 8}')
 | 
			
		||||
        self.icon  = ColdIcon(self.ICON_WIDTH, self.ICON_HEIGHT)
 | 
			
		||||
 | 
			
		||||
    def format_text(self):
 | 
			
		||||
        return "%.1f°C" % self.value
 | 
			
		||||
| 
						 | 
				
			
			@ -149,7 +143,8 @@ class AmbientTemp(TextGauge):
 | 
			
		|||
 | 
			
		||||
            self.icon.draw(cr,
 | 
			
		||||
                           text_x_offset + width - self.icon.width,
 | 
			
		||||
                        self.y - icon_y_offset)
 | 
			
		||||
                           self.y - icon_y_offset,
 | 
			
		||||
                           ColdStatus.ON)
 | 
			
		||||
 | 
			
		||||
class Clock(TextGauge):
 | 
			
		||||
    def __init__(self, x: float, y: float, align: Align):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										64
									
								
								py/hexagram/icon.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								py/hexagram/icon.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,64 @@
 | 
			
		|||
import os
 | 
			
		||||
import enum
 | 
			
		||||
import cairo
 | 
			
		||||
 | 
			
		||||
from hexagram.svg import render_to_image
 | 
			
		||||
 | 
			
		||||
class Icon():
 | 
			
		||||
    __slots__ = 'name', 'width', 'height', 'style', 'variants', \
 | 
			
		||||
                '_path', '_surfaces',
 | 
			
		||||
 | 
			
		||||
    __dirs__ = ('./icons',
 | 
			
		||||
                '/usr/local/share/hexagram/icons'
 | 
			
		||||
                '/usr/share/hexagram/icons')
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _locate(name: str):
 | 
			
		||||
        for dir in Icon.__dirs__:
 | 
			
		||||
            path = "%s/%s.svg" % (dir, name)
 | 
			
		||||
 | 
			
		||||
            if os.path.exists(path):
 | 
			
		||||
                return path
 | 
			
		||||
 | 
			
		||||
        raise FileNotFoundError(name + '.svg')
 | 
			
		||||
 | 
			
		||||
    STYLE = 'rect,path,circle{stroke:%(s)s;}'
 | 
			
		||||
 | 
			
		||||
    def __init__(self, name: str, width: float, height: float, variants: dict, style: str=STYLE):
 | 
			
		||||
        self.name      = name
 | 
			
		||||
        self.width     = width
 | 
			
		||||
        self.height    = height
 | 
			
		||||
        self.style     = style
 | 
			
		||||
        self.variants  = variants
 | 
			
		||||
        self._path     = Icon._locate(name)
 | 
			
		||||
        self._surfaces: dict[enum.Enum, cairo.ImageSurface] = dict()
 | 
			
		||||
 | 
			
		||||
    def __del__(self):
 | 
			
		||||
        for key in self._surfaces:
 | 
			
		||||
            self._surfaces[key].finish()
 | 
			
		||||
 | 
			
		||||
    def _surface(self, status) -> cairo.ImageSurface:
 | 
			
		||||
        surface = self._surfaces.get(status)
 | 
			
		||||
 | 
			
		||||
        if surface is not None:
 | 
			
		||||
            return surface
 | 
			
		||||
 | 
			
		||||
        color   = self.variants[status]
 | 
			
		||||
        surface = render_to_image(self._path,
 | 
			
		||||
                                  self.width,
 | 
			
		||||
                                  self.height,
 | 
			
		||||
                                  self.style % {'s': color})
 | 
			
		||||
 | 
			
		||||
        self._surfaces[status] = surface
 | 
			
		||||
 | 
			
		||||
        return surface
 | 
			
		||||
 | 
			
		||||
    def drawable(self, status: enum.Enum):
 | 
			
		||||
        return status in self.variants
 | 
			
		||||
 | 
			
		||||
    def draw(self, cr: cairo.Context, x: float, y: float, status):
 | 
			
		||||
        surface = self._surface(status)
 | 
			
		||||
 | 
			
		||||
        cr.set_source_surface(surface, x, y)
 | 
			
		||||
        cr.rectangle(x, y, self.width, self.height)
 | 
			
		||||
        cr.fill()
 | 
			
		||||
| 
						 | 
				
			
			@ -4,147 +4,285 @@ import cairo
 | 
			
		|||
 | 
			
		||||
from typing import Optional
 | 
			
		||||
 | 
			
		||||
from hexagram.svg   import render_to_image
 | 
			
		||||
from hexagram.gauge import Gauge
 | 
			
		||||
from hexagram.icon  import Icon
 | 
			
		||||
 | 
			
		||||
class VehicleStatus(enum.Enum):
 | 
			
		||||
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
 | 
			
		||||
    ABS_FAULT           = 1 <<  0
 | 
			
		||||
    AIRBAG_FAULT        = 1 <<  1
 | 
			
		||||
    BATTERY_FAULT       = 1 <<  2
 | 
			
		||||
    BEAMS_FOG           = 1 <<  3
 | 
			
		||||
    BEAMS_HIGH          = 1 <<  4
 | 
			
		||||
    BEAMS_LOW           = 1 <<  5
 | 
			
		||||
    BEAMS_PARKING       = 1 <<  6
 | 
			
		||||
    BELT                = 1 <<  7
 | 
			
		||||
    CAUTION             = 1 <<  8
 | 
			
		||||
    COLD                = 1 <<  9
 | 
			
		||||
    COLLISION           = 1 << 10
 | 
			
		||||
    COOLANT_LOW         = 1 << 11
 | 
			
		||||
    COOLANT_OVERHEAT    = 1 << 12
 | 
			
		||||
    CRUISE              = 1 << 13
 | 
			
		||||
    FUEL_LOW            = 1 << 14
 | 
			
		||||
    LANEKEEP_OFF        = 1 << 15
 | 
			
		||||
    ENGINE_FAULT        = 1 << 16
 | 
			
		||||
    OIL_LOW             = 1 << 17
 | 
			
		||||
    OIL_OVERHEAT        = 1 << 18
 | 
			
		||||
    PARKING_BRAKE_ON    = 1 << 19
 | 
			
		||||
    PARKING_BRAKE_FAULT = 1 << 20
 | 
			
		||||
    STABILITY_OFF       = 1 << 21
 | 
			
		||||
    TPMS_WARNING        = 1 << 22
 | 
			
		||||
    TPMS_FAULT          = 1 << 23
 | 
			
		||||
    TRACTION_OFF        = 1 << 24
 | 
			
		||||
    TRACTION_FAULT      = 1 << 25
 | 
			
		||||
    WARNING             = 1 << 26
 | 
			
		||||
    WIPER_WASHER_LOW    = 1 << 27
 | 
			
		||||
    DETECTED = 1
 | 
			
		||||
 | 
			
		||||
    __strings__ = {
 | 
			
		||||
        OK: 'OK', ABS_FAULT: "ABS fault", AIRBAG_FAULT: "airbag fault",
 | 
			
		||||
        BATTERY_FAULT: "low battery voltage", BEAMS_FOG: "foglights on",
 | 
			
		||||
        BEAMS_HIGH: "highbeams on", BEAMS_LOW: "lowbeams on",
 | 
			
		||||
        BEAMS_PARKING: "parking beams on", BELT: "warning: fasten seatbelt",
 | 
			
		||||
        CAUTION: "caution", COLLISION: "collision detected", COLD: "outside temperatures cold",
 | 
			
		||||
        COOLANT_LOW: "coolant low", COOLANT_OVERHEAT: "engine overheating",
 | 
			
		||||
        CRUISE: "cruise control on", FUEL_LOW: "fuel low",
 | 
			
		||||
        LANEKEEP_OFF: "lane assist deactivated", ENGINE_FAULT: "check engine",
 | 
			
		||||
        OIL_LOW: "oil level low", OIL_OVERHEAT: "oil overheating",
 | 
			
		||||
        PARKING_BRAKE_ON: "parking brake on", PARKING_BRAKE_FAULT: "parking brake malfunction",
 | 
			
		||||
        STABILITY_OFF: "stability control off", TPMS_WARNING: "check tire pressures",
 | 
			
		||||
        TPMS_FAULT: "tire pressure monitoring system fault",
 | 
			
		||||
        TRACTION_OFF: "traction control off", WARNING: "warning, drive with caution",
 | 
			
		||||
        WIPER_WASHER_LOW: "wiper washer fluid low"
 | 
			
		||||
class CollisionIcon(StatusIcon):
 | 
			
		||||
    TYPE     = CollisionStatus
 | 
			
		||||
    NAME     = 'collision'
 | 
			
		||||
    STYLE    = 'path {stroke-width: 10px; stroke: %(s)s}'
 | 
			
		||||
    VARIANTS = {
 | 
			
		||||
        CollisionStatus.DETECTED: '#f00'
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return self.__strings__.get(self.value, "unknown")
 | 
			
		||||
class FuelStatus(enum.Enum):
 | 
			
		||||
    OK    = 0
 | 
			
		||||
    LOW   = 1
 | 
			
		||||
    FAULT = 2
 | 
			
		||||
 | 
			
		||||
class StatusIcon():
 | 
			
		||||
    __slots__ = 'status', 'name', 'width', 'height', 'color', 'surface',
 | 
			
		||||
class FuelIcon(StatusIcon):
 | 
			
		||||
    TYPE     = FuelStatus
 | 
			
		||||
    NAME     = 'fuel'
 | 
			
		||||
    STYLE    = '#g9 * {stroke: %(s)s} #path9 {fill: %(s)s}'
 | 
			
		||||
    VARIANTS = {
 | 
			
		||||
        FuelStatus.LOW:   '#fa0',
 | 
			
		||||
        FuelStatus.FAULT: '#f00'
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    __dirs__ = ('./icons',
 | 
			
		||||
                '/usr/local/share/hexagram/icons'
 | 
			
		||||
                '/usr/share/hexagram/icons')
 | 
			
		||||
class SeatBeltStatus(enum.Enum):
 | 
			
		||||
    OK         = 0
 | 
			
		||||
    UNFASTENED = 1
 | 
			
		||||
    FAULT      = 2
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _locate(name: str):
 | 
			
		||||
        for dir in StatusIcon.__dirs__:
 | 
			
		||||
            path = "%s/%s.svg" % (dir, name)
 | 
			
		||||
class SeatBeltIcon(StatusIcon):
 | 
			
		||||
    TYPE     = SeatBeltStatus
 | 
			
		||||
    NAME     = 'belt'
 | 
			
		||||
    STYLE    = 'path {fill: %(s)s}'
 | 
			
		||||
    VARIANTS = {
 | 
			
		||||
        SeatBeltStatus.UNFASTENED: '#f00'
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
            if os.path.exists(path):
 | 
			
		||||
                return path
 | 
			
		||||
class CoolantStatus(enum.Enum):
 | 
			
		||||
    OK       = 0
 | 
			
		||||
    LOW      = 1
 | 
			
		||||
    OVERHEAT = 2
 | 
			
		||||
    FAULT    = 3
 | 
			
		||||
 | 
			
		||||
        raise FileNotFoundError(name + '.svg')
 | 
			
		||||
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'
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    def __init__(self, status: VehicleStatus, name: str, width: float, height: float, color: str, style: Optional[str]=None):
 | 
			
		||||
        self.status = status
 | 
			
		||||
        self.name   = name
 | 
			
		||||
        self.width  = width
 | 
			
		||||
        self.height = height
 | 
			
		||||
        self.color  = color
 | 
			
		||||
class OilStatus(enum.Enum):
 | 
			
		||||
    OK       = 0
 | 
			
		||||
    LOW      = 1
 | 
			
		||||
    OVERHEAT = 2
 | 
			
		||||
    FAULT    = 3
 | 
			
		||||
 | 
			
		||||
        if style is None:
 | 
			
		||||
            style = 'rect,path,circle{stroke:%(s)s;}'
 | 
			
		||||
class OilIcon(StatusIcon):
 | 
			
		||||
    TYPE     = OilStatus
 | 
			
		||||
    NAME     = 'oil'
 | 
			
		||||
    STYLE    ='path {fill: %(s)s; stroke: %(s)s}'
 | 
			
		||||
    VARIANTS = {
 | 
			
		||||
        OilStatus.LOW:      '#fa0',
 | 
			
		||||
        OilStatus.OVERHEAT: '#f00'
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        path = StatusIcon._locate(name)
 | 
			
		||||
class ParkingBrakeStatus(enum.Enum):
 | 
			
		||||
    OFF   = 0
 | 
			
		||||
    ON    = 1
 | 
			
		||||
    FAULT = 2
 | 
			
		||||
 | 
			
		||||
        self.surface = render_to_image(path, width, height, style % {'s': color})
 | 
			
		||||
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'
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    def __del__(self):
 | 
			
		||||
        if self.surface is not None:
 | 
			
		||||
            self.surface.finish()
 | 
			
		||||
class WiperWasherStatus(enum.Enum):
 | 
			
		||||
    OK    = 0
 | 
			
		||||
    LOW   = 1
 | 
			
		||||
    FAULT = 2
 | 
			
		||||
 | 
			
		||||
    def draw(self, cr: cairo.Context, x: float, y: float):
 | 
			
		||||
        cr.set_source_surface(self.surface, x, y)
 | 
			
		||||
        cr.rectangle(x, y, self.width, self.height)
 | 
			
		||||
        cr.fill()
 | 
			
		||||
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', 'value', 'surface', 'icons', '_redraw'
 | 
			
		||||
 | 
			
		||||
    __icons__ = (
 | 
			
		||||
        (VehicleStatus.BATTERY_FAULT,       'battery',        '#f00', '#rect5, #rect6 {fill: %(s)s} #rect4, path {stroke: %(s)s}'),
 | 
			
		||||
        (VehicleStatus.BEAMS_FOG,           'beams-fog',      '#0f0', '#g9 path {fill: %(s)s} #g5 path {stroke: %(s)s}'),
 | 
			
		||||
        (VehicleStatus.BEAMS_HIGH,          'beams-high',     '#00f', 'path {fill: %(s)s; stroke: %(s)s}'),
 | 
			
		||||
        (VehicleStatus.BEAMS_LOW,           'beams-low',      '#0f0', 'path {fill: %(s)s; stroke: %(s)s}'),
 | 
			
		||||
        (VehicleStatus.BEAMS_PARKING,       'beams-parking',  '#ff0', 'path {stroke: %(s)s; stroke-width: 12}'),
 | 
			
		||||
        (VehicleStatus.BELT,                'belt',           '#f00', 'path {fill: %(s)s}'),
 | 
			
		||||
        (VehicleStatus.COLLISION,           'collision',      '#f00', 'path {stroke-width: 10px; stroke: %(s)s}'),
 | 
			
		||||
        (VehicleStatus.COOLANT_LOW,         'coolant',        '#fa0', 'path, circle, rect {fill: %(s)s} #g9 path {stroke: %(s)s}'),
 | 
			
		||||
        (VehicleStatus.COOLANT_OVERHEAT,    'coolant',        '#f00', 'path, circle, rect {fill: %(s)s} #g9 path {stroke: %(s)s}'),
 | 
			
		||||
        (VehicleStatus.CRUISE,              'cruise',         '#0f0', 'path, circle {fill: %(s)s; stroke: %(s)s}'),
 | 
			
		||||
        (VehicleStatus.FUEL_LOW,            'fuel',           '#fa0', '#g9 * {stroke: %(s)s} #path9 {fill: %(s)s}'),
 | 
			
		||||
        (VehicleStatus.LANEKEEP_OFF,        'lane-departure', '#fa0', 'path {stroke: %(s)s; stroke-width: 8}'),
 | 
			
		||||
        (VehicleStatus.OIL_LOW,             'oil',            '#fa0', 'path {fill: %(s)s; stroke: %(s)s}'),
 | 
			
		||||
        (VehicleStatus.OIL_OVERHEAT,        'oil',            '#f00', 'path {fill: %(s)s; stroke: %(s)s}'),
 | 
			
		||||
        (VehicleStatus.PARKING_BRAKE_ON,    'parking',        '#fa0', '#path6, #path5 {stroke: %(s)s} circle {stroke: %(s)s} #path7 {fill: %(s)s}'),
 | 
			
		||||
        (VehicleStatus.PARKING_BRAKE_FAULT, 'parking',        '#f00', '#path6, #path5 {stroke: %(s)s} circle {stroke: %(s)s} #path7 {fill: %(s)s}'),
 | 
			
		||||
        (VehicleStatus.STABILITY_OFF,       'stability',      '#fa0', '#path9, #path8, #path7, #path6, #path5 {fill: %(s)s} #path10 {stroke: %(s)s}'),
 | 
			
		||||
        (VehicleStatus.TPMS_WARNING,        'tpms',           '#fa0', 'path, circle {fill: %(s)s}'),
 | 
			
		||||
        (VehicleStatus.WIPER_WASHER_LOW,    'wiper-washer',   '#fa0'),
 | 
			
		||||
    )
 | 
			
		||||
    __slots__ = 'x', 'y', 'width', 'height', 'surface', 'statuses', 'icons', \
 | 
			
		||||
                '_redraw'
 | 
			
		||||
 | 
			
		||||
    ICON_WIDTH  = 48
 | 
			
		||||
    ICON_HEIGHT = 48
 | 
			
		||||
 | 
			
		||||
    ICONS = (
 | 
			
		||||
        CollisionIcon, FuelIcon, SeatBeltIcon, CoolantIcon, OilIcon,
 | 
			
		||||
        ParkingBrakeIcon, WiperWasherIcon, LaneKeepIcon, StabilityControlIcon,
 | 
			
		||||
        TPMSIcon, FogBeamIcon, ParkingBeamIcon, LowBeamIcon, HighBeamIcon
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    def __init__(self, x: float, y: float, width: float, height: float):
 | 
			
		||||
        self.x       = x
 | 
			
		||||
        self.y       = y
 | 
			
		||||
        self.width   = width
 | 
			
		||||
        self.height  = height
 | 
			
		||||
        self.value   = VehicleStatus.OK.value
 | 
			
		||||
        self.surface = cairo.ImageSurface(cairo.FORMAT_ARGB32,int(width), int(height))
 | 
			
		||||
        self._redraw = False
 | 
			
		||||
 | 
			
		||||
        self.icons: dict[int, StatusIcon]  = dict()
 | 
			
		||||
        self.statuses: dict[enum.EnumType, enum.Enum] = dict()
 | 
			
		||||
        self.icons: dict[enum.EnumType, StatusIcon]   = dict()
 | 
			
		||||
 | 
			
		||||
        for item in self.__icons__:
 | 
			
		||||
            status, name, color = item[0:3]
 | 
			
		||||
            style = 'rect,path,circle{stroke:%(s)s;}' if len(item) < 4 else item[3]
 | 
			
		||||
 | 
			
		||||
            self.icons[status.value] = StatusIcon(status, name,
 | 
			
		||||
                                                  self.ICON_WIDTH,
 | 
			
		||||
                                                  self.ICON_HEIGHT,
 | 
			
		||||
                                                  color, style)
 | 
			
		||||
        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()
 | 
			
		||||
| 
						 | 
				
			
			@ -161,11 +299,11 @@ class StatusIconBox(Gauge):
 | 
			
		|||
 | 
			
		||||
        x, y = 0, self.height - self.ICON_HEIGHT
 | 
			
		||||
 | 
			
		||||
        for status_value in self.icons:
 | 
			
		||||
            icon = self.icons[status_value]
 | 
			
		||||
        for typeof in self.statuses:
 | 
			
		||||
            status = self.statuses[typeof]
 | 
			
		||||
            icon   = self.icons[typeof]
 | 
			
		||||
 | 
			
		||||
            if self.value & status_value:
 | 
			
		||||
                if icon is not None:
 | 
			
		||||
            if icon.drawable(status):
 | 
			
		||||
                icon.draw(cr, x, y)
 | 
			
		||||
 | 
			
		||||
                x += self.ICON_WIDTH
 | 
			
		||||
| 
						 | 
				
			
			@ -183,14 +321,5 @@ class StatusIconBox(Gauge):
 | 
			
		|||
        cr.rectangle(self.x, self.y, self.width, self.height)
 | 
			
		||||
        cr.fill()
 | 
			
		||||
 | 
			
		||||
    def reset(self):
 | 
			
		||||
        self.value = VehicleStatus.OK.value
 | 
			
		||||
        self._redraw = True
 | 
			
		||||
 | 
			
		||||
    def set_status(self, status: VehicleStatus):
 | 
			
		||||
        self.value |= status.value
 | 
			
		||||
        self._redraw = True
 | 
			
		||||
 | 
			
		||||
    def clear_status(self, status: VehicleStatus):
 | 
			
		||||
        self.value &= ~status.value
 | 
			
		||||
        self._redraw = True
 | 
			
		||||
    def set(self, status: enum.Enum):
 | 
			
		||||
        self.statuses[type(status)] = status
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,7 +23,7 @@ def render_to_surface(path: str, surface: cairo.Surface, width: float, height: f
 | 
			
		|||
 | 
			
		||||
    svg.render_layer(cr, None, rect)
 | 
			
		||||
 | 
			
		||||
def render_to_image(path: str, width: float, height: float, style: Optional[str]=None) -> cairo.Surface:
 | 
			
		||||
def render_to_image(path: str, width: float, height: float, style: Optional[str]=None) -> cairo.ImageSurface:
 | 
			
		||||
    surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, int(width), int(height))
 | 
			
		||||
 | 
			
		||||
    render_to_surface(path, surface, width, height, style)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue