From 6bdb91fda6e7641723236715e2540880aaa704ef Mon Sep 17 00:00:00 2001 From: Eashan-H Date: Mon, 15 Jun 2026 16:17:05 +0530 Subject: [PATCH] refactor: move platform-specific code to platform_util.py --- src/toolManager/gui_fixed.py | 7 +--- src/toolManager/main.py | 44 ++++++++++++++++++------- src/toolManager/tool_manager_windows.py | 9 +++-- src/toolManager/updater_gui.py | 7 +++- src/toolManager/utils.py | 8 ++--- 5 files changed, 47 insertions(+), 28 deletions(-) diff --git a/src/toolManager/gui_fixed.py b/src/toolManager/gui_fixed.py index a06c685f1..c40439bcc 100644 --- a/src/toolManager/gui_fixed.py +++ b/src/toolManager/gui_fixed.py @@ -3,7 +3,7 @@ import sys import ctypes import subprocess -import platform +from platform_utils import IS_WINDOWS, IS_LINUX from PyQt6.QtWidgets import ( QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QTableWidget, QTableWidgetItem, QHeaderView, QProgressBar, @@ -15,11 +15,6 @@ from PyQt6.QtGui import QFont import logging -PYTHON = sys.executable -SYSTEM = platform.system() -IS_WINDOWS = SYSTEM == "Windows" -IS_LINUX = SYSTEM == "Linux" - import os BASE_DIR = os.path.dirname(os.path.abspath(__file__)) diff --git a/src/toolManager/main.py b/src/toolManager/main.py index 40d5720ea..0855b60df 100644 --- a/src/toolManager/main.py +++ b/src/toolManager/main.py @@ -12,11 +12,29 @@ ) from PyQt6.QtCore import Qt, QThread, pyqtSignal from PyQt6.QtGui import QFont +from platform_utils import IS_WINDOWS, IS_LINUX, IS_MAC, distro_label # ==================== CONFIG ==================== BASE_DIR = Path(__file__).resolve().parent INFO_JSON = BASE_DIR / "information.json" -FULL_GUI = BASE_DIR / "gui_fixed.py" + +if IS_WINDOWS: + BACKEND = BASE_DIR / "tool_manager_windows.py" + FULL_GUI = BASE_DIR / "gui_fixed.py" + OS_LABEL = "Windows Edition" +elif IS_LINUX: + BACKEND = BASE_DIR / "tool_manager_linux.py" + FULL_GUI = BASE_DIR / "updater_gui.py" + OS_LABEL = f"{distro_label()} Edition" +elif IS_MAC: + BACKEND = BASE_DIR / "tool_manager_macos.py" + FULL_GUI = BASE_DIR / "updater_gui.py" + OS_LABEL = f"{distro_label()} Edition" +else: + BACKEND = BASE_DIR / "tool_manager_linux.py" + FULL_GUI = BASE_DIR / "updater_gui.py" + OS_LABEL = "Linux Edition" + PYTHON = sys.executable ANALOG_TOOLS = ["esim", "kicad", "ngspice"] @@ -41,12 +59,16 @@ } def is_admin(): + if not IS_WINDOWS: + return True try: return ctypes.windll.shell32.IsUserAnAdmin() except Exception: return False def relaunch_as_admin(): + if not IS_WINDOWS: + return True script = str(Path(__file__).resolve()) # Swap to pythonw.exe to suppress the black console window @@ -92,7 +114,7 @@ def __init__(self, tools): def run(self): success = True - backend = str(BASE_DIR / "tool_manager_windows.py") + backend = str(BACKEND) for tool, version in self.tools: self.progress.emit( f"Installing {TOOL_LABELS.get(tool, tool)} {version}..." @@ -186,7 +208,7 @@ def _create_header(self): t1 = QLabel("eSim Tool Manager") t1.setFont(QFont("Arial", 24, QFont.Weight.Bold)) t1.setStyleSheet("color: white; background: transparent;") - t2 = QLabel("Package Management System • Windows Edition") + t2 = QLabel(OS_LABEL) t2.setFont(QFont("Arial", 11)) t2.setStyleSheet("color: rgba(255,255,255,0.9); background: transparent;") vbox.addWidget(t1) @@ -658,17 +680,15 @@ def _on_install_done(self, success, mode_name): def _open_full_manager(self): if not FULL_GUI.exists(): - QMessageBox.critical( - self, "File Not Found", - f"gui_fixed.py not found at:\n{FULL_GUI}\n\n" - f"Make sure all files are in:\n{BASE_DIR}" - ) + QMessageBox.critical(self, "Error", + f"GUI not found at:\n{FULL_GUI}\n\n" + f"Details: {e}") return try: subprocess.Popen( [PYTHON, str(FULL_GUI)], creationflags=(subprocess.CREATE_NO_WINDOW - if sys.platform == "win32" else 0) + if IS_WINDOWS else 0) ) except Exception as e: QMessageBox.critical(self, "Error", @@ -719,12 +739,12 @@ def _uninstall_all(self): def _run_uninstall(self, tools, label): try: - backend = str(BASE_DIR / "tool_manager_windows.py") + backend = str(BACKEND) for tool, _ in tools: subprocess.Popen( [PYTHON, backend, "uninstall", tool, "none"], creationflags=(subprocess.CREATE_NO_WINDOW - if sys.platform == "win32" else 0) + if IS_WINDOWS else 0) ) QMessageBox.information( self, "Uninstall Started", @@ -737,7 +757,7 @@ def _run_uninstall(self, tools, label): def main(): - if sys.platform == "win32" and not is_admin(): + if IS_WINDOWS and not is_admin(): # Note: We deliberately skip a manual PyQt consent dialog here because # Windows will natively prompt the user with a UAC shield anyway. relaunch_as_admin() diff --git a/src/toolManager/tool_manager_windows.py b/src/toolManager/tool_manager_windows.py index 5cd42e1ea..c2ba1d6e0 100644 --- a/src/toolManager/tool_manager_windows.py +++ b/src/toolManager/tool_manager_windows.py @@ -13,6 +13,7 @@ import time import io from pathlib import Path +from platform_utils import IS_WINDOWS, MSYS2_PATH # Add local directory to path for backend utility imports _local_path = str(Path(__file__).resolve().parent) @@ -21,12 +22,12 @@ from utils import ( run_cmd_safe, run_cmd_stream, which, print_status, - DEFAULT_MSYS2_PATH, DEFAULT_ESIM_DIR, WIN_KICAD_PATHS, + DEFAULT_ESIM_DIR, WIN_KICAD_PATHS, WIN_NGSPICE_PATHS, WIN_LLVM_PATHS, get_msys2_bash, get_msys2_mingw_bin, get_msys2_mingw_root ) -if sys.platform == "win32": +if IS_WINDOWS: sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='ignore') sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='ignore') @@ -34,8 +35,6 @@ STATE_FILE = BASE_DIR / "information.json" BASE_DIR.mkdir(parents=True, exist_ok=True) -MSYS2_PATH = DEFAULT_MSYS2_PATH - DOWNLOAD_DIR = BASE_DIR / "Download" DOWNLOAD_DIR.mkdir(parents=True, exist_ok=True) @@ -202,7 +201,7 @@ def _find_ngspice_exe(): return which("ngspice") or which("ngspice.exe") def find_llvm_fixed(version=None): - if platform.system() == "Windows": + if IS_WINDOWS: import ctypes HWND_BROADCAST = 0xFFFF WM_SETTINGCHANGE = 0x001A diff --git a/src/toolManager/updater_gui.py b/src/toolManager/updater_gui.py index 6bd40e60c..6e6371109 100644 --- a/src/toolManager/updater_gui.py +++ b/src/toolManager/updater_gui.py @@ -11,6 +11,7 @@ QAbstractItemView) from PyQt6.QtCore import Qt, QThread, pyqtSignal from PyQt6.QtGui import QFont +from platform_utils import IS_WINDOWS, distro_label class InstallerThread(QThread): progress = pyqtSignal(str, int) @@ -22,6 +23,10 @@ def __init__(self, packages_to_install): self.packages = packages_to_install def run(self): + if IS_WINDOWS: + self.finished.emit(False, "Package Updater is Linux only.\nOn Windows, use the main Tool Manager.") + return + total = len(self.packages) for pkg_idx, (package_name, version, script_name) in enumerate(self.packages): try: @@ -368,7 +373,7 @@ def create_header(self): title.setFont(QFont("Segoe UI", 14, QFont.Weight.Bold)) title.setStyleSheet("color: white; background: transparent;") - subtitle = QLabel("Real-time progress • Ngspice 35-43 • Ubuntu 22.04") + subtitle = QLabel(f"Real-time progress • Ngspice 35-43 • {distro_label()}") subtitle.setFont(QFont("Segoe UI", 8)) subtitle.setStyleSheet("color: rgba(255,255,255,0.9); background: transparent;") diff --git a/src/toolManager/utils.py b/src/toolManager/utils.py index 2481da5a6..1ca1d7faa 100644 --- a/src/toolManager/utils.py +++ b/src/toolManager/utils.py @@ -17,11 +17,11 @@ import threading import time from pathlib import Path +from platform_utils import IS_WINDOWS, MSYS2_PATH # ==================== CONSTANTS & DEFAULTS ==================== # Default Windows installation paths -DEFAULT_MSYS2_PATH = Path(r"C:\msys64") DEFAULT_ESIM_DIR = Path(r"C:\FOSSEE\eSim") WIN_KICAD_PATHS = [ @@ -62,7 +62,7 @@ def run_cmd_safe(cmd, timeout=30, cwd=None): Returns the subprocess.CompletedProcess result or None if failed. """ try: - if platform.system() == "Windows": + if IS_WINDOWS: startupinfo = subprocess.STARTUPINFO() startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW startupinfo.wShowWindow = subprocess.SW_HIDE @@ -94,7 +94,7 @@ def run_cmd_stream(cmd, timeout=900, cwd=None): Returns a tuple of (returncode, full_output_string). """ try: - if platform.system() == "Windows": + if IS_WINDOWS: si = subprocess.STARTUPINFO() si.dwFlags |= subprocess.STARTF_USESHOWWINDOW si.wShowWindow = subprocess.SW_HIDE @@ -161,7 +161,7 @@ def get_msys2_bash(): """ Returns the path to MSYS2 bash.exe if found, else None. """ - bash_path = DEFAULT_MSYS2_PATH / "usr" / "bin" / "bash.exe" + bash_path = MSYS2_PATH / "usr" / "bin" / "bash.exe" return bash_path if bash_path.exists() else None def get_msys2_mingw_root():