Skip to content

Commit 34b49b6

Browse files
committed
Add classic theme converter workflow
1 parent 2f4337e commit 34b49b6

4 files changed

Lines changed: 1040 additions & 28 deletions

File tree

configure.py

Lines changed: 152 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import babel
3737
from datetime import datetime, timezone
3838
from library.smartmonitor_compile import compile_theme_file
39+
from library.smartmonitor_classic_theme_convert import convert_classic_theme_to_smartmonitor_project
3940
from library.smartmonitor_ui import encode_ui_file
4041

4142
try:
@@ -52,8 +53,8 @@
5253
from tktooltip import ToolTip
5354
except Exception as e:
5455
print("""Import error: %s
55-
Please see README.md / README_RU.md in the repository root for installation steps.
56-
If the GUI still does not start, check repository issues or discussions.""" % str(
56+
Please follow start guide to install required packages: https://github.com/mathoudebine/turing-smart-screen-python/wiki/System-monitor-:-how-to-start
57+
Or the troubleshooting page: https://github.com/mathoudebine/turing-smart-screen-python/wiki/Troubleshooting#all-os-tkinter-dependency-not-installed""" % str(
5758
e))
5859
try:
5960
sys.exit(0)
@@ -137,7 +138,7 @@
137138
DEFAULT_SMARTMONITOR_VENDOR_THEME_ROOT = (
138139
os.path.join(MAIN_DIRECTORY, "vendor", "themefor3.5")
139140
if os.path.isdir(os.path.join(MAIN_DIRECTORY, "vendor", "themefor3.5"))
140-
else os.path.join(MAIN_DIRECTORY, "vendor", "themefor3.5")
141+
else "/tmp/smartmonitor_unpacked/app/themefor3.5"
141142
)
142143
DEFAULT_SMARTMONITOR_PROJECTS_DIR = os.path.join(MAIN_DIRECTORY, "res", "smartmonitor", "projects")
143144

@@ -350,6 +351,10 @@ def autostart_service_path() -> Path:
350351
return autostart_service_dir() / AUTOSTART_SERVICE_NAME
351352

352353

354+
def autostart_service_template_path() -> Path:
355+
return Path(MAIN_DIRECTORY) / "tools" / AUTOSTART_SERVICE_NAME
356+
357+
353358
def render_autostart_service() -> str:
354359
python_exec = Path(sys.executable).resolve()
355360
main_script = Path(MAIN_DIRECTORY) / "main.py"
@@ -488,6 +493,43 @@ def compile_and_import_smartmonitor_ui(theme_name: str, ui_path: str):
488493
return target_img
489494

490495

496+
def compile_and_import_classic_theme(theme_name: str, classic_theme_path: str):
497+
project_root = Path(DEFAULT_SMARTMONITOR_PROJECTS_DIR) / "classic-converted"
498+
result = convert_classic_theme_to_smartmonitor_project(
499+
classic_theme_path,
500+
project_root,
501+
project_name=theme_name,
502+
)
503+
compiled = compile_theme_file(result.ui_path)
504+
theme_dir = Path(smartmonitor_theme_dir(theme_name))
505+
os.makedirs(theme_dir, exist_ok=True)
506+
target_img = smartmonitor_theme_img(theme_name)
507+
with open(target_img, "wb") as stream:
508+
stream.write(compiled)
509+
shutil.copy2(target_img, smartmonitor_bundled_dat_path(theme_name))
510+
511+
editable_project_dir = theme_dir / "source"
512+
if editable_project_dir.exists():
513+
shutil.rmtree(editable_project_dir)
514+
shutil.copytree(result.output_dir, editable_project_dir)
515+
editable_ui_path = editable_project_dir / result.ui_path.name
516+
517+
write_smartmonitor_theme_metadata(
518+
theme_name,
519+
{
520+
"name": theme_name,
521+
"source_theme": str(Path(classic_theme_path).resolve()),
522+
"source_ui": str(editable_ui_path.resolve()),
523+
"source_project": str(editable_project_dir.resolve()),
524+
"compiled_at": datetime.now(timezone.utc).isoformat(),
525+
"compiler": "classic_theme_to_imgdat",
526+
"size": os.path.getsize(target_img),
527+
"sha256": compute_file_sha256(target_img),
528+
},
529+
)
530+
return target_img, result
531+
532+
491533
def create_smartmonitor_ui_project(project_dir: str, project_name: str):
492534
project_path = Path(project_dir)
493535
images_dir = project_path / "images"
@@ -697,7 +739,7 @@ class TuringConfigWindow:
697739
def __init__(self):
698740
self.window = Tk()
699741
self.window.title('Turing System Monitor configuration')
700-
self.window.geometry("940x580")
742+
self.window.geometry("940x640")
701743
self.window.iconphoto(True, PhotoImage(file=MAIN_DIRECTORY + "res/icons/monitor-icon-17865/64.png"))
702744
# When window gets focus again, reload theme preview in case it has been updated by theme editor
703745
self.window.bind("<FocusIn>", self.on_theme_change)
@@ -795,57 +837,57 @@ def __init__(self):
795837
"Fans missing from the list? Install lm-sensors package\n"
796838
"and run 'sudo sensors-detect' command, then reboot.")
797839

798-
self.weather_ping_btn = ttk.Button(self.window, text="Weather & ping",
840+
self.weather_ping_btn = ttk.Button(self.window, text="Weather",
799841
command=lambda: self.on_weatherping_click())
800-
self.weather_ping_btn.place(x=20, y=520, height=50, width=100)
842+
self.weather_ping_btn.place(x=20, y=505, height=42, width=105)
801843

802-
self.open_theme_folder_btn = ttk.Button(self.window, text="Open themes\nfolder",
844+
self.open_theme_folder_btn = ttk.Button(self.window, text="Themes",
803845
command=lambda: self.on_open_theme_folder_click())
804-
self.open_theme_folder_btn.place(x=130, y=520, height=50, width=100)
846+
self.open_theme_folder_btn.place(x=135, y=505, height=42, width=105)
805847

806848
self.edit_theme_btn = ttk.Button(self.window, text="Edit theme", command=lambda: self.on_theme_editor_click())
807-
self.edit_theme_btn.place(x=240, y=520, height=50, width=100)
849+
self.edit_theme_btn.place(x=250, y=505, height=42, width=105)
808850

809851
self.edit_ui_btn = ttk.Button(
810852
self.window,
811853
text="Edit UI",
812854
command=lambda: self.on_edit_ui_click(),
813855
)
814-
self.edit_ui_btn.place(x=350, y=520, height=50, width=100)
856+
self.edit_ui_btn.place(x=365, y=505, height=42, width=105)
815857

816858
self.convert_theme_btn = ttk.Button(
817859
self.window,
818860
text="Convert",
819861
command=lambda: self.on_theme_convert_click(),
820862
)
821-
self.convert_theme_btn.place(x=460, y=520, height=50, width=100)
863+
self.convert_theme_btn.place(x=480, y=505, height=42, width=105)
822864

823865
self.new_ui_btn = ttk.Button(
824866
self.window,
825867
text="New UI",
826868
command=lambda: self.on_new_ui_project_click(),
827869
)
828-
self.new_ui_btn.place(x=570, y=520, height=50, width=100)
870+
self.new_ui_btn.place(x=595, y=505, height=42, width=105)
829871

830872
self.autostart_enable_btn = ttk.Button(
831873
self.window,
832-
text="Enable\nautostart",
874+
text="Autostart ON",
833875
command=lambda: self.on_enable_autostart_click(),
834876
)
835-
self.autostart_enable_btn.place(x=680, y=520, height=50, width=100)
877+
self.autostart_enable_btn.place(x=20, y=555, height=42, width=120)
836878

837879
self.autostart_disable_btn = ttk.Button(
838880
self.window,
839-
text="Disable\nautostart",
881+
text="Autostart OFF",
840882
command=lambda: self.on_disable_autostart_click(),
841883
)
842-
self.autostart_disable_btn.place(x=790, y=520, height=50, width=100)
884+
self.autostart_disable_btn.place(x=150, y=555, height=42, width=120)
843885

844-
self.save_btn = ttk.Button(self.window, text="Save settings", command=lambda: self.on_save_click())
845-
self.save_btn.place(x=900, y=520, height=50, width=110)
886+
self.save_btn = ttk.Button(self.window, text="Save", command=lambda: self.on_save_click())
887+
self.save_btn.place(x=280, y=555, height=42, width=120)
846888

847-
self.save_run_btn = ttk.Button(self.window, text="Save and run", command=lambda: self.on_saverun_click())
848-
self.save_run_btn.place(x=1020, y=520, height=50, width=110)
889+
self.save_run_btn = ttk.Button(self.window, text="Save + run", command=lambda: self.on_saverun_click())
890+
self.save_run_btn.place(x=410, y=555, height=42, width=120)
849891

850892
self.config = None
851893
self.load_config_values()
@@ -860,18 +902,18 @@ def refresh_theme_selector(self):
860902
if self.is_smartmonitor_model():
861903
themes = get_smartmonitor_themes()
862904
self.theme_label.config(text='SmartMonitor theme')
863-
self.open_theme_folder_btn.config(text="Import vendor\nset")
864-
self.edit_theme_btn.config(text="Import\n.dat")
865-
self.edit_ui_btn.config(text="Open/Edit\nUI", state="normal")
866-
self.convert_theme_btn.config(text="Convert\nUI->DAT", state="normal")
867-
self.new_ui_btn.config(text="New UI\nproject", state="normal")
905+
self.open_theme_folder_btn.config(text="Vendor set")
906+
self.edit_theme_btn.config(text="Import .dat")
907+
self.edit_ui_btn.config(text="Edit UI", state="normal")
908+
self.convert_theme_btn.config(text="Convert")
909+
self.new_ui_btn.config(text="New UI", state="normal")
868910
else:
869911
size = self.size_cb.get().replace(SIZE_2_x_INCH, SIZE_2_1_INCH)
870912
themes = get_themes(size)
871913
self.theme_label.config(text='Theme')
872-
self.open_theme_folder_btn.config(text="Open themes\nfolder")
914+
self.open_theme_folder_btn.config(text="Themes")
873915
self.edit_theme_btn.config(text="Edit theme")
874-
self.edit_ui_btn.config(text="Open/Edit UI", state="disabled")
916+
self.edit_ui_btn.config(text="Edit UI", state="disabled")
875917
self.convert_theme_btn.config(text="Convert", state="disabled")
876918
self.new_ui_btn.config(text="New UI", state="disabled")
877919

@@ -1114,7 +1156,7 @@ def on_theme_editor_click(self):
11141156

11151157
def on_theme_convert_click(self):
11161158
if self.is_smartmonitor_model():
1117-
self.on_smartmonitor_ui_convert_click()
1159+
self.on_smartmonitor_convert_click()
11181160
return
11191161

11201162
def on_new_ui_project_click(self):
@@ -1405,6 +1447,88 @@ def on_smartmonitor_ui_convert_click(self):
14051447
parent=self.window,
14061448
)
14071449

1450+
def on_smartmonitor_classic_theme_convert_click(self):
1451+
theme_dir = filedialog.askdirectory(
1452+
parent=self.window,
1453+
title="Choose classic theme directory to convert",
1454+
initialdir=THEMES_DIR,
1455+
mustexist=True,
1456+
)
1457+
if not theme_dir:
1458+
return
1459+
1460+
theme_yaml = Path(theme_dir) / "theme.yaml"
1461+
if not theme_yaml.is_file():
1462+
messagebox.showerror(
1463+
"Invalid classic theme",
1464+
"The selected directory does not contain theme.yaml",
1465+
parent=self.window,
1466+
)
1467+
return
1468+
1469+
suggested_name = f"{Path(theme_dir).name}-smartmonitor"
1470+
theme_name = simpledialog.askstring(
1471+
"Convert classic theme",
1472+
"Theme name for the compiled .dat:",
1473+
parent=self.window,
1474+
initialvalue=suggested_name,
1475+
)
1476+
if theme_name is None:
1477+
return
1478+
1479+
theme_name = sanitize_smartmonitor_theme_name(theme_name)
1480+
if not theme_name:
1481+
messagebox.showerror("Invalid name", "Theme name cannot be empty.", parent=self.window)
1482+
return
1483+
1484+
target_img = smartmonitor_theme_img(theme_name)
1485+
if os.path.isfile(target_img):
1486+
overwrite = messagebox.askyesno(
1487+
"Overwrite theme?",
1488+
f"A SmartMonitor theme named '{theme_name}' already exists.\nOverwrite it?",
1489+
parent=self.window,
1490+
)
1491+
if not overwrite:
1492+
return
1493+
1494+
try:
1495+
_, result = compile_and_import_classic_theme(theme_name, str(theme_yaml))
1496+
except Exception as exc:
1497+
messagebox.showerror("Classic theme conversion failed", str(exc), parent=self.window)
1498+
return
1499+
1500+
self.refresh_theme_selector()
1501+
self.theme_cb.set(theme_name)
1502+
self.on_theme_change()
1503+
skipped = ""
1504+
if result.skipped_items:
1505+
preview = ", ".join(result.skipped_items[:6])
1506+
skipped = f"\n\nSkipped unsupported classic items:\n{preview}"
1507+
if len(result.skipped_items) > 6:
1508+
skipped += ", ..."
1509+
messagebox.showinfo(
1510+
"Classic theme converted",
1511+
f"Theme '{theme_name}' was converted from a classic YAML theme to SmartMonitor .dat.\n"
1512+
f"Use Save or Save and run to activate it.{skipped}",
1513+
parent=self.window,
1514+
)
1515+
1516+
def on_smartmonitor_convert_click(self):
1517+
source_kind = messagebox.askyesnocancel(
1518+
"Choose conversion source",
1519+
"What do you want to convert?\n\n"
1520+
"Yes: vendor SmartMonitor .ui -> .dat\n"
1521+
"No: classic YAML theme -> SmartMonitor .dat\n"
1522+
"Cancel: do nothing",
1523+
parent=self.window,
1524+
)
1525+
if source_kind is None:
1526+
return
1527+
if source_kind:
1528+
self.on_smartmonitor_ui_convert_click()
1529+
else:
1530+
self.on_smartmonitor_classic_theme_convert_click()
1531+
14081532
def on_smartmonitor_new_ui_project_click(self):
14091533
os.makedirs(DEFAULT_SMARTMONITOR_PROJECTS_DIR, exist_ok=True)
14101534
parent_dir = filedialog.askdirectory(

0 commit comments

Comments
 (0)