Skip to content
Open
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
11 changes: 5 additions & 6 deletions src/toolManager/gui_fixed.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import sys
import ctypes
import subprocess
import platform
from PyQt6.QtWidgets import (
QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
QTableWidget, QTableWidgetItem, QHeaderView, QProgressBar,
Expand All @@ -15,12 +14,12 @@
from PyQt6.QtGui import QFont
import logging

PYTHON = sys.executable
SYSTEM = platform.system()
IS_WINDOWS = SYSTEM == "Windows"
IS_LINUX = SYSTEM == "Linux"
try:
from toolManager.constants import IS_WINDOWS, IS_LINUX
except (ImportError, ValueError):
from constants import IS_WINDOWS, IS_LINUX

import os
PYTHON = sys.executable
BASE_DIR = os.path.dirname(os.path.abspath(__file__))

if IS_WINDOWS:
Expand Down
29 changes: 16 additions & 13 deletions src/toolManager/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,26 @@
from PyQt6.QtGui import QFont

try:
from gui_fixed import ToolManagerGUI
from toolManager.gui_fixed import ToolManagerGUI
except ImportError:
pass # Will handle gracefully later if missing
from gui_fixed import ToolManagerGUI

try:
from toolManager.constants import IS_WINDOWS
except (ImportError, ValueError):
from constants import IS_WINDOWS

try:
from toolManager.registry import CATEGORIES
except (ImportError, ValueError):
from registry import CATEGORIES

# ==================== CONFIG ====================
BASE_DIR = Path(__file__).resolve().parent
INFO_JSON = BASE_DIR / "information.json"
FULL_GUI = BASE_DIR / "gui_fixed.py"
PYTHON = sys.executable

ANALOG_TOOLS = ["esim", "kicad", "ngspice"]
DIGITAL_TOOLS = ["esim", "kicad", "ngspice", "ghdl", "verilator", "llvm"]
ANALOG_TOOLS = CATEGORIES["analog"]
DIGITAL_TOOLS = CATEGORIES["digital"]

TOOL_LABELS = {
"esim": "eSim",
Expand Down Expand Up @@ -54,7 +62,6 @@ def is_admin():
def relaunch_as_admin():
script = str(Path(__file__).resolve())

# Swap to pythonw.exe to suppress the black console window
python_exe = PYTHON
if python_exe.lower().endswith("python.exe"):
pythonw_exe = python_exe[:-10] + "pythonw.exe"
Expand Down Expand Up @@ -126,7 +133,6 @@ def run(self):
self.finished.emit(success)


# ==================== MAIN WINDOW ====================
class ToolManagerWindow(QMainWindow):
def __init__(self):
super().__init__()
Expand Down Expand Up @@ -396,7 +402,6 @@ def _create_uninstall_tab(self):
layout.setContentsMargins(30, 30, 30, 30)
layout.setSpacing(16)

# Warning banner
warn = QFrame()
warn.setStyleSheet("""
QFrame {
Expand Down Expand Up @@ -656,7 +661,7 @@ def _run_uninstall(self, tools, label):
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 @@ -669,9 +674,7 @@ def _run_uninstall(self, tools, label):


def main():
if sys.platform == "win32" 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.
if IS_WINDOWS and not is_admin():
relaunch_as_admin()
sys.exit(0)

Expand Down
Empty file added src/toolManager/qt/__init__.py
Empty file.
9 changes: 9 additions & 0 deletions src/toolManager/qt/gui_fixed.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from toolManager.gui_fixed import ToolManagerGUI, UninstallWindow, CommandWorker
from toolManager.registry import TOOLS
from toolManager.constants import IS_WINDOWS, IS_LINUX
from toolManager.paths import get_toolmanager_root
Comment on lines +1 to +4

__all__ = [
"ToolManagerGUI", "UninstallWindow", "CommandWorker",
"TOOLS", "IS_WINDOWS", "IS_LINUX", "get_toolmanager_root",
]
49 changes: 49 additions & 0 deletions src/toolManager/qt/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
from toolManager.main import ToolManagerWindow, main
from toolManager.registry import TOOLS, CATEGORIES
from toolManager.constants import IS_WINDOWS
from toolManager.qt.gui_fixed import ToolManagerGUI
Comment on lines +1 to +4

ANALOG_TOOLS = CATEGORIES["analog"]
DIGITAL_TOOLS = CATEGORIES["digital"]
TOOL_LABELS = {tid: spec.label for tid, spec in TOOLS.items()
if tid in CATEGORIES["analog"] or tid in CATEGORIES["digital"]}
TOOL_VERSIONS = {
"esim": "2.4",
"kicad": "latest",
"ngspice": "latest",
"ghdl": "latest",
"verilator": "latest",
"llvm": "latest",
}

try:
from toolManager.platform_utils import is_admin, relaunch_as_admin
except ImportError:
import ctypes
import os
import sys

def is_admin():
if IS_WINDOWS:
try:
return ctypes.windll.shell32.IsUserAnAdmin()
except Exception:
return False
return True

def relaunch_as_admin():
script = sys.argv[0]
python_exe = sys.executable
if python_exe.lower().endswith("python.exe"):
pythonw_exe = python_exe[:-10] + "pythonw.exe"
if os.path.exists(pythonw_exe):
python_exe = pythonw_exe
ctypes.windll.shell32.ShellExecuteW(
None, "runas", python_exe, f'"{script}"', None, 1
)

__all__ = [
"ToolManagerWindow", "main", "ToolManagerGUI",
"TOOL_LABELS", "TOOL_VERSIONS", "ANALOG_TOOLS", "DIGITAL_TOOLS",
"IS_WINDOWS", "is_admin", "relaunch_as_admin",
]
4 changes: 4 additions & 0 deletions src/toolManager/qt/updater_gui.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from toolManager.updater_gui import PackageUpdaterWindow, main
from toolManager.registry import TOOLS
Comment on lines +1 to +2

__all__ = ["PackageUpdaterWindow", "main", "TOOLS"]
61 changes: 61 additions & 0 deletions src/toolManager/qt/worker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
from PyQt6.QtCore import QThread, pyqtSignal
import subprocess
import sys

try:
from toolManager.worker import BaseWorker, CommandWorker, InstallWorker, InstallerThread
except ImportError:
from toolManager.gui_fixed import CommandWorker
from toolManager.updater_gui import InstallerThread

class BaseWorker(QThread):
def cancel(self):
pass

try:
from toolManager.worker import InstallWorker
except ImportError:
from pathlib import Path
from toolManager.registry import TOOLS

BASE_DIR = Path(__file__).resolve().parent.parent
PYTHON = sys.executable

class InstallWorker(QThread):
progress = pyqtSignal(str)
finished = pyqtSignal(bool)

def __init__(self, tools):
super().__init__()
self.tools = tools

def run(self):
success = True
backend = str(BASE_DIR / "tool_manager_windows.py")
for tool, version in self.tools:
label = TOOLS[tool].label if tool in TOOLS else tool
self.progress.emit(f"Installing {label} {version}...")
try:
proc = subprocess.Popen(
[PYTHON, backend, "install", tool, version],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
encoding="utf-8",
errors="ignore",
)
for line in proc.stdout:
line = line.rstrip()
if line:
self.progress.emit(f" {line}")
if "[ERROR]" in line or "install_failed|" in line:
success = False
proc.wait()
if proc.returncode != 0:
success = False
except Exception as e:
self.progress.emit(f" [ERROR] {tool}: {e}")
success = False
self.finished.emit(success)

__all__ = ["BaseWorker", "CommandWorker", "InstallWorker", "InstallerThread"]