Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 1 addition & 6 deletions src/toolManager/gui_fixed.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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__))

Expand Down
44 changes: 32 additions & 12 deletions src/toolManager/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand All @@ -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
Expand Down Expand Up @@ -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}..."
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand All @@ -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()
Expand Down
9 changes: 4 additions & 5 deletions src/toolManager/tool_manager_windows.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -21,21 +22,19 @@

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')

BASE_DIR = Path(__file__).resolve().parent
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)

Expand Down Expand Up @@ -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
Expand Down
7 changes: 6 additions & 1 deletion src/toolManager/updater_gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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:
Expand Down Expand Up @@ -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;")

Expand Down
8 changes: 4 additions & 4 deletions src/toolManager/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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():
Expand Down