Skip to content

Commit f654f23

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 f654f23

27 files changed

Lines changed: 368 additions & 411 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 & 22 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):
@@ -148,8 +150,6 @@ def _update_label(self):
148150
time_remaining = self._get_time_remaining()
149151
is_charging_str = "yes" if self._battery_state.is_charging else "no"
150152
charging_icon = self._get_charging_icon(original_threshold)
151-
152-
# Extended battery info formatting
153153
state = self._battery_state
154154
rate_str = f"{abs(state.rate):.1f}" if state.rate is not None else "N/A"
155155
voltage_str = f"{state.voltage:.2f}" if state.voltage is not None else "N/A"
@@ -161,28 +161,30 @@ def _update_label(self):
161161
health_str = f"{state.health_percent:.1f}" if state.health_percent is not None else "N/A"
162162
chemistry_str = state.chemistry if state.chemistry else "N/A"
163163

164+
active_label_content = active_label_content.format(
165+
percent=str(self._battery_state.percent),
166+
time_remaining=time_remaining,
167+
is_charging=is_charging_str,
168+
icon=charging_icon,
169+
power=rate_str,
170+
voltage=voltage_str,
171+
capacity=capacity_str,
172+
full_capacity=full_capacity_str,
173+
designed_capacity=designed_capacity_str,
174+
temperature=temperature_str,
175+
cycle_count=cycle_count_str,
176+
health=health_str,
177+
chemistry=chemistry_str,
178+
)
179+
label_parts = re.split("(<span.*?>.*?</span>)", active_label_content)
180+
164181
for part in label_parts:
165182
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:
183+
if part and widget_index < len(active_widgets) and isinstance(active_widgets[widget_index], QLabel):
184+
if "<span" in part and "</span>" in part:
183185
# icon-only QLabel
184186
widget_label = active_widgets[widget_index]
185-
icon = re.sub(r"<span.*?>|</span>", "", battery_status).strip()
187+
icon = re.sub(r"<span.*?>|</span>", "", part).strip()
186188
widget_label.setText(icon)
187189
# apply status‐class
188190
existing_classes = widget_label.property("class")
@@ -207,8 +209,7 @@ def _update_label(self):
207209
refresh_widget_style(widget_label)
208210
else:
209211
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)
212+
active_widgets[widget_index].setText(part)
212213
active_widgets[widget_index].setProperty("class", f"label {alt_class} status-{threshold}")
213214
refresh_widget_style(active_widgets[widget_index])
214215
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: 65 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -485,19 +485,40 @@ 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
489+
491490
now = datetime.now(ZoneInfo(self._active_tz)) if self._active_tz else datetime.now().astimezone()
491+
492+
# Finding the datetime format string in the label using keyerror exception.
493+
# This code assumes that there's only 1 time format placeholder in the label.
494+
try:
495+
active_label_content.format(icon="", alarm="", timedata=now)
496+
except KeyError as ke:
497+
# Remove the quotes around the exception message to get the datetime format.
498+
datetime_format = str(ke)[1:-1]
499+
500+
datetime_format_idx = active_label_content.find(datetime_format)
501+
closing_braces_idx = active_label_content.find("}", datetime_format_idx + len(datetime_format))
502+
503+
datetime_format = active_label_content[datetime_format_idx:closing_braces_idx]
504+
active_label_content = active_label_content.replace(
505+
"{" + datetime_format + "}", now.strftime(datetime_format)
506+
)
507+
492508
current_hour = f"{now.hour:02d}"
493509
current_minute = f"{now.minute:02d}"
494510
hour_changed = self._current_hour != current_hour
495511
minute_changed = self._current_minute != current_minute
512+
496513
if hour_changed:
497514
self._current_hour = current_hour
515+
498516
if minute_changed:
499517
self._current_minute = current_minute
500518

519+
has_alarm = self._shared_state._snoozed_alarms or self._has_enabled_alarms()
520+
alarm_state_changed = has_alarm != self._previous_alarm_state
521+
501522
# Temporarily switch locale so strftime outputs are localized
502523
org_locale_time, org_locale_ctype = self._set_locale_context()
503524

@@ -513,75 +534,57 @@ def _update_label(self):
513534
self._timer_label.hide()
514535
self._timer_visible = False
515536

537+
clock_icon = self._get_icon_for_hour(now.hour)
538+
hour_class = f"clock_{current_hour}"
539+
alarm_icon = alarm_class = ""
540+
541+
if self._shared_state._snoozed_alarms:
542+
alarm_class = "icon alarm snooze"
543+
alarm_icon = self.config.alarm_icons.snooze
544+
545+
elif self._has_enabled_alarms():
546+
alarm_icon = self.config.alarm_icons.enabled
547+
alarm_class = "icon alarm"
548+
549+
# The icon place holders are replaced with a span tag of the icon string, with all of its appropriate classes
550+
# assigned to it.
551+
label_content_values = {
552+
"icon": f'<span class="{hour_class}">{clock_icon}</span>',
553+
"alarm": f'<span class="{alarm_class}">{alarm_icon}</span>',
554+
"timedata": now,
555+
}
556+
557+
active_label_content = active_label_content.format_map(label_content_values)
558+
559+
# If any of the icon placeholders are already in a span tag, then flatten the 2 layers of nested span tags, and
560+
# merge their classes into one span tag, before sending them for parsing.
561+
active_label_content = re.sub(
562+
r'<span(?: class=([\'"])(?P<class_name>[\w -]*)\1)?><span class=([\'"])(?P<class_name2>[\w -]*)\3>(?P<label_name>.*?)</span></span>',
563+
r'<span class="\g<class_name> \g<class_name2>">\g<label_name></span>',
564+
active_label_content,
565+
)
566+
567+
label_parts = re.split("(<span.*?>.*?</span>)", active_label_content)
568+
516569
for part in label_parts:
517570
part = part.strip()
518571
if part and widget_index < len(active_widgets) and isinstance(active_widgets[widget_index], QLabel):
519572
if "<span" in part and "</span>" in part:
573+
match = re.search(r'<span class=([\'"])(?P<class_name>[\w -]*)\1>[^>]*</span>', part)
574+
classes = match.group("class_name").strip() or ""
575+
520576
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)
577+
active_widgets[widget_index].setText(icon_placeholder)
578+
active_widgets[widget_index].setProperty("class", classes)
579+
refresh_widget_style(active_widgets[widget_index])
580+
545581
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")
582+
active_widgets[widget_index].setText(part)
583+
if hour_changed or alarm_state_changed:
575584
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
583585
widget_index += 1
584586

587+
self._previous_alarm_state = has_alarm
585588
self._restore_locale_context(org_locale_time, org_locale_ctype)
586589

587590
def _update_tooltip(self):

0 commit comments

Comments
 (0)