Skip to content

Commit 8e8eee7

Browse files
committed
Add classic theme batch conversion workflow
1 parent bba7ebd commit 8e8eee7

4 files changed

Lines changed: 525 additions & 13 deletions

File tree

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,17 @@ All notable changes to this fork are documented here.
44

55
The format is based on a simple chronological release history.
66

7+
## v0.3.3 - Classic Theme Batch Conversion
8+
9+
- Added batch conversion of classic `theme.yaml` themes into SmartMonitor HID themes.
10+
- Added compatibility-style statuses for classic conversion results: converted, skipped features, unsupported.
11+
- Added classic preview rendering before conversion with a preview-only CLI mode.
12+
- Added JSON and Markdown compatibility reports for batch classic conversion runs.
13+
- Improved `LINE_GRAPH` and `RADIAL` fallbacks for classic themes.
14+
- Added static placeholder handling for unsupported runtime-driven fields such as weather, custom values, swap, and GPU FPS.
15+
- Added a `Classic batch` action to `configure.py` for converting and importing many classic themes at once.
16+
- Fixed the SmartMonitor GUI batch conversion path and synced the same workflow into the GitHub package.
17+
718
## v0.3.2 - SmartMonitor GUI Workflow Refinements
819

920
- Added separate SmartMonitor GUI actions for vendor `.ui` conversion and classic theme conversion.

configure.py

Lines changed: 108 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,13 @@
3434
import webbrowser
3535
import requests
3636
import babel
37+
from typing import Any
3738
from datetime import datetime, timezone
3839
from library.smartmonitor_compile import compile_theme_file
39-
from library.smartmonitor_classic_theme_convert import convert_classic_theme_to_smartmonitor_project
40+
from library.smartmonitor_classic_theme_convert import (
41+
convert_classic_theme_to_smartmonitor_project,
42+
find_classic_theme_files,
43+
)
4044
from library.smartmonitor_ui import encode_ui_file, parse_theme_bundle, resolve_theme_path
4145

4246
try:
@@ -530,6 +534,23 @@ def compile_and_import_classic_theme(theme_name: str, classic_theme_path: str):
530534
return target_img, result
531535

532536

537+
def compile_and_import_classic_theme_batch(classic_root: str) -> tuple[list[str], list[str], list[tuple[str, Any]]]:
538+
imported: list[str] = []
539+
failed: list[str] = []
540+
results: list[tuple[str, Any]] = []
541+
542+
for theme_yaml in find_classic_theme_files(classic_root):
543+
theme_name = sanitize_smartmonitor_theme_name(theme_yaml.parent.name)
544+
try:
545+
_, result = compile_and_import_classic_theme(theme_name, str(theme_yaml))
546+
imported.append(theme_name)
547+
results.append((theme_name, result))
548+
except Exception as exc:
549+
failed.append(f"{theme_yaml.parent.name}: {exc}")
550+
551+
return imported, failed, results
552+
553+
533554
def create_smartmonitor_ui_project(project_dir: str, project_name: str):
534555
project_path = Path(project_dir)
535556
images_dir = project_path / "images"
@@ -1013,12 +1034,24 @@ def __init__(self):
10131034
"A linked editable .ui project is saved for later editing.",
10141035
)
10151036

1037+
self.classic_batch_btn = ttk.Button(
1038+
self.window,
1039+
text="Classic batch",
1040+
command=lambda: self.on_smartmonitor_classic_batch_convert_click(),
1041+
)
1042+
self.classic_batch_btn.place(x=480, y=635, height=42, width=110)
1043+
self.classic_batch_tooltip = ToolTip(
1044+
self.classic_batch_btn,
1045+
msg="Convert every classic theme.yaml found under a folder into\n"
1046+
"SmartMonitor themes and import them into the GUI library.",
1047+
)
1048+
10161049
self.new_ui_btn = ttk.Button(
10171050
self.window,
10181051
text="New UI",
10191052
command=lambda: self.on_new_ui_project_click(),
10201053
)
1021-
self.new_ui_btn.place(x=480, y=635, height=42, width=100)
1054+
self.new_ui_btn.place(x=600, y=635, height=42, width=100)
10221055
self.new_ui_tooltip = ToolTip(
10231056
self.new_ui_btn,
10241057
msg="Create a new starter SmartMonitor UI project with background,\n"
@@ -1052,7 +1085,7 @@ def __init__(self):
10521085
text="Force upload",
10531086
command=lambda: self.on_force_upload_click(),
10541087
)
1055-
self.force_upload_btn.place(x=590, y=635, height=42, width=110)
1088+
self.force_upload_btn.place(x=710, y=635, height=42, width=110)
10561089
self.force_upload_tooltip = ToolTip(
10571090
self.force_upload_btn,
10581091
msg="Upload the currently selected SmartMonitor .dat theme immediately,\n"
@@ -1064,7 +1097,7 @@ def __init__(self):
10641097
text="Doctor",
10651098
command=lambda: self.on_doctor_click(),
10661099
)
1067-
self.doctor_btn.place(x=710, y=635, height=42, width=90)
1100+
self.doctor_btn.place(x=830, y=635, height=42, width=80)
10681101
self.doctor_tooltip = ToolTip(
10691102
self.doctor_btn,
10701103
msg="Run first-launch diagnostics: Python packages, config, themes,\n"
@@ -1107,6 +1140,7 @@ def refresh_theme_selector(self):
11071140
self.edit_ui_btn.config(text="Edit UI", state="normal")
11081141
self.vendor_convert_btn.config(text="Vendor UI", state="normal")
11091142
self.classic_convert_btn.config(text="Classic->DAT", state="normal")
1143+
self.classic_batch_btn.config(text="Classic batch", state="normal")
11101144
self.new_ui_btn.config(text="New UI", state="normal")
11111145
self.force_upload_btn.config(state="normal")
11121146
else:
@@ -1118,6 +1152,7 @@ def refresh_theme_selector(self):
11181152
self.edit_ui_btn.config(text="Edit UI", state="disabled")
11191153
self.vendor_convert_btn.config(text="Vendor UI", state="disabled")
11201154
self.classic_convert_btn.config(text="Classic->DAT", state="disabled")
1155+
self.classic_batch_btn.config(text="Classic batch", state="disabled")
11211156
self.new_ui_btn.config(text="New UI", state="disabled")
11221157
self.force_upload_btn.config(state="disabled")
11231158

@@ -1771,6 +1806,75 @@ def on_smartmonitor_classic_theme_convert_click(self):
17711806
parent=self.window,
17721807
)
17731808

1809+
def on_smartmonitor_classic_batch_convert_click(self):
1810+
batch_root = filedialog.askdirectory(
1811+
parent=self.window,
1812+
title="Choose classic themes root for batch conversion",
1813+
initialdir=THEMES_DIR,
1814+
mustexist=True,
1815+
)
1816+
if not batch_root:
1817+
return
1818+
1819+
overwrite = messagebox.askyesno(
1820+
"Batch convert classic themes?",
1821+
"This will scan the selected folder for every classic theme.yaml,\n"
1822+
"compile each compatible theme to SmartMonitor .dat and overwrite\n"
1823+
"existing themes in the SmartMonitor library if names match.\n\n"
1824+
"Continue?",
1825+
parent=self.window,
1826+
)
1827+
if not overwrite:
1828+
return
1829+
1830+
try:
1831+
imported, failed, results = compile_and_import_classic_theme_batch(batch_root)
1832+
except Exception as exc:
1833+
messagebox.showerror("Classic batch conversion failed", str(exc), parent=self.window)
1834+
return
1835+
1836+
self.refresh_theme_selector()
1837+
if imported:
1838+
self.theme_cb.set(imported[0])
1839+
self.on_theme_change()
1840+
1841+
converted = sum(1 for _, result in results if not result.placeholder_items and not result.skipped_items)
1842+
skipped = len(results) - converted
1843+
1844+
message_lines = [
1845+
f"Imported: {len(imported)}",
1846+
f"Converted cleanly: {converted}",
1847+
f"Imported with placeholders/skips: {skipped}",
1848+
f"Failed: {len(failed)}",
1849+
]
1850+
1851+
if imported:
1852+
preview = ", ".join(imported[:6])
1853+
if len(imported) > 6:
1854+
preview += ", ..."
1855+
message_lines.append(f"Themes: {preview}")
1856+
1857+
skipped_entries = [
1858+
f"{theme_name}: placeholders={len(result.placeholder_items)}, skipped={len(result.skipped_items)}"
1859+
for theme_name, result in results
1860+
if result.placeholder_items or result.skipped_items
1861+
]
1862+
if skipped_entries:
1863+
message_lines.append("")
1864+
message_lines.append("Themes with placeholders/skipped features:")
1865+
message_lines.extend(skipped_entries[:8])
1866+
if len(skipped_entries) > 8:
1867+
message_lines.append("...")
1868+
1869+
if failed:
1870+
message_lines.append("")
1871+
message_lines.append("Errors:")
1872+
message_lines.extend(failed[:8])
1873+
if len(failed) > 8:
1874+
message_lines.append("...")
1875+
1876+
messagebox.showinfo("Classic batch conversion", "\n".join(message_lines), parent=self.window)
1877+
17741878
def on_smartmonitor_new_ui_project_click(self):
17751879
os.makedirs(DEFAULT_SMARTMONITOR_PROJECTS_DIR, exist_ok=True)
17761880
parent_dir = filedialog.askdirectory(

0 commit comments

Comments
 (0)