From 8138d2d97941fbc8b96046483cbdcfa6e24b687e Mon Sep 17 00:00:00 2001 From: worldpeace-germany Date: Thu, 15 Aug 2024 19:29:22 +0200 Subject: [PATCH 1/5] segement_display_player default for flashing changed from not_set to off --- mpf/config_spec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mpf/config_spec.yaml b/mpf/config_spec.yaml index ae98b7742..c163a9054 100644 --- a/mpf/config_spec.yaml +++ b/mpf/config_spec.yaml @@ -1498,7 +1498,7 @@ segment_display_player: action: single|enum(add,remove,flash,no_flash,flash_match,flash_mask,set_color)|add transition: ignore transition_out: ignore - flashing: single|enum(off,all,match,mask,not_set)|not_set + flashing: single|enum(off,all,match,mask,not_set)|off flash_mask: single|str|None key: single|str|None expire: single|ms_or_token|None From 0cb6e3cf7b44153ac9ebd78907bb2839a0d21d24 Mon Sep 17 00:00:00 2001 From: worldpeace-germany Date: Thu, 15 Aug 2024 19:49:35 +0200 Subject: [PATCH 2/5] Initial implementation for update_method replace in segment_display --- .../segment_display/segment_display.py | 63 ++++++++++++++++--- 1 file changed, 56 insertions(+), 7 deletions(-) diff --git a/mpf/devices/segment_display/segment_display.py b/mpf/devices/segment_display/segment_display.py index 98a8c4344..6fe852d72 100644 --- a/mpf/devices/segment_display/segment_display.py +++ b/mpf/devices/segment_display/segment_display.py @@ -51,7 +51,7 @@ class SegmentDisplay(SystemWideDevice): __slots__ = ["hw_display", "size", "virtual_connector", "_text_stack", "_current_placeholder", "_current_text_stack_entry", "_transition_update_task", "_current_transition", "_default_color", - "_current_state", "_current_placeholder_future"] + "_current_state", "_current_placeholder_future", "_previous_text", "_previous_color", "_previous_transition_out"] config_section = 'segment_displays' collection = 'segment_displays' @@ -73,6 +73,9 @@ def __init__(self, machine, name: str) -> None: self._current_transition = None # type: Optional[TransitionRunner] self._default_color = None # type: Optional[RGBColor] self._current_state = None # type: Optional[SegmentDisplayState] + self._previous_text = None # Last text entry for transitions if update_method = replace + self._previous_color = None # Last color for transitions if update_method = replace + self._previous_transition_out = None # Last transistion_out if update_method = replace async def _initialize(self): """Initialize display.""" @@ -155,11 +158,57 @@ def add_text_entry(self, text, color, flashing, flash_mask, transition, transiti raise ValueError(f"Unknown update_method '{self.config['update_method']}' for segment display {self.name}") # For the replace-text update method, skip the stack and write straight to the display - new_text = TextTemplate(self.machine, text).evaluate({}) - text = SegmentDisplayText.from_str(new_text, self.size, self.config['integrated_dots'], - self.config['integrated_commas'], self.config['use_dots_for_commas'], - color) - self._update_display(SegmentDisplayState(text, flashing, flash_mask)) + + ############################### + #Store current color and text as previous text/color of next run even if no transition in this step, + #the next step might have a transition, that the old text/color needs to be included into that transition + ############################### + + # Handle new and previous text + if self._previous_text: + previous_text = self._previous_text + else: + previous_text = "" + self._previous_text = text # Save the new text as the next previous text + + # Handle new and previous color + if self._previous_color: + previous_color = self._previous_color + else: + previous_color = self._default_color + self._previous_color = color # Save the new color as the next previous color + + if transition or self._previous_transition_out: + if transition: #if transition exists, then ignore transition_out of previous text/color + transition_conf = TransitionManager.get_transition(self.size, + self.config['integrated_dots'], + self.config['integrated_commas'], + self.config['use_dots_for_commas'], + transition) + elif self._previous_transition_out: + transition_conf = TransitionManager.get_transition(self.size, + self.config['integrated_dots'], + self.config['integrated_commas'], + self.config['use_dots_for_commas'], + self._previous_transition_out) + if transition_out: #in case transition_out is set we need to preserve it for the next step but only after the previous transition_out is in this step's config + self._previous_transition_out = transition_out + + #start transition + self._start_transition(transition_conf, previous_text, text, + previous_color, color, + self.config['default_transition_update_hz'], flashing, flash_mask) + + else: #No transition configured + if len(color) == 0: #no color set in show, case handled in transition, so extra treatment here for no transition + color = self._default_color + if transition_out: #in case transition_out is set we need to preserve it for the next step + self._previous_transition_out = transition_out + new_text = TextTemplate(self.machine, text).evaluate({}) + text = SegmentDisplayText.from_str(new_text, self.size, self.config['integrated_dots'], + self.config['integrated_commas'], self.config['use_dots_for_commas'], + color) + self._update_display(SegmentDisplayState(text, flashing, flash_mask)) def add_text(self, text: str, priority: int = 0, key: str = None) -> None: """Add text to display stack. @@ -171,7 +220,7 @@ def add_text(self, text: str, priority: int = 0, key: str = None) -> None: def remove_text_by_key(self, key: Optional[str]): """Remove entry from text stack.""" if self.config['update_method'] != "stack": - self.info_log("Segment display 'remove' action is TBD.") + self.add_text_entry("", self._previous_color, FlashingType.NO_FLASH, "", None, None, 100, key) return if key in self._text_stack: From 06b04cb59aebc92e51fc85d5e94e62a316b27a65 Mon Sep 17 00:00:00 2001 From: worldpeace-germany Date: Wed, 28 Aug 2024 19:03:48 +0200 Subject: [PATCH 3/5] fixed transition_out issue if the next step doesnt have a transition_out --- mpf/devices/segment_display/segment_display.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mpf/devices/segment_display/segment_display.py b/mpf/devices/segment_display/segment_display.py index 6fe852d72..6d6f33423 100644 --- a/mpf/devices/segment_display/segment_display.py +++ b/mpf/devices/segment_display/segment_display.py @@ -191,6 +191,7 @@ def add_text_entry(self, text, color, flashing, flash_mask, transition, transiti self.config['integrated_commas'], self.config['use_dots_for_commas'], self._previous_transition_out) + self._previous_transition_out = None # Once the transistion_out is played removed it that is not played in the next step again if transition_out: #in case transition_out is set we need to preserve it for the next step but only after the previous transition_out is in this step's config self._previous_transition_out = transition_out From ae93e49173dcee4bab6d4b570d950b983ba4925c Mon Sep 17 00:00:00 2001 From: worldpeace-germany Date: Sat, 31 Aug 2024 10:30:04 +0200 Subject: [PATCH 4/5] fixed and clean up of color handling in segment displays --- .../segment_display/segment_display.py | 12 ++++--- .../segment_display/segment_display_text.py | 34 ++++++++++++------- 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/mpf/devices/segment_display/segment_display.py b/mpf/devices/segment_display/segment_display.py index 6d6f33423..1ac59a983 100644 --- a/mpf/devices/segment_display/segment_display.py +++ b/mpf/devices/segment_display/segment_display.py @@ -148,7 +148,14 @@ def add_text_entry(self, text, color, flashing, flash_mask, transition, transiti This will replace texts with the same key. """ + + if len(color) == 0: + color = self._current_state.text.get_colors() + + if self.config['update_method'] == "stack": + + self._text_stack[key] = TextStackEntry( text, color, flashing, flash_mask, transition, transition_out, priority, key) self._update_stack() @@ -201,8 +208,6 @@ def add_text_entry(self, text, color, flashing, flash_mask, transition, transiti self.config['default_transition_update_hz'], flashing, flash_mask) else: #No transition configured - if len(color) == 0: #no color set in show, case handled in transition, so extra treatment here for no transition - color = self._default_color if transition_out: #in case transition_out is set we need to preserve it for the next step self._previous_transition_out = transition_out new_text = TextTemplate(self.machine, text).evaluate({}) @@ -232,9 +237,8 @@ def remove_text_by_key(self, key: Optional[str]): def _start_transition(self, transition: TransitionBase, current_text: str, new_text: str, current_colors: List[RGBColor], new_colors: List[RGBColor], update_hz: float, flashing, flash_mask): + """Start the specified transition.""" - current_colors = self._expand_colors(current_colors, len(current_text)) - new_colors = self._expand_colors(new_colors, len(new_text)) if self._current_transition: self._stop_transition() self._current_transition = TransitionRunner(self.machine, transition, current_text, new_text, diff --git a/mpf/devices/segment_display/segment_display_text.py b/mpf/devices/segment_display/segment_display_text.py index 8ef8630c5..c37c9a572 100644 --- a/mpf/devices/segment_display/segment_display_text.py +++ b/mpf/devices/segment_display/segment_display_text.py @@ -76,28 +76,36 @@ def _create_characters(cls, text: str, display_size: int, collapse_dots: bool, c """Create characters from text and color them. - Colors are used from the left to the right (starting with the first character). - - If colors are shorter than text the last color is repeated for text. - - The first color is used to pad the text to the left if text is shorter than the display - thus text is right - aligned. - Dots and commas are embedded on the fly. + - Text will be right aligned on the display, thus if text is shorter than display spaces will be padded before the text + - Provided colors are assumed to be for the text itself, thus the list of colors is "right aligned" too + - If list of colors is less than the display size the list will be extended with white as default color on the left endswith + so that the provided colors are for the text and not being used for invisible spaces """ char_list = [] - left_pad_color = colors[0] if colors else None - default_right_color = colors[len(colors) - 1] if colors else None uncolored_chars = cls._embed_dots_and_commas(text, collapse_dots, collapse_commas, use_dots_for_commas) - colors = colors[-len(uncolored_chars):] - for char_code, char_has_dot, char_has_comma in uncolored_chars: - color = colors.pop(0) if colors else default_right_color - char_list.append(DisplayCharacter(char_code, char_has_dot, char_has_comma, color)) - # ensure list is the same size as the segment display (cut off on left or right justify characters) - current_length = len(char_list) + # Adujust the color array if needed + color_length = len(colors) + if color_length > display_size: + for _ in range(color_length - display_size): + colors.pop(0) # remove very left color of array if too long + elif color_length < display_size: + for _ in range(display_size - color_length): + colors.append(RGBColor("white")) # add default color on the left of the array if too few colors + + # ensure list is the same size as the segment display (cut off on left if too long or right justify characters if too short) + current_length = len(uncolored_chars) if current_length > display_size: for _ in range(current_length - display_size): - char_list.pop(0) + uncolored_chars.pop(0) # remove very left char of array if too long elif current_length < display_size: for _ in range(display_size - current_length): - char_list.insert(0, DisplayCharacter(SPACE_CODE, False, False, left_pad_color)) + uncolored_chars.insert(0, (SPACE_CODE, False, False)) + + for _ in range(len(uncolored_chars)): + color = colors[_] + char_list.append(DisplayCharacter(uncolored_chars[_][0], uncolored_chars[_][1], uncolored_chars[_][2], color)) return char_list From 875beeaa4f51100ab2a6096d06d9611bd32a1e65 Mon Sep 17 00:00:00 2001 From: worldpeace-germany Date: Tue, 17 Sep 2024 20:47:49 +0200 Subject: [PATCH 5/5] mainly feedback from last PR, plus improved clear context --- mpf/config_players/segment_display_player.py | 23 ++++-- .../segment_display/segment_display.py | 70 ++++++++++--------- .../segment_display/segment_display_text.py | 24 +++---- 3 files changed, 63 insertions(+), 54 deletions(-) diff --git a/mpf/config_players/segment_display_player.py b/mpf/config_players/segment_display_player.py index 9862e8a2c..a574a9b87 100644 --- a/mpf/config_players/segment_display_player.py +++ b/mpf/config_players/segment_display_player.py @@ -106,12 +106,23 @@ def _remove(self, instance_dict, key, display): del instance_dict[display][key] def clear_context(self, context): - """Remove all texts.""" - instance_dict = self._get_instance_dict(context) - for display, keys in instance_dict.items(): - for key in dict(keys).keys(): - self._remove(instance_dict=instance_dict, - key=key, display=display) + + ############## + # Remove all texts. Ignore what keys are available, that will be checked later in the segment display code. + # Especially important for update_method replace since there are no keys. + ############## + + instance_dict = self._get_instance_dict(context) # key of the dict is the display, the value is another dict + + for display, keys_dict in instance_dict.items(): # keys_dict key is the show key, the value is a boolean (with yet unknown usage) + if(keys_dict): #depending on the situation the keys_dict might be empty, still need to clear the display + for key in dict(keys_dict).keys(): + display.clear_segment_display(key) + if instance_dict[display][key] is not True: + self.delay.remove(instance_dict[display][key]) + del instance_dict[display][key] + else: + display.clear_segment_display(None) self._reset_instance_dict(context) diff --git a/mpf/devices/segment_display/segment_display.py b/mpf/devices/segment_display/segment_display.py index 1ac59a983..1c4371d42 100644 --- a/mpf/devices/segment_display/segment_display.py +++ b/mpf/devices/segment_display/segment_display.py @@ -89,8 +89,14 @@ async def _initialize(self): self.size = self.config['size'] self._default_color = [RGBColor(color) for color in self.config["default_color"][0:self.size]] - if len(self._default_color) < self.size: - self._default_color += [RGBColor("white")] * (self.size - len(self._default_color)) + + if (len(self._default_color)) == 1: + self._default_color = self._default_color * self.size + elif len(self._default_color) != self.size: + self.warning_log("The amount of colors you specified for your text has to be either equal to " + "the amount of digits in your display or equals 1. Your display has a size of %s and the " + "amount of colors specified is %s. All display colors will be set to white.", self.size, len(self._default_color)) + self._default_color = [RGBColor("white")] * self.size # configure hardware try: @@ -148,11 +154,9 @@ def add_text_entry(self, text, color, flashing, flash_mask, transition, transiti This will replace texts with the same key. """ - - if len(color) == 0: + if not color: color = self._current_state.text.get_colors() - if self.config['update_method'] == "stack": @@ -172,35 +176,19 @@ def add_text_entry(self, text, color, flashing, flash_mask, transition, transiti ############################### # Handle new and previous text - if self._previous_text: - previous_text = self._previous_text - else: - previous_text = "" + previous_text = self._previous_text or "" self._previous_text = text # Save the new text as the next previous text # Handle new and previous color - if self._previous_color: - previous_color = self._previous_color - else: - previous_color = self._default_color + previous_color = self._previous_color or self._default_color self._previous_color = color # Save the new color as the next previous color if transition or self._previous_transition_out: - if transition: #if transition exists, then ignore transition_out of previous text/color - transition_conf = TransitionManager.get_transition(self.size, - self.config['integrated_dots'], - self.config['integrated_commas'], - self.config['use_dots_for_commas'], - transition) - elif self._previous_transition_out: - transition_conf = TransitionManager.get_transition(self.size, - self.config['integrated_dots'], - self.config['integrated_commas'], - self.config['use_dots_for_commas'], - self._previous_transition_out) - self._previous_transition_out = None # Once the transistion_out is played removed it that is not played in the next step again - if transition_out: #in case transition_out is set we need to preserve it for the next step but only after the previous transition_out is in this step's config - self._previous_transition_out = transition_out + transition_conf = TransitionManager.get_transition(self.size, + self.config['integrated_dots'], + self.config['integrated_commas'], + self.config['use_dots_for_commas'], + transition or self._previous_transition_out) #start transition self._start_transition(transition_conf, previous_text, text, @@ -216,6 +204,12 @@ def add_text_entry(self, text, color, flashing, flash_mask, transition, transiti color) self._update_display(SegmentDisplayState(text, flashing, flash_mask)) + ############################### + # Once the transistion_out is played, removed it that is not played in the next step again, but in case transition_out is set in the current step + # then we need to preserve it for the next step but only after the previous step's transition_out is in this step's config (or the transition of the current step) + ############################### + self._previous_transition_out = transition_out or None + def add_text(self, text: str, priority: int = 0, key: str = None) -> None: """Add text to display stack. @@ -225,13 +219,23 @@ def add_text(self, text: str, priority: int = 0, key: str = None) -> None: def remove_text_by_key(self, key: Optional[str]): """Remove entry from text stack.""" - if self.config['update_method'] != "stack": + if self.config['update_method'] == "stack": + if key in self._text_stack: + del self._text_stack[key] + self._update_stack() + else: # must be update_method replace, send empyt text since no key in that case self.add_text_entry("", self._previous_color, FlashingType.NO_FLASH, "", None, None, 100, key) - return - if key in self._text_stack: - del self._text_stack[key] - self._update_stack() + + def clear_segment_display(self, key: Optional[str]): + """Clear segment dispaly if context is removed from player.""" + + if self.config['update_method'] == "replace": + self._stop_transition() + self._previous_transition_out = None + + self.remove_text_by_key(key) + # pylint: disable=too-many-arguments def _start_transition(self, transition: TransitionBase, current_text: str, new_text: str, diff --git a/mpf/devices/segment_display/segment_display_text.py b/mpf/devices/segment_display/segment_display_text.py index c37c9a572..60b28dc64 100644 --- a/mpf/devices/segment_display/segment_display_text.py +++ b/mpf/devices/segment_display/segment_display_text.py @@ -74,25 +74,19 @@ def _embed_dots_and_commas(cls, text: str, collapse_dots: bool, collapse_commas: def _create_characters(cls, text: str, display_size: int, collapse_dots: bool, collapse_commas: bool, use_dots_for_commas: bool, colors: List[Optional[RGBColor]]) -> List[DisplayCharacter]: """Create characters from text and color them. - - - Colors are used from the left to the right (starting with the first character). - Dots and commas are embedded on the fly. - Text will be right aligned on the display, thus if text is shorter than display spaces will be padded before the text - - Provided colors are assumed to be for the text itself, thus the list of colors is "right aligned" too - - If list of colors is less than the display size the list will be extended with white as default color on the left endswith - so that the provided colors are for the text and not being used for invisible spaces + - If list of colors is less than the display size then all white will be used, if only one color is given that will be used for the full display """ char_list = [] uncolored_chars = cls._embed_dots_and_commas(text, collapse_dots, collapse_commas, use_dots_for_commas) # Adujust the color array if needed - color_length = len(colors) - if color_length > display_size: - for _ in range(color_length - display_size): - colors.pop(0) # remove very left color of array if too long - elif color_length < display_size: - for _ in range(display_size - color_length): - colors.append(RGBColor("white")) # add default color on the left of the array if too few colors + if (len(colors)) == 1: + colors = colors * display_size + elif len(colors) != display_size: + #TODO: Log that colors were adjusted to white as default + colors = [RGBColor("white")] * display_size # ensure list is the same size as the segment display (cut off on left if too long or right justify characters if too short) current_length = len(uncolored_chars) @@ -103,9 +97,9 @@ def _create_characters(cls, text: str, display_size: int, collapse_dots: bool, c for _ in range(display_size - current_length): uncolored_chars.insert(0, (SPACE_CODE, False, False)) - for _ in range(len(uncolored_chars)): - color = colors[_] - char_list.append(DisplayCharacter(uncolored_chars[_][0], uncolored_chars[_][1], uncolored_chars[_][2], color)) + for i, char in enumerate(uncolored_chars): + color = colors[i] + char_list.append(DisplayCharacter(char[0], char[1], char[2], color)) #0: char code 1: char_has_dot 2: char_has_comma return char_list