mirror of
https://github.com/vosen/ZLUDA.git
synced 2025-08-04 07:09:53 +00:00
add build button
Build button is for linux only and needs testing
This commit is contained in:
parent
1d25762097
commit
14b73f8acb
1 changed files with 723 additions and 477 deletions
|
@ -10,13 +10,116 @@ import psutil
|
||||||
import time
|
import time
|
||||||
import threading
|
import threading
|
||||||
from concurrent.futures import ThreadPoolExecutor
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
from PyQt6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,
|
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,
|
||||||
QHBoxLayout, QPushButton, QLineEdit, QLabel,
|
QHBoxLayout, QPushButton, QLineEdit, QLabel,
|
||||||
QFileDialog, QMessageBox, QTextEdit, QFrame,
|
QFileDialog, QMessageBox, QTextEdit, QFrame,
|
||||||
QTabWidget, QProgressBar, QGroupBox, QGridLayout,
|
QTabWidget, QProgressBar, QGroupBox, QGridLayout,
|
||||||
QCheckBox)
|
QCheckBox)
|
||||||
from PyQt6.QtCore import Qt, QThread, pyqtSignal, QSize
|
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QSize
|
||||||
from PyQt6.QtGui import QPalette, QColor, QScreen
|
from PyQt5.QtGui import QPalette, QColor, QScreen
|
||||||
|
from pathlib import Path
|
||||||
|
import json
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
def check_admin():
|
||||||
|
"""Check if the program is running with admin privileges."""
|
||||||
|
try:
|
||||||
|
return os.geteuid() == 0
|
||||||
|
except AttributeError:
|
||||||
|
# Windows platform
|
||||||
|
try:
|
||||||
|
return bool(os.getuid() & 0x4000) # Check for admin bit
|
||||||
|
except AttributeError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def restart_as_admin():
|
||||||
|
"""Restart the application with admin privileges."""
|
||||||
|
if platform.system() == "Windows":
|
||||||
|
import ctypes
|
||||||
|
if not ctypes.windll.shell32.IsUserAnAdmin():
|
||||||
|
ctypes.windll.shell32.ShellExecuteW(
|
||||||
|
None, "runas", sys.executable, " ".join(sys.argv), None, 1
|
||||||
|
)
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
if os.geteuid() != 0:
|
||||||
|
os.execvp('sudo', ['sudo', 'python3'] + sys.argv)
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_linux_distro():
|
||||||
|
"""Detect the Linux distribution."""
|
||||||
|
try:
|
||||||
|
with open('/etc/os-release', 'r') as f:
|
||||||
|
lines = f.readlines()
|
||||||
|
info = {}
|
||||||
|
for line in lines:
|
||||||
|
if '=' in line:
|
||||||
|
key, value = line.strip().split('=', 1)
|
||||||
|
info[key] = value.strip('"')
|
||||||
|
|
||||||
|
# Get the ID and ID_LIKE fields
|
||||||
|
distro_id = info.get('ID', '').lower()
|
||||||
|
distro_like = info.get('ID_LIKE', '').lower()
|
||||||
|
|
||||||
|
# Handle special cases
|
||||||
|
if 'cachyos' in distro_id:
|
||||||
|
return 'cachyos'
|
||||||
|
elif 'pikaos' in distro_id:
|
||||||
|
return 'pikaos'
|
||||||
|
elif 'nobara' in distro_id:
|
||||||
|
return 'nobara'
|
||||||
|
|
||||||
|
return distro_id
|
||||||
|
except:
|
||||||
|
return ''
|
||||||
|
|
||||||
|
def get_package_manager_commands():
|
||||||
|
"""Get the appropriate package manager commands for the detected distro."""
|
||||||
|
distro = get_linux_distro()
|
||||||
|
commands = {
|
||||||
|
'ubuntu': {
|
||||||
|
'update': 'sudo apt update',
|
||||||
|
'install': 'sudo apt install -y build-essential curl git cmake pkg-config libvulkan-dev vulkan-tools spirv-tools libclang-dev clang llvm-dev python3-pip ninja-build',
|
||||||
|
'rust': 'curl --proto "=https" --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y'
|
||||||
|
},
|
||||||
|
'debian': {
|
||||||
|
'update': 'sudo apt update',
|
||||||
|
'install': 'sudo apt install -y build-essential curl git cmake pkg-config libvulkan-dev vulkan-tools spirv-tools libclang-dev clang llvm-dev python3-pip ninja-build',
|
||||||
|
'rust': 'curl --proto "=https" --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y'
|
||||||
|
},
|
||||||
|
'pikaos': {
|
||||||
|
'update': 'sudo apt update',
|
||||||
|
'install': 'sudo apt install -y build-essential curl git cmake pkg-config libvulkan-dev vulkan-tools spirv-tools libclang-dev clang llvm-dev python3-pip ninja-build',
|
||||||
|
'rust': 'curl --proto "=https" --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y'
|
||||||
|
},
|
||||||
|
'arch': {
|
||||||
|
'update': 'sudo pacman -S --noconfirm base-devel git cmake vulkan-devel vulkan-tools spirv-tools clang llvm python-pip ninja rust',
|
||||||
|
'install': '', # Empty since we do it all in the update command
|
||||||
|
'rust': '' # Empty since rust is included in the update command
|
||||||
|
},
|
||||||
|
'cachyos': {
|
||||||
|
'update': 'sudo pacman -S --noconfirm base-devel git cmake vulkan-devel vulkan-tools spirv-tools clang llvm python-pip ninja rust',
|
||||||
|
'install': '', # Empty since we do it all in the update command
|
||||||
|
'rust': '' # Empty since rust is included in the update command
|
||||||
|
},
|
||||||
|
'manjaro': {
|
||||||
|
'update': 'sudo pacman -S --noconfirm base-devel git cmake vulkan-devel vulkan-tools spirv-tools clang llvm python-pip ninja rust',
|
||||||
|
'install': '', # Empty since we do it all in the update command
|
||||||
|
'rust': '' # Empty since rust is included in the update command
|
||||||
|
},
|
||||||
|
'fedora': {
|
||||||
|
'update': 'sudo dnf update -y',
|
||||||
|
'install': 'sudo dnf install -y gcc gcc-c++ git cmake pkgconfig vulkan-devel vulkan-tools spirv-tools clang-devel llvm-devel python3-pip ninja-build',
|
||||||
|
'rust': 'sudo dnf install -y rust cargo'
|
||||||
|
},
|
||||||
|
'nobara': {
|
||||||
|
'update': 'sudo dnf update -y',
|
||||||
|
'install': 'sudo dnf install -y gcc gcc-c++ git cmake pkgconfig vulkan-devel vulkan-tools spirv-tools clang-devel llvm-devel python3-pip ninja-build',
|
||||||
|
'rust': 'sudo dnf install -y rust cargo'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return commands.get(distro, {})
|
||||||
|
|
||||||
class DownloadThread(QThread):
|
class DownloadThread(QThread):
|
||||||
"""Thread for downloading ZLUDA with progress tracking"""
|
"""Thread for downloading ZLUDA with progress tracking"""
|
||||||
|
@ -119,6 +222,219 @@ class ProcessMonitor(QThread):
|
||||||
def stop(self):
|
def stop(self):
|
||||||
self.running = False
|
self.running = False
|
||||||
|
|
||||||
|
class BuildWorker(QThread):
|
||||||
|
progress = pyqtSignal(str)
|
||||||
|
finished = pyqtSignal(bool, str)
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.process = None
|
||||||
|
|
||||||
|
def run_command(self, command, env=None, timeout=None):
|
||||||
|
"""Run a command and stream its output to the progress signal."""
|
||||||
|
try:
|
||||||
|
process = subprocess.Popen(
|
||||||
|
command,
|
||||||
|
shell=True,
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE,
|
||||||
|
env=env,
|
||||||
|
text=True,
|
||||||
|
bufsize=1,
|
||||||
|
universal_newlines=True
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create threads to read stdout and stderr
|
||||||
|
def read_output(pipe, prefix):
|
||||||
|
for line in pipe:
|
||||||
|
line = line.strip()
|
||||||
|
if line: # Only emit non-empty lines
|
||||||
|
if "Cloning into" in line:
|
||||||
|
self.progress.emit(f"\n{prefix}Starting clone: {line}")
|
||||||
|
elif any(x in line for x in ["Receiving objects:", "Resolving deltas:", "Updating files:", "remote: Counting", "remote: Compressing"]):
|
||||||
|
self.progress.emit(f"\r{prefix}{line}") # Use \r for progress updates
|
||||||
|
else:
|
||||||
|
self.progress.emit(f"{prefix}{line}")
|
||||||
|
|
||||||
|
# Start threads for reading output
|
||||||
|
stdout_thread = threading.Thread(target=read_output, args=(process.stdout, ""))
|
||||||
|
stderr_thread = threading.Thread(target=read_output, args=(process.stderr, "[Info] "))
|
||||||
|
|
||||||
|
stdout_thread.start()
|
||||||
|
stderr_thread.start()
|
||||||
|
|
||||||
|
# Wait for process to complete
|
||||||
|
returncode = process.wait()
|
||||||
|
|
||||||
|
# Wait for output threads to finish
|
||||||
|
stdout_thread.join()
|
||||||
|
stderr_thread.join()
|
||||||
|
|
||||||
|
return returncode == 0
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.progress.emit(f"[Error] Failed to run command: {str(e)}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
try:
|
||||||
|
# Get distro-specific commands
|
||||||
|
commands = get_package_manager_commands()
|
||||||
|
if not commands:
|
||||||
|
self.finished.emit(False, "Unsupported Linux distribution")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Update package manager
|
||||||
|
self.progress.emit("=== Updating package manager ===")
|
||||||
|
if not self.run_command(commands['update']):
|
||||||
|
self.finished.emit(False, "Failed to update package manager")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
self.progress.emit("\n=== Installing dependencies ===")
|
||||||
|
if not self.run_command(commands['install']):
|
||||||
|
self.finished.emit(False, "Failed to install dependencies")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Install Rust if needed
|
||||||
|
self.progress.emit("\n=== Installing Rust ===")
|
||||||
|
if not self.run_command(commands['rust']):
|
||||||
|
self.finished.emit(False, "Failed to install Rust")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Source cargo environment
|
||||||
|
self.progress.emit("\n=== Setting up Rust environment ===")
|
||||||
|
os.environ["PATH"] = f"{str(Path.home())}/.cargo/bin:{os.environ['PATH']}"
|
||||||
|
|
||||||
|
# Set up build directory in the GUI folder
|
||||||
|
gui_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
build_dir = os.path.join(gui_dir, "zluda_build")
|
||||||
|
|
||||||
|
# Clean up any existing build directory
|
||||||
|
if os.path.exists(build_dir):
|
||||||
|
self.progress.emit("\n=== Cleaning up previous build ===")
|
||||||
|
try:
|
||||||
|
shutil.rmtree(build_dir)
|
||||||
|
except Exception as e:
|
||||||
|
self.progress.emit(f"[Warning] Failed to clean up previous build: {str(e)}")
|
||||||
|
|
||||||
|
# Create build directory
|
||||||
|
os.makedirs(build_dir, exist_ok=True)
|
||||||
|
|
||||||
|
# Clone ZLUDA into the build directory
|
||||||
|
self.progress.emit("\n=== Cloning ZLUDA repository ===")
|
||||||
|
if not self.run_command(f"git clone --progress https://github.com/vosen/ZLUDA.git {build_dir}"):
|
||||||
|
self.progress.emit("\n=== Cleaning up failed clone ===")
|
||||||
|
try:
|
||||||
|
shutil.rmtree(build_dir)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
self.finished.emit(False, "Failed to clone ZLUDA")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Initialize and update git submodules
|
||||||
|
self.progress.emit("\n=== Initializing git submodules ===")
|
||||||
|
os.chdir(build_dir)
|
||||||
|
|
||||||
|
# Clone LLVM submodule with progress
|
||||||
|
self.progress.emit("\n=== Cloning LLVM (this may take a while) ===")
|
||||||
|
if not self.run_command("git submodule init ext/llvm-project && git submodule update --progress --depth 1 ext/llvm-project"):
|
||||||
|
self.progress.emit("\n=== Cleaning up failed LLVM clone ===")
|
||||||
|
try:
|
||||||
|
os.chdir(gui_dir)
|
||||||
|
shutil.rmtree(build_dir)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
self.finished.emit(False, "Failed to clone LLVM")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Update other submodules
|
||||||
|
self.progress.emit("\n=== Updating other submodules ===")
|
||||||
|
if not self.run_command("git submodule update --init --recursive --progress --depth 1 -- $(ls -d ext/* | grep -v llvm-project)"):
|
||||||
|
self.progress.emit("\n=== Cleaning up failed submodule initialization ===")
|
||||||
|
try:
|
||||||
|
os.chdir(gui_dir)
|
||||||
|
shutil.rmtree(build_dir)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
self.finished.emit(False, "Failed to initialize other submodules")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Build ZLUDA
|
||||||
|
self.progress.emit("\n=== Building ZLUDA ===")
|
||||||
|
build_env = os.environ.copy()
|
||||||
|
build_env["RUST_LOG"] = "debug"
|
||||||
|
build_env["RUST_BACKTRACE"] = "1"
|
||||||
|
|
||||||
|
if not self.run_command("cargo build --release", env=build_env):
|
||||||
|
self.progress.emit("\n=== Cleaning up failed build ===")
|
||||||
|
try:
|
||||||
|
os.chdir(gui_dir)
|
||||||
|
shutil.rmtree(build_dir)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
self.finished.emit(False, "Failed to build ZLUDA")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Find the built library
|
||||||
|
self.progress.emit("\n=== Locating built library ===")
|
||||||
|
lib_path = None
|
||||||
|
for path in Path(build_dir).rglob("*.so"):
|
||||||
|
if "target/release" in str(path) and "libzluda" in str(path):
|
||||||
|
lib_path = str(path)
|
||||||
|
self.progress.emit(f"Found library at: {lib_path}")
|
||||||
|
break
|
||||||
|
|
||||||
|
if not lib_path:
|
||||||
|
self.progress.emit("\n=== Cleaning up failed build ===")
|
||||||
|
try:
|
||||||
|
os.chdir(gui_dir)
|
||||||
|
shutil.rmtree(build_dir)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
self.finished.emit(False, "Could not find built ZLUDA library")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Copy the built library to the GUI directory
|
||||||
|
target_lib = os.path.join(gui_dir, "libzluda.so")
|
||||||
|
try:
|
||||||
|
shutil.copy2(lib_path, target_lib)
|
||||||
|
self.progress.emit(f"\nCopied library to: {target_lib}")
|
||||||
|
except Exception as e:
|
||||||
|
self.progress.emit("\n=== Cleaning up failed copy ===")
|
||||||
|
try:
|
||||||
|
os.chdir(gui_dir)
|
||||||
|
shutil.rmtree(build_dir)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
self.finished.emit(False, f"Failed to copy library: {str(e)}")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Clean up build directory
|
||||||
|
self.progress.emit("\n=== Cleaning up build directory ===")
|
||||||
|
try:
|
||||||
|
os.chdir(gui_dir)
|
||||||
|
shutil.rmtree(build_dir)
|
||||||
|
except Exception as e:
|
||||||
|
self.progress.emit(f"[Warning] Failed to clean up build directory: {str(e)}")
|
||||||
|
|
||||||
|
self.progress.emit("\n=== Build completed successfully! ===")
|
||||||
|
self.finished.emit(True, target_lib)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.progress.emit(f"\n[Error] Build failed: {str(e)}")
|
||||||
|
# Try to clean up on any unexpected error
|
||||||
|
try:
|
||||||
|
gui_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
build_dir = os.path.join(gui_dir, "zluda_build")
|
||||||
|
os.chdir(gui_dir)
|
||||||
|
if os.path.exists(build_dir):
|
||||||
|
self.progress.emit("\n=== Cleaning up after error ===")
|
||||||
|
shutil.rmtree(build_dir)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
self.finished.emit(False, str(e))
|
||||||
|
|
||||||
class ZLUDA_GUI(QMainWindow):
|
class ZLUDA_GUI(QMainWindow):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
# Enable high DPI scaling
|
# Enable high DPI scaling
|
||||||
|
@ -129,31 +445,12 @@ class ZLUDA_GUI(QMainWindow):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.setWindowTitle("ZLUDA GUI")
|
self.setWindowTitle("ZLUDA GUI")
|
||||||
|
|
||||||
# Get the primary screen
|
# Initialize variables first
|
||||||
screen = QApplication.primaryScreen()
|
self.current_process = None
|
||||||
if screen:
|
self.process_monitor = None
|
||||||
# Get the screen's geometry and scale factor
|
self.download_thread = None
|
||||||
geometry = screen.geometry()
|
self.download_progress = None
|
||||||
scale_factor = screen.devicePixelRatio()
|
self.build_worker = None
|
||||||
|
|
||||||
# Adjust scale factor to be more reasonable
|
|
||||||
scale_factor = min(scale_factor, 1.5) # Cap the scaling
|
|
||||||
|
|
||||||
# Set minimum size scaled according to DPI
|
|
||||||
self.setMinimumSize(900, 700)
|
|
||||||
|
|
||||||
# Calculate the window size based on screen size and scale factor
|
|
||||||
width = int(geometry.width() * 0.7) # 70% of screen width
|
|
||||||
height = int(geometry.height() * 0.7) # 70% of screen height
|
|
||||||
|
|
||||||
# Set the window size
|
|
||||||
self.resize(width, height)
|
|
||||||
|
|
||||||
# Center the window on screen
|
|
||||||
self.move(
|
|
||||||
int((geometry.width() - width) / 2),
|
|
||||||
int((geometry.height() - height) / 2)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Initialize widgets
|
# Initialize widgets
|
||||||
self.zluda_path = QLineEdit()
|
self.zluda_path = QLineEdit()
|
||||||
|
@ -161,212 +458,12 @@ class ZLUDA_GUI(QMainWindow):
|
||||||
self.libs_path = QLineEdit()
|
self.libs_path = QLineEdit()
|
||||||
self.download_button = QPushButton("Download ZLUDA")
|
self.download_button = QPushButton("Download ZLUDA")
|
||||||
self.run_button = QPushButton("Run Application")
|
self.run_button = QPushButton("Run Application")
|
||||||
|
self.build_button = QPushButton("Build ZLUDA")
|
||||||
|
|
||||||
|
# Connect signals
|
||||||
self.download_button.clicked.connect(self.download_zluda)
|
self.download_button.clicked.connect(self.download_zluda)
|
||||||
self.run_button.clicked.connect(self.run_application)
|
self.run_button.clicked.connect(self.run_application)
|
||||||
|
self.build_button.clicked.connect(self.build_zluda)
|
||||||
# Adjust font sizes based on DPI
|
|
||||||
base_font_size = int(11 * scale_factor)
|
|
||||||
title_font_size = int(18 * scale_factor)
|
|
||||||
button_font_size = int(12 * scale_factor)
|
|
||||||
|
|
||||||
# Update the style sheet with adjusted sizes
|
|
||||||
self.setStyleSheet(f"""
|
|
||||||
/* Main window and widget backgrounds */
|
|
||||||
QMainWindow {{
|
|
||||||
background-color: #1a1a1a;
|
|
||||||
}}
|
|
||||||
QWidget {{
|
|
||||||
background-color: transparent;
|
|
||||||
color: #ffffff;
|
|
||||||
}}
|
|
||||||
|
|
||||||
/* Labels */
|
|
||||||
QLabel {{
|
|
||||||
color: #ffffff;
|
|
||||||
font-size: {base_font_size}px;
|
|
||||||
}}
|
|
||||||
|
|
||||||
/* Group boxes */
|
|
||||||
QGroupBox {{
|
|
||||||
background-color: #212121;
|
|
||||||
border: 1px solid #2a2a2a;
|
|
||||||
border-radius: 6px;
|
|
||||||
margin-top: 12px;
|
|
||||||
padding: 15px;
|
|
||||||
font-size: {base_font_size}px;
|
|
||||||
}}
|
|
||||||
QGroupBox::title {{
|
|
||||||
subcontrol-origin: margin;
|
|
||||||
left: 10px;
|
|
||||||
padding: 3px 6px;
|
|
||||||
color: #00e5ff;
|
|
||||||
font-weight: bold;
|
|
||||||
background-color: #212121;
|
|
||||||
}}
|
|
||||||
|
|
||||||
/* Frames */
|
|
||||||
QFrame {{
|
|
||||||
background-color: #212121;
|
|
||||||
border: 1px solid #2a2a2a;
|
|
||||||
border-radius: 6px;
|
|
||||||
margin: 2px 0;
|
|
||||||
}}
|
|
||||||
|
|
||||||
/* Line edits */
|
|
||||||
QLineEdit {{
|
|
||||||
background-color: #2a2a2a;
|
|
||||||
color: #ffffff;
|
|
||||||
border: 1px solid #333333;
|
|
||||||
border-radius: 4px;
|
|
||||||
padding: 6px 10px;
|
|
||||||
font-size: {base_font_size}px;
|
|
||||||
selection-background-color: #006064;
|
|
||||||
min-height: 24px;
|
|
||||||
}}
|
|
||||||
QLineEdit:focus {{
|
|
||||||
border: 2px solid #00e5ff;
|
|
||||||
background-color: #2d2d2d;
|
|
||||||
}}
|
|
||||||
QLineEdit:hover {{
|
|
||||||
background-color: #2d2d2d;
|
|
||||||
border: 1px solid #404040;
|
|
||||||
}}
|
|
||||||
|
|
||||||
/* Buttons */
|
|
||||||
QPushButton {{
|
|
||||||
border: none;
|
|
||||||
border-radius: 4px;
|
|
||||||
padding: 6px 16px;
|
|
||||||
font-size: {button_font_size}px;
|
|
||||||
font-weight: bold;
|
|
||||||
color: white;
|
|
||||||
background-color: #424242;
|
|
||||||
min-height: 28px;
|
|
||||||
}}
|
|
||||||
QPushButton:hover {{
|
|
||||||
background-color: #4a4a4a;
|
|
||||||
}}
|
|
||||||
QPushButton:pressed {{
|
|
||||||
background-color: #383838;
|
|
||||||
}}
|
|
||||||
QPushButton[cssClass="browse"] {{
|
|
||||||
background-color: #333333;
|
|
||||||
padding: 6px 12px;
|
|
||||||
min-width: 70px;
|
|
||||||
}}
|
|
||||||
QPushButton[cssClass="browse"]:hover {{
|
|
||||||
background-color: #3d3d3d;
|
|
||||||
}}
|
|
||||||
QPushButton[cssClass="browse"]:pressed {{
|
|
||||||
background-color: #2a2a2a;
|
|
||||||
}}
|
|
||||||
|
|
||||||
/* Checkboxes */
|
|
||||||
QCheckBox {{
|
|
||||||
spacing: 6px;
|
|
||||||
color: #ffffff;
|
|
||||||
font-size: {base_font_size}px;
|
|
||||||
min-height: 20px;
|
|
||||||
padding: 2px;
|
|
||||||
}}
|
|
||||||
QCheckBox::indicator {{
|
|
||||||
width: 16px;
|
|
||||||
height: 16px;
|
|
||||||
border-radius: 3px;
|
|
||||||
border: 1px solid #404040;
|
|
||||||
background-color: #2a2a2a;
|
|
||||||
}}
|
|
||||||
QCheckBox::indicator:hover {{
|
|
||||||
border-color: #00e5ff;
|
|
||||||
background-color: #2d2d2d;
|
|
||||||
}}
|
|
||||||
QCheckBox::indicator:checked {{
|
|
||||||
background-color: #00e5ff;
|
|
||||||
border-color: #00e5ff;
|
|
||||||
}}
|
|
||||||
|
|
||||||
/* Tab widget */
|
|
||||||
QTabWidget::pane {{
|
|
||||||
border: 1px solid #2a2a2a;
|
|
||||||
border-radius: 6px;
|
|
||||||
background-color: #212121;
|
|
||||||
top: -1px;
|
|
||||||
}}
|
|
||||||
QTabBar::tab {{
|
|
||||||
background-color: #2a2a2a;
|
|
||||||
color: #b0b0b0;
|
|
||||||
padding: 8px 16px;
|
|
||||||
border-top-left-radius: 4px;
|
|
||||||
border-top-right-radius: 4px;
|
|
||||||
margin-right: 2px;
|
|
||||||
font-size: {base_font_size}px;
|
|
||||||
min-height: 24px;
|
|
||||||
min-width: 80px;
|
|
||||||
}}
|
|
||||||
QTabBar::tab:selected {{
|
|
||||||
background-color: #212121;
|
|
||||||
color: #00e5ff;
|
|
||||||
border: 1px solid #2a2a2a;
|
|
||||||
border-bottom: 2px solid #00e5ff;
|
|
||||||
}}
|
|
||||||
QTabBar::tab:hover:!selected {{
|
|
||||||
background-color: #333333;
|
|
||||||
color: #ffffff;
|
|
||||||
}}
|
|
||||||
|
|
||||||
/* Progress bar */
|
|
||||||
QProgressBar {{
|
|
||||||
border: 1px solid #2a2a2a;
|
|
||||||
border-radius: 4px;
|
|
||||||
background-color: #2a2a2a;
|
|
||||||
text-align: center;
|
|
||||||
font-size: {int(10 * scale_factor)}px;
|
|
||||||
color: white;
|
|
||||||
min-height: 20px;
|
|
||||||
}}
|
|
||||||
QProgressBar::chunk {{
|
|
||||||
background-color: #00e5ff;
|
|
||||||
border-radius: 3px;
|
|
||||||
}}
|
|
||||||
|
|
||||||
/* Text edit */
|
|
||||||
QTextEdit {{
|
|
||||||
background-color: #2a2a2a;
|
|
||||||
color: #ffffff;
|
|
||||||
border: 1px solid #333333;
|
|
||||||
border-radius: 4px;
|
|
||||||
padding: 8px;
|
|
||||||
font-size: {base_font_size}px;
|
|
||||||
line-height: 1.4;
|
|
||||||
selection-background-color: #006064;
|
|
||||||
}}
|
|
||||||
QTextEdit:focus {{
|
|
||||||
border: 2px solid #00e5ff;
|
|
||||||
}}
|
|
||||||
|
|
||||||
/* Scrollbars */
|
|
||||||
QScrollBar:vertical {{
|
|
||||||
border: none;
|
|
||||||
background: #2a2a2a;
|
|
||||||
width: 10px;
|
|
||||||
margin: 2px;
|
|
||||||
border-radius: 5px;
|
|
||||||
}}
|
|
||||||
QScrollBar::handle:vertical {{
|
|
||||||
background: #404040;
|
|
||||||
min-height: 20px;
|
|
||||||
border-radius: 5px;
|
|
||||||
}}
|
|
||||||
QScrollBar::handle:vertical:hover {{
|
|
||||||
background: #4a4a4a;
|
|
||||||
}}
|
|
||||||
QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {{
|
|
||||||
height: 0;
|
|
||||||
}}
|
|
||||||
QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {{
|
|
||||||
background: none;
|
|
||||||
}}
|
|
||||||
""")
|
|
||||||
|
|
||||||
# Create main widget and layout
|
# Create main widget and layout
|
||||||
main_widget = QWidget()
|
main_widget = QWidget()
|
||||||
|
@ -380,15 +477,15 @@ class ZLUDA_GUI(QMainWindow):
|
||||||
header.setSpacing(6)
|
header.setSpacing(6)
|
||||||
|
|
||||||
title = QLabel("ZLUDA GUI")
|
title = QLabel("ZLUDA GUI")
|
||||||
title.setStyleSheet(f"""
|
title.setStyleSheet("""
|
||||||
font-size: {title_font_size}px;
|
font-size: 18px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: #00e5ff;
|
color: #00e5ff;
|
||||||
""")
|
""")
|
||||||
|
|
||||||
version = QLabel("v4.0")
|
version = QLabel("v4.0")
|
||||||
version.setStyleSheet(f"""
|
version.setStyleSheet("""
|
||||||
font-size: {button_font_size}px;
|
font-size: 12px;
|
||||||
color: #808080;
|
color: #808080;
|
||||||
padding-top: 4px;
|
padding-top: 4px;
|
||||||
padding-left: 6px;
|
padding-left: 6px;
|
||||||
|
@ -491,6 +588,7 @@ class ZLUDA_GUI(QMainWindow):
|
||||||
buttons_layout = QHBoxLayout()
|
buttons_layout = QHBoxLayout()
|
||||||
buttons_layout.setSpacing(10)
|
buttons_layout.setSpacing(10)
|
||||||
|
|
||||||
|
# Set button styles
|
||||||
self.download_button.setStyleSheet("""
|
self.download_button.setStyleSheet("""
|
||||||
QPushButton {
|
QPushButton {
|
||||||
background-color: #00796b;
|
background-color: #00796b;
|
||||||
|
@ -525,14 +623,39 @@ class ZLUDA_GUI(QMainWindow):
|
||||||
}
|
}
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
self.build_button.setStyleSheet("""
|
||||||
|
QPushButton {
|
||||||
|
background-color: #2196f3;
|
||||||
|
min-width: 150px;
|
||||||
|
}
|
||||||
|
QPushButton:hover {
|
||||||
|
background-color: #42a5f5;
|
||||||
|
}
|
||||||
|
QPushButton:pressed {
|
||||||
|
background-color: #1976d2;
|
||||||
|
}
|
||||||
|
QPushButton:disabled {
|
||||||
|
background-color: #2d3436;
|
||||||
|
color: #666666;
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
|
||||||
buttons_layout.addWidget(self.download_button)
|
buttons_layout.addWidget(self.download_button)
|
||||||
buttons_layout.addWidget(self.run_button)
|
buttons_layout.addWidget(self.run_button)
|
||||||
|
buttons_layout.addWidget(self.build_button)
|
||||||
|
buttons_layout.addStretch()
|
||||||
|
|
||||||
main_layout.addLayout(buttons_layout)
|
main_layout.addLayout(buttons_layout)
|
||||||
|
|
||||||
# Progress bar
|
# Progress bars
|
||||||
self.progress_bar = QProgressBar()
|
self.progress_bar = QProgressBar()
|
||||||
main_layout.addWidget(self.progress_bar)
|
main_layout.addWidget(self.progress_bar)
|
||||||
|
|
||||||
|
self.build_progress = QProgressBar()
|
||||||
|
self.build_progress.setTextVisible(False)
|
||||||
|
self.build_progress.hide()
|
||||||
|
main_layout.addWidget(self.build_progress)
|
||||||
|
|
||||||
# Log areas
|
# Log areas
|
||||||
log_frame = QFrame()
|
log_frame = QFrame()
|
||||||
log_layout = QVBoxLayout(log_frame)
|
log_layout = QVBoxLayout(log_frame)
|
||||||
|
@ -557,6 +680,9 @@ class ZLUDA_GUI(QMainWindow):
|
||||||
|
|
||||||
main_layout.addWidget(log_frame)
|
main_layout.addWidget(log_frame)
|
||||||
|
|
||||||
|
# Add main tab
|
||||||
|
tabs.addTab(main_tab, "Main")
|
||||||
|
|
||||||
# Debug tab
|
# Debug tab
|
||||||
debug_tab = QWidget()
|
debug_tab = QWidget()
|
||||||
debug_layout = QVBoxLayout(debug_tab)
|
debug_layout = QVBoxLayout(debug_tab)
|
||||||
|
@ -586,27 +712,76 @@ class ZLUDA_GUI(QMainWindow):
|
||||||
|
|
||||||
debug_layout.addWidget(debug_log_frame)
|
debug_layout.addWidget(debug_log_frame)
|
||||||
|
|
||||||
# Add tabs
|
# Add debug tab
|
||||||
tabs.addTab(main_tab, "Main")
|
|
||||||
tabs.addTab(debug_tab, "Debug")
|
tabs.addTab(debug_tab, "Debug")
|
||||||
|
|
||||||
# Set window flags for proper window controls
|
# Set window flags for proper window controls
|
||||||
self.setWindowFlags(
|
self.setWindowFlags(
|
||||||
Qt.WindowType.Window |
|
Qt.Window |
|
||||||
Qt.WindowType.CustomizeWindowHint |
|
Qt.CustomizeWindowHint |
|
||||||
Qt.WindowType.WindowCloseButtonHint |
|
Qt.WindowCloseButtonHint |
|
||||||
Qt.WindowType.WindowMinimizeButtonHint |
|
Qt.WindowMinimizeButtonHint |
|
||||||
Qt.WindowType.WindowMaximizeButtonHint
|
Qt.WindowMaximizeButtonHint
|
||||||
)
|
)
|
||||||
|
|
||||||
# Maximize the window by default
|
# Apply dark theme stylesheet
|
||||||
self.setWindowState(Qt.WindowState.WindowMaximized)
|
self.setStyleSheet("""
|
||||||
|
QMainWindow, QWidget {
|
||||||
|
background-color: #1a1a1a;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
QGroupBox {
|
||||||
|
background-color: #212121;
|
||||||
|
border: 1px solid #2a2a2a;
|
||||||
|
border-radius: 6px;
|
||||||
|
margin-top: 12px;
|
||||||
|
padding: 15px;
|
||||||
|
}
|
||||||
|
QFrame {
|
||||||
|
background-color: #212121;
|
||||||
|
border: 1px solid #2a2a2a;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
QLineEdit {
|
||||||
|
background-color: #2a2a2a;
|
||||||
|
border: 1px solid #333333;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 6px 10px;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
QTextEdit {
|
||||||
|
background-color: #2a2a2a;
|
||||||
|
border: 1px solid #333333;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 8px;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
QPushButton[cssClass="browse"] {
|
||||||
|
background-color: #333333;
|
||||||
|
padding: 6px 12px;
|
||||||
|
min-width: 70px;
|
||||||
|
}
|
||||||
|
QTabWidget::pane {
|
||||||
|
border: 1px solid #2a2a2a;
|
||||||
|
border-radius: 6px;
|
||||||
|
background-color: #212121;
|
||||||
|
}
|
||||||
|
QTabBar::tab {
|
||||||
|
background-color: #2a2a2a;
|
||||||
|
color: #b0b0b0;
|
||||||
|
padding: 8px 16px;
|
||||||
|
border-top-left-radius: 4px;
|
||||||
|
border-top-right-radius: 4px;
|
||||||
|
}
|
||||||
|
QTabBar::tab:selected {
|
||||||
|
background-color: #212121;
|
||||||
|
color: #00e5ff;
|
||||||
|
border-bottom: 2px solid #00e5ff;
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
|
||||||
# Initialize variables
|
# Maximize the window by default
|
||||||
self.current_process = None
|
self.setWindowState(Qt.WindowMaximized)
|
||||||
self.process_monitor = None
|
|
||||||
self.download_thread = None
|
|
||||||
self.download_progress = None
|
|
||||||
|
|
||||||
def log(self, message):
|
def log(self, message):
|
||||||
"""Add a message to the main log area"""
|
"""Add a message to the main log area"""
|
||||||
|
@ -892,6 +1067,77 @@ class ZLUDA_GUI(QMainWindow):
|
||||||
# Reset process reference
|
# Reset process reference
|
||||||
self.current_process = None
|
self.current_process = None
|
||||||
|
|
||||||
|
def build_zluda(self):
|
||||||
|
"""Build ZLUDA from source."""
|
||||||
|
if not sys.platform.startswith('linux'):
|
||||||
|
QMessageBox.warning(self, "Error", "ZLUDA building is only supported on Linux")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Check for admin privileges
|
||||||
|
if not check_admin():
|
||||||
|
reply = QMessageBox.question(
|
||||||
|
self,
|
||||||
|
"Admin Privileges Required",
|
||||||
|
"Installing dependencies requires administrator privileges. Would you like to restart the application as admin?",
|
||||||
|
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No
|
||||||
|
)
|
||||||
|
|
||||||
|
if reply == QMessageBox.StandardButton.Yes:
|
||||||
|
self.log("Restarting with admin privileges...")
|
||||||
|
if restart_as_admin():
|
||||||
|
sys.exit(0) # Exit current instance
|
||||||
|
else:
|
||||||
|
QMessageBox.critical(self, "Error", "Failed to restart with admin privileges")
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
||||||
|
reply = QMessageBox.question(
|
||||||
|
self,
|
||||||
|
"Build ZLUDA",
|
||||||
|
"This will install required dependencies and build ZLUDA from source. Continue?",
|
||||||
|
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No
|
||||||
|
)
|
||||||
|
|
||||||
|
if reply == QMessageBox.StandardButton.No:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Disable buttons during build
|
||||||
|
self.build_button.setEnabled(False)
|
||||||
|
self.download_button.setEnabled(False)
|
||||||
|
self.run_button.setEnabled(False)
|
||||||
|
|
||||||
|
# Show and start progress bar
|
||||||
|
self.build_progress.setRange(0, 0) # Indeterminate mode
|
||||||
|
self.build_progress.show()
|
||||||
|
|
||||||
|
# Start build process
|
||||||
|
self.build_worker = BuildWorker()
|
||||||
|
self.build_worker.progress.connect(self.update_build_progress)
|
||||||
|
self.build_worker.finished.connect(self.build_finished)
|
||||||
|
self.build_worker.start()
|
||||||
|
|
||||||
|
def update_build_progress(self, message):
|
||||||
|
"""Update build progress in log area."""
|
||||||
|
self.log_area.append(f"[Build] {message}")
|
||||||
|
|
||||||
|
def build_finished(self, success, result):
|
||||||
|
"""Handle build completion."""
|
||||||
|
# Re-enable buttons
|
||||||
|
self.build_button.setEnabled(True)
|
||||||
|
self.download_button.setEnabled(True)
|
||||||
|
self.run_button.setEnabled(True)
|
||||||
|
|
||||||
|
# Hide progress bar
|
||||||
|
self.build_progress.hide()
|
||||||
|
|
||||||
|
if success:
|
||||||
|
self.log_area.append("[Build] ZLUDA built successfully!")
|
||||||
|
self.zluda_path.setText(result)
|
||||||
|
else:
|
||||||
|
self.log_area.append(f"[Build] Error: {result}")
|
||||||
|
QMessageBox.warning(self, "Build Error", f"Failed to build ZLUDA: {result}")
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
app = QApplication(sys.argv)
|
app = QApplication(sys.argv)
|
||||||
window = ZLUDA_GUI()
|
window = ZLUDA_GUI()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue