156 lines
4.2 KiB
Python
156 lines
4.2 KiB
Python
import enum
|
|
import math
|
|
import cairo
|
|
|
|
from hexagram.gauge import Gauge
|
|
from hexagram.dial import Dial
|
|
from hexagram.trans import Gear, ShiftMode
|
|
|
|
class Tacho(Dial):
|
|
__slots__ = 'redline', 'gear', 'shift_mode',
|
|
|
|
MIN_ANGLE = 232.0 * (math.pi / 180.0)
|
|
MAX_ANGLE = 488.0 * (math.pi / 180.0)
|
|
|
|
FONT_FACE = "Muli"
|
|
|
|
def __init__(self, x: float, y: float, radius: float, redline: float, max_value: float):
|
|
super().__init__(x, y, radius, self.MIN_ANGLE, self.MAX_ANGLE, 0, max_value)
|
|
|
|
self.value = 0
|
|
self.redline = redline
|
|
self.gear: Gear = Gear.N
|
|
self.shift_mode: ShiftMode = ShiftMode.NORMAL
|
|
|
|
def draw_bg(self, cr: cairo.Context):
|
|
super().draw_bg(cr)
|
|
|
|
cr.select_font_face(self.FONT_FACE,
|
|
cairo.FontSlant.ITALIC,
|
|
cairo.FontWeight.BOLD)
|
|
|
|
cr.set_font_size(self.radius * 0.15)
|
|
cr.set_source_rgb(1, 1, 1)
|
|
|
|
for speed in range(0, int(self.max_value)+1, 500):
|
|
min_radius = 0.81 if speed % 1000 == 0 else 0.82
|
|
|
|
if speed >= self.redline:
|
|
cr.set_source_rgb(0.6, 0.1, 0.1)
|
|
|
|
cr.set_line_width(6.0 if speed % 1000 == 0 else 2.0)
|
|
self.draw_mark(cr, min_radius, 0.87, speed)
|
|
|
|
cr.set_source_rgb(1.0, 1.0, 1.0)
|
|
|
|
for speed in range(0, int(self.max_value)+1, 1000):
|
|
if speed >= self.redline:
|
|
cr.set_source_rgb(1.0, 0.1, 0.1)
|
|
|
|
self.draw_legend(cr, 0.675, speed, "%d" % int(speed / 1000))
|
|
|
|
def gear_text(self):
|
|
text = ''
|
|
|
|
if self.gear.value >= Gear.GEAR_1.value:
|
|
if self.shift_mode is ShiftMode.SPORT or self.shift_mode is ShiftMode.RACE:
|
|
text += 'S'
|
|
elif self.shift_mode is ShiftMode.MANUAL:
|
|
text += 'M'
|
|
|
|
text += str(self.gear)
|
|
|
|
return text
|
|
|
|
def draw_fg(self, cr: cairo.Context):
|
|
super().draw_fg(cr)
|
|
|
|
text = self.gear_text()
|
|
|
|
cr.select_font_face("HEX:gon Bold Italic")
|
|
|
|
cr.set_font_size(self.radius * 0.4)
|
|
cr.set_source_rgb(1, 0.4, 1)
|
|
|
|
extents = cr.text_extents(text)
|
|
width = extents[2] - extents[0]
|
|
height = extents[3] - extents[1]
|
|
|
|
cr.move_to(self.x - width / 2,
|
|
self.y + height / 4)
|
|
|
|
cr.show_text(text)
|
|
|
|
class ShiftIndicator(Gauge):
|
|
__slots__ = 'x', 'y', 'rpm_min', 'rpm_redline', 'rpm_max',
|
|
|
|
LIGHT_WIDTH = 48
|
|
LIGHT_HEIGHT = 12
|
|
LIGHT_SPACING = 8
|
|
LIGHT_LOW = 0.25
|
|
|
|
LIGHT_COLORS = (
|
|
(0.0, 1.0, 0.0),
|
|
(0.6, 1.0, 0.0),
|
|
(1.0, 1.0, 0.0),
|
|
(1.0, 0.6, 0.0),
|
|
(1.0, 0.0, 0.0),
|
|
)
|
|
|
|
LIGHT_LEVELS = (
|
|
(0, 8),
|
|
(1, 7),
|
|
(2, 6),
|
|
(3, 5),
|
|
(4,),
|
|
)
|
|
|
|
def __init__(self, x: float, y: float, rpm_min: float, rpm_redline: float, rpm_max: float):
|
|
self.value = 0
|
|
self.x = x
|
|
self.y = y
|
|
self.rpm_min = rpm_min
|
|
self.rpm_redline = rpm_redline
|
|
self.rpm_max = rpm_max
|
|
|
|
def draw_level(self, cr: cairo.Context, level: int, on: bool=False):
|
|
max_level = len(self.LIGHT_LEVELS) - 1
|
|
|
|
if level < 0:
|
|
level = 0
|
|
elif level >= max_level:
|
|
level = max_level
|
|
|
|
r, g, b = self.LIGHT_COLORS[level]
|
|
|
|
if not on:
|
|
r *= self.LIGHT_LOW
|
|
g *= self.LIGHT_LOW
|
|
b *= self.LIGHT_LOW
|
|
|
|
cr.set_source_rgb(r, g, b)
|
|
|
|
for i in self.LIGHT_LEVELS[level]:
|
|
x = i * (self.LIGHT_WIDTH + self.LIGHT_SPACING)
|
|
|
|
cr.rectangle(self.x + x,
|
|
self.y,
|
|
self.LIGHT_WIDTH,
|
|
self.LIGHT_HEIGHT)
|
|
|
|
cr.fill()
|
|
|
|
def draw_bg(self, cr: cairo.Context):
|
|
for level in range(0, len(self.LIGHT_LEVELS)):
|
|
self.draw_level(cr, level)
|
|
|
|
def draw_fg(self, cr: cairo.Context):
|
|
if self.value < self.rpm_min:
|
|
return
|
|
|
|
adj = self.value - self.rpm_min
|
|
adj_max = self.rpm_max - self.rpm_min
|
|
level = int((adj / adj_max) * len(self.LIGHT_LEVELS))
|
|
|
|
for l in range(0, len(self.LIGHT_LEVELS)):
|
|
self.draw_level(cr, l, level >= l)
|