Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions configfileparser.py
Original file line number Diff line number Diff line change
Expand Up @@ -327,11 +327,27 @@ def get_linear_section(self, config_file, section, meter_type):
d[STEP_WIDTH_OVERLOAD] = config_file[section].getint(STEP_WIDTH_OVERLOAD, 0)
d[DIRECTION] = config_file[section].get(DIRECTION, None)
d[INDICATOR_TYPE] = config_file[section].get(INDICATOR_TYPE, None)
d[FLIP_LEFT_X] = config_file[section].get(FLIP_LEFT_X, None)
d[FLIP_RIGHT_X] = config_file[section].get(FLIP_RIGHT_X, None)
# Same boolean semantics as left.needle.flip / right.needle.flip.
# Raw .get() returned strings, so flip.*.x = False was truthy in Python.
d[FLIP_LEFT_X] = self.get_optional_boolean(config_file, section, FLIP_LEFT_X)
d[FLIP_RIGHT_X] = self.get_optional_boolean(config_file, section, FLIP_RIGHT_X)

return d

def get_optional_boolean(self, config_file, section, key):
"""Parse an optional meters.txt boolean.

Missing or empty => False.
Accepts ConfigParser booleans and common True/False spellings.
"""
raw = config_file[section].get(key, None)
if raw is None or str(raw).strip() == "":
return False
try:
return config_file[section].getboolean(key)
except ValueError:
return str(raw).strip().lower() in ("1", "true", "yes", "on")

def get_circular_section(self, config_file, section, meter_type):
""" Parser for circular meter

Expand Down
53 changes: 44 additions & 9 deletions linear.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,18 +52,53 @@ def __init__(self, data_source, components, base, ui_refresh_period, direction,

if direction == None:
self.direction = DIRECTION_LEFT_RIGHT

if flip_right_x:

# Left and right channels often share one cached indicator Surface.
# Flip through a detached copy, then convert_alpha so the result stays
# blit-safe across pygame/SDL backends (some desktop drivers otherwise
# draw a flipped indicator as empty / invisible).
if self.as_bool(flip_right_x):
self.right_origin_x = self.components[2].content_x
self.components[2].content = (self.components[2].content[0], pygame.transform.flip(self.components[2].content[1], True, False))
self.components[2].content = (
self.components[2].content[0],
self.flip_x(self.components[2].content[1]),
)

if flip_left_x:
self.components[1].content = (self.components[1].content[0], pygame.transform.flip(self.components[1].content[1], True, False))
if self.as_bool(flip_left_x):
self.components[1].content = (
self.components[1].content[0],
self.flip_x(self.components[1].content[1]),
)

# previous_rect_* is screen-space dirty area for draw_bgr_fgr.
# reset_mask() leaves bounding_box in image-local coords (blit source);
# copying that here seeds (0,0) and the first volume update unions a
# huge wipe that can erase other layers (e.g. vinyl/cdart under meters).
self.previous_rect_left = pygame.Rect(
self.components[1].content_x, self.components[1].content_y, 1, 1)
self.previous_rect_right = pygame.Rect(
self.components[2].content_x, self.components[2].content_y, 1, 1)
self.previous_volume_left = self.previous_volume_right = 0.0

@staticmethod
def as_bool(value):
"""Normalize flip flags (bool or meters.txt-style string)."""
if isinstance(value, bool):
return value
if value is None:
return False
return str(value).strip().lower() in ("1", "true", "yes", "on")

@staticmethod
def flip_x(surface):
"""Horizontal flip that does not mutate a shared cached surface."""
src = surface.copy() if hasattr(surface, "copy") else surface
flipped = pygame.transform.flip(src, True, False)
try:
return flipped.convert_alpha()
except Exception:
return flipped

self.previous_rect_left = self.components[1].bounding_box.copy()
self.previous_rect_right = self.components[2].bounding_box.copy()
self.previous_volume_left = self.previous_volume_right = 0.0

def run(self):
""" Converts volume value into the mask width and displays corresponding mask.

Expand Down
20 changes: 14 additions & 6 deletions meter.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ def __init__(self, util, meter_type, meter_parameters, data_source):
self.meter_y = meter_parameters[METER_Y]
self.direction = meter_parameters.get(DIRECTION)
self.indicator_type = meter_parameters.get(INDICATOR_TYPE, None)
self.flip_left_x = meter_parameters.get(FLIP_LEFT_X, None)
self.flip_right_x = meter_parameters.get(FLIP_RIGHT_X, None)
self.flip_left_x = meter_parameters.get(FLIP_LEFT_X, False)
self.flip_right_x = meter_parameters.get(FLIP_RIGHT_X, False)

def add_background(self, image_name, meter_x, meter_y):
""" Position and add background image.
Expand Down Expand Up @@ -220,14 +220,22 @@ def reset_bgr_fgr(self, comp):
comp.content_y = self.origin_y

def reset_mask(self, comp):
""" Initialize linear mask. """

comp.bounding_box.x = comp.content_x
comp.bounding_box.y = comp.content_y
"""Initialize linear mask source rectangle in image-local coordinates.

Component.draw() passes bounding_box as the blit source rect into the
indicator image. Using screen coordinates here selects pixels outside
the image and can make the indicator appear missing until the first
volume update rewrites the rect. LinearAnimator must seed its
screen-space previous_rect from content_x/content_y, not from this box.
"""
w, h = comp.content[1].get_size()
comp.bounding_box.x = 0
comp.bounding_box.y = 0
if w > h:
comp.bounding_box.w = 1
comp.bounding_box.h = h
else:
comp.bounding_box.w = w
comp.bounding_box.h = 1

def stop(self):
Expand Down