Skip to content

Commit fef83ca

Browse files
committed
Leveraged str.format and str.format_map methods to substitute values in labels
- Replaced all the instances of str.replace to substitute appropriate data in labels with str.format and str.format_map methods
1 parent a360855 commit fef83ca

27 files changed

Lines changed: 496 additions & 490 deletions

src/core/widgets/glazewm/binding_mode.py

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -73,34 +73,29 @@ def _reload_css(self, label: QLabel):
7373
def _update_label(self):
7474
active_widgets = self._widgets_alt if self._show_alt_label else self._widgets
7575
active_label_content = self._label_alt_content if self._show_alt_label else self._label_content
76+
77+
active_label_content = active_label_content.format(
78+
binding_mode=(
79+
self._active_binding_mode.display_name or self._active_binding_mode.name or self._label_if_no_active
80+
),
81+
icon=self._icons.get(self._active_binding_mode.name or "none", self._default_icon),
82+
)
83+
7684
label_parts = re.split("(<span.*?>.*?</span>)", active_label_content)
77-
label_parts = [part for part in label_parts if part]
7885
widget_index = 0
7986

80-
label_options = {
81-
"{binding_mode}": self._active_binding_mode.display_name
82-
or self._active_binding_mode.name
83-
or self._label_if_no_active,
84-
"{icon}": self._icons.get(self._active_binding_mode.name or "none", self._default_icon),
85-
}
8687
for part in label_parts:
8788
part = part.strip()
8889
if part and widget_index < len(active_widgets) and isinstance(active_widgets[widget_index], QLabel):
89-
formatted_text = part
90-
for option, value in label_options.items():
91-
formatted_text = formatted_text.replace(option, str(value))
9290
if "<span" in part and "</span>" in part:
93-
icon = re.sub(r"<span.*?>|</span>", "", part).strip()
94-
if icon in label_options:
95-
active_widgets[widget_index].setProperty(
96-
"class", f"icon {self._active_binding_mode.name or 'none'}"
97-
)
98-
active_widgets[widget_index].setText(formatted_text)
99-
else:
100-
active_widgets[widget_index].setText(icon)
91+
part = re.sub(r"<span.*?>|</span>", "", part).strip()
92+
active_widgets[widget_index].setProperty(
93+
"class", f"icon {self._active_binding_mode.name or 'none'}"
94+
)
95+
active_widgets[widget_index].setText(part)
10196
else:
10297
if widget_index < len(active_widgets) and isinstance(active_widgets[widget_index], QLabel):
103-
active_widgets[widget_index].setText(formatted_text)
98+
active_widgets[widget_index].setText(part)
10499
if active_widgets[widget_index].property("class") == "label-offline":
105100
active_widgets[widget_index].setProperty("class", "label")
106101
if not self._active_binding_mode.name:

src/core/widgets/yasb/battery.py

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,8 @@ def _update_label(self):
134134
self.timer.stop()
135135
return
136136

137+
label_parts = re.split("(<span.*?>.*?</span>)", active_label_content)
138+
137139
for part in label_parts:
138140
part = part.strip()
139141
if widget_index < len(active_widgets):
@@ -161,28 +163,30 @@ def _update_label(self):
161163
health_str = f"{state.health_percent:.1f}" if state.health_percent is not None else "N/A"
162164
chemistry_str = state.chemistry if state.chemistry else "N/A"
163165

166+
active_label_content = active_label_content.format(
167+
percent=str(self._battery_state.percent),
168+
time_remaining=time_remaining,
169+
is_charging=is_charging_str,
170+
icon=charging_icon,
171+
power=rate_str,
172+
voltage=voltage_str,
173+
capacity=capacity_str,
174+
full_capacity=full_capacity_str,
175+
designed_capacity=designed_capacity_str,
176+
temperature=temperature_str,
177+
cycle_count=cycle_count_str,
178+
health=health_str,
179+
chemistry=chemistry_str,
180+
)
181+
label_parts = re.split("(<span.*?>.*?</span>)", active_label_content)
182+
164183
for part in label_parts:
165184
part = part.strip()
166-
if part and widget_index < len(active_widgets):
167-
battery_status = (
168-
part.replace("{percent}", str(self._battery_state.percent))
169-
.replace("{time_remaining}", time_remaining)
170-
.replace("{is_charging}", is_charging_str)
171-
.replace("{icon}", charging_icon)
172-
.replace("{power}", rate_str)
173-
.replace("{voltage}", voltage_str)
174-
.replace("{capacity}", capacity_str)
175-
.replace("{full_capacity}", full_capacity_str)
176-
.replace("{designed_capacity}", designed_capacity_str)
177-
.replace("{temperature}", temperature_str)
178-
.replace("{cycle_count}", cycle_count_str)
179-
.replace("{health}", health_str)
180-
.replace("{chemistry}", chemistry_str)
181-
)
182-
if "<span" in battery_status and "</span>" in battery_status:
185+
if part and widget_index < len(active_widgets) and isinstance(active_widgets[widget_index], QLabel):
186+
if "<span" in part and "</span>" in part:
183187
# icon-only QLabel
184188
widget_label = active_widgets[widget_index]
185-
icon = re.sub(r"<span.*?>|</span>", "", battery_status).strip()
189+
icon = re.sub(r"<span.*?>|</span>", "", part).strip()
186190
widget_label.setText(icon)
187191
# apply status‐class
188192
existing_classes = widget_label.property("class")
@@ -207,8 +211,7 @@ def _update_label(self):
207211
refresh_widget_style(widget_label)
208212
else:
209213
alt_class = "alt" if self._show_alt_label else ""
210-
formatted_text = battery_status.format(battery_status)
211-
active_widgets[widget_index].setText(formatted_text)
214+
active_widgets[widget_index].setText(part)
212215
active_widgets[widget_index].setProperty("class", f"label {alt_class} status-{threshold}")
213216
refresh_widget_style(active_widgets[widget_index])
214217
widget_index += 1

src/core/widgets/yasb/bluetooth.py

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -268,8 +268,6 @@ def _toggle_label(self):
268268
def _update_label(self, icon, connected_devices=None):
269269
active_widgets = self._widgets_alt if self._show_alt_label else self._widgets
270270
active_label_content = self.config.label_alt if self._show_alt_label else self.config.label
271-
label_parts = re.split("(<span.*?>.*?</span>)", active_label_content)
272-
label_parts = [part for part in label_parts if part]
273271
widget_index = 0
274272

275273
if connected_devices:
@@ -291,26 +289,25 @@ def _update_label(self, icon, connected_devices=None):
291289
device_names = self.config.label_no_device
292290
tooltip_text = self.config.label_no_device
293291

294-
label_options = {
295-
"{icon}": icon,
296-
"{device_name}": device_names,
297-
"{device_count}": len(connected_devices) if connected_devices else 0,
298-
}
292+
active_label_content = active_label_content.format(
293+
icon=icon,
294+
device_name=device_names,
295+
device_count=len(connected_devices) if connected_devices else 0,
296+
)
297+
298+
label_parts = re.split("(<span.*?>.*?</span>)", active_label_content)
299299

300300
for part in label_parts:
301301
part = part.strip()
302302
if part:
303-
formatted_text = part
304-
for option, value in label_options.items():
305-
formatted_text = formatted_text.replace(option, str(value))
306303
if "<span" in part and "</span>" in part:
307304
if widget_index < len(active_widgets) and isinstance(active_widgets[widget_index], QLabel):
308-
active_widgets[widget_index].setText(formatted_text)
305+
active_widgets[widget_index].setText(part)
309306
else:
310-
if self.config.max_length and len(formatted_text) > self.config.max_length:
311-
formatted_text = formatted_text[: self.config.max_length] + self.config.max_length_ellipsis
307+
if self.config.max_length and len(part) > self.config.max_length:
308+
part = part[: self.config.max_length] + self.config.max_length_ellipsis
312309
if widget_index < len(active_widgets) and isinstance(active_widgets[widget_index], QLabel):
313-
active_widgets[widget_index].setText(formatted_text)
310+
active_widgets[widget_index].setText(part)
314311
widget_index += 1
315312

316313
if self.config.tooltip:

src/core/widgets/yasb/brightness.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -148,9 +148,6 @@ def _update_label(self):
148148
"""Update the widget label with current brightness."""
149149
active_widgets = self._widgets_alt if self._show_alt_label else self._widgets
150150
active_label_content = self.config.label_alt if self._show_alt_label else self.config.label
151-
label_parts = re.split("(<span.*?>.*?</span>)", active_label_content)
152-
label_parts = [part for part in label_parts if part]
153-
widget_index = 0
154151

155152
percent = self.current_brightness
156153
if percent is None:
@@ -165,7 +162,9 @@ def _update_label(self):
165162
if self.config.tooltip:
166163
set_tooltip(self, f"Brightness {percent}%")
167164

168-
label_options = {"{icon}": icon, "{percent}": percent}
165+
active_label_content = active_label_content.format(icon=icon, percent=percent)
166+
label_parts = re.split("(<span.*?>.*?</span>)", active_label_content)
167+
widget_index = 0
169168

170169
# Update progress bar
171170
if self.config.progress_bar.enabled and self.progress_widget:
@@ -178,10 +177,7 @@ def _update_label(self):
178177
for part in label_parts:
179178
part = part.strip()
180179
if part and widget_index < len(active_widgets):
181-
formatted_text = part
182-
for option, value in label_options.items():
183-
formatted_text = formatted_text.replace(option, str(value))
184-
active_widgets[widget_index].setText(formatted_text)
180+
active_widgets[widget_index].setText(part)
185181
widget_index += 1
186182

187183
def _get_brightness_icon(self, brightness: int) -> str:

src/core/widgets/yasb/clock.py

Lines changed: 87 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -485,10 +485,27 @@ def _update_label(self):
485485
# Choose which label set to update (primary or alternate)
486486
active_widgets = self._widgets_alt if self._show_alt_label else self._widgets
487487
active_label_content = self._label_alt_content if self._show_alt_label else self._label_content
488-
label_parts = re.split("(<span.*?>.*?</span>)", active_label_content)
489-
label_parts = [part for part in label_parts if part]
490488
widget_index = 0
491489
now = datetime.now(ZoneInfo(self._active_tz)) if self._active_tz else datetime.now().astimezone()
490+
491+
# Finding the datetime format string in the label using keyerror exception.
492+
# This code assumes that there's only 1 time format placeholder in the label.
493+
try:
494+
active_label_content.format(icon="", alarm="", timedata=now)
495+
except KeyError as ke:
496+
# Remove the quotes around the exception message to get the datetime format.
497+
datetime_format = str(ke)[1:-1]
498+
499+
datetime_format_idx = active_label_content.find(datetime_format)
500+
closing_braces_idx = active_label_content.find("}", datetime_format_idx + len(datetime_format))
501+
datetime_format = active_label_content[datetime_format_idx:closing_braces_idx]
502+
503+
active_label_content = (
504+
active_label_content[: datetime_format_idx - 1]
505+
+ now.strftime(datetime_format)
506+
+ active_label_content[closing_braces_idx + 1 :]
507+
)
508+
492509
current_hour = f"{now.hour:02d}"
493510
current_minute = f"{now.minute:02d}"
494511
hour_changed = self._current_hour != current_hour
@@ -498,6 +515,9 @@ def _update_label(self):
498515
if minute_changed:
499516
self._current_minute = current_minute
500517

518+
has_alarm = self._shared_state._snoozed_alarms or self._has_enabled_alarms()
519+
alarm_state_changed = has_alarm != self._previous_alarm_state
520+
501521
# Temporarily switch locale so strftime outputs are localized
502522
org_locale_time, org_locale_ctype = self._set_locale_context()
503523

@@ -513,75 +533,77 @@ def _update_label(self):
513533
self._timer_label.hide()
514534
self._timer_visible = False
515535

536+
clock_icon = self._get_icon_for_hour(now.hour)
537+
hour_class = f"clock_{current_hour}"
538+
alarm_icon = alarm_class = ""
539+
540+
if self._shared_state._snoozed_alarms:
541+
alarm_class = "icon alarm snooze"
542+
alarm_icon = self.config.alarm_icons.snooze
543+
544+
elif self._has_enabled_alarms():
545+
alarm_icon = self.config.alarm_icons.enabled
546+
alarm_class = "icon alarm"
547+
548+
# The icon place holders are replaced with a span tag of the icon string, with all of its appropriate classes
549+
# assigned to it.
550+
label_content_values = {
551+
"icon": f'<span class="{hour_class}">{clock_icon}</span>',
552+
"alarm": f'<span class="{alarm_class}">{alarm_icon}</span>',
553+
"timedata": now,
554+
}
555+
556+
active_label_content = active_label_content.format_map(label_content_values)
557+
558+
# If any of the icon placeholders are already in a span tag, then flatten the 2 layers of nested span tags, and
559+
# merge their classes into one span tag, before sending them for parsing.
560+
active_label_content = re.sub(
561+
r'<span(?: class=([\'"])(?P<class_name>[\w -]*)\1)?><span class=([\'"])(?P<class_name2>[\w -]*)\3>(?P<label_name>.*?)</span></span>',
562+
r'<span class="\g<class_name> \g<class_name2>">\g<label_name></span>',
563+
active_label_content,
564+
)
565+
566+
label_parts = re.split("(<span.*?>.*?</span>)", active_label_content)
567+
516568
for part in label_parts:
517-
part = part.strip()
518-
if part and widget_index < len(active_widgets) and isinstance(active_widgets[widget_index], QLabel):
569+
if not part:
570+
continue
571+
572+
if widget_index < len(active_widgets):
573+
active_widget = active_widgets[widget_index]
574+
else:
575+
active_widget = label = QLabel(part)
576+
label.setProperty("class", "label alt" if self._show_alt_label else "label")
577+
label.setAlignment(Qt.AlignmentFlag.AlignCenter)
578+
label.setCursor(Qt.CursorShape.PointingHandCursor)
579+
content_shadow = self.config.label_shadow.model_dump()
580+
if content_shadow:
581+
add_shadow(label, content_shadow)
582+
self._widget_container_layout.addWidget(label)
583+
active_widgets.append(label)
584+
585+
if isinstance(active_widget, QLabel):
519586
if "<span" in part and "</span>" in part:
587+
match = re.search(r'<span class=([\'"])(?P<class_name>[\w -]*)\1>[^>]*</span>', part)
588+
classes = match.group("class_name").strip() or ""
589+
520590
icon_placeholder = re.sub(r"<span.*?>|</span>", "", part).strip()
521-
if icon_placeholder == "{icon}":
522-
if hour_changed:
523-
icon = self._get_icon_for_hour(now.hour)
524-
active_widgets[widget_index].setText(icon)
525-
hour_class = f"clock_{current_hour}"
526-
active_widgets[widget_index].setProperty("class", f"icon {hour_class}")
527-
refresh_widget_style(active_widgets[widget_index])
528-
elif icon_placeholder == "{alarm}":
529-
if self._shared_state._snoozed_alarms:
530-
active_widgets[widget_index].setText(self.config.alarm_icons.snooze)
531-
active_widgets[widget_index].setProperty("class", "icon alarm snooze")
532-
active_widgets[widget_index].setVisible(True)
533-
refresh_widget_style(active_widgets[widget_index])
534-
elif self._has_enabled_alarms():
535-
active_widgets[widget_index].setText(self.config.alarm_icons.enabled)
536-
active_widgets[widget_index].setProperty("class", "icon alarm")
537-
active_widgets[widget_index].setVisible(True)
538-
refresh_widget_style(active_widgets[widget_index])
539-
else:
540-
active_widgets[widget_index].setText("")
541-
active_widgets[widget_index].setVisible(False)
542-
543-
else:
544-
active_widgets[widget_index].setText(icon_placeholder)
591+
active_widget.setText(icon_placeholder)
592+
active_widget.setProperty("class", classes)
593+
refresh_widget_style(active_widget)
594+
545595
else:
546-
has_alarm = "{alarm}" in part and (self._shared_state._snoozed_alarms or self._has_enabled_alarms())
547-
548-
if "{icon}" in part:
549-
icon = self._get_icon_for_hour(now.hour)
550-
part = part.replace("{icon}", icon)
551-
552-
if "{alarm}" in part:
553-
if self._shared_state._snoozed_alarms:
554-
part = part.replace("{alarm}", self.config.alarm_icons.snooze)
555-
elif self._has_enabled_alarms():
556-
part = part.replace("{alarm}", self.config.alarm_icons.enabled)
557-
else:
558-
part = part.replace("{alarm}", "")
559-
try:
560-
datetime_format_search = re.search(r"\{(.*)}", part)
561-
datetime_format_str = datetime_format_search.group()
562-
datetime_format = datetime_format_search.group(1)
563-
format_label_content = part.replace(datetime_format_str, now.strftime(datetime_format))
564-
except Exception:
565-
format_label_content = part
566-
567-
active_widgets[widget_index].setText(format_label_content)
568-
569-
alarm_state_changed = has_alarm != self._previous_alarm_state
570-
if has_alarm:
571-
if self._shared_state._snoozed_alarms:
572-
active_widgets[widget_index].setProperty("class", "label alarm snooze")
573-
else:
574-
active_widgets[widget_index].setProperty("class", "label alarm")
575-
refresh_widget_style(active_widgets[widget_index])
576-
else:
577-
hour_class = f"clock_{current_hour}"
578-
active_widgets[widget_index].setProperty("class", f"label {hour_class}")
579-
if hour_changed or alarm_state_changed:
580-
refresh_widget_style(active_widgets[widget_index])
581-
582-
self._previous_alarm_state = has_alarm
596+
active_widget.setText(part)
597+
if hour_changed or alarm_state_changed:
598+
refresh_widget_style(active_widget)
599+
active_widget.setVisible(True)
583600
widget_index += 1
584601

602+
while widget_index < len(active_widgets):
603+
active_widgets[widget_index].setVisible(False)
604+
widget_index += 1
605+
606+
self._previous_alarm_state = has_alarm
585607
self._restore_locale_context(org_locale_time, org_locale_ctype)
586608

587609
def _update_tooltip(self):

0 commit comments

Comments
 (0)