Add select and fcntl modules to PyInstaller hiddenimports

- Added 'select' and 'fcntl' modules to hiddenimports in build_autoinstaller_gui.py
- These modules are used for non-blocking I/O operations in the copy_live_system method
- Ensures PyInstaller includes these standard library modules in the frozen binary
- Prevents import errors when running the frozen AutoInstaller GUI binary
- Required for dynamic progress updates during system copy process

This ensures the PyInstaller frozen binary has all necessary modules
for the enhanced copy progress functionality with non-blocking I/O.
parent ec3319e2
......@@ -10,6 +10,8 @@ import subprocess
import threading
import re
import time
import select
import fcntl
from pathlib import Path
from datetime import datetime
import glob
......@@ -417,24 +419,24 @@ class InstallerWorker(QThread):
self.status_updated.emit("Detecting devices...")
self.progress_updated.emit(10)
# Step 1: Device Detection
# Step 1: Device Detection (5%)
usb_device = self.detect_usb_device()
target_disk = self.detect_target_disk(usb_device)
self.config['usb_device'] = usb_device
self.config['target_disk'] = target_disk
self.step_completed.emit("Device detection completed")
self.progress_updated.emit(20)
self.progress_updated.emit(5)
# Step 2: Timezone (use config or default)
# Step 2: Timezone (10%)
timezone = self.config.get('timezone', 'UTC')
self.log(f"Setting timezone to {timezone}")
self.status_updated.emit(f"Setting timezone: {timezone}")
self.step_completed.emit("Timezone set")
self.progress_updated.emit(30)
self.progress_updated.emit(10)
# Step 3: Network Configuration (moved before mounting)
# Step 3: Network Configuration (15%)
if self.config.get('configure_network', False):
self.status_updated.emit("Preparing network configuration...")
# Just store the config for now, will apply after mounting
......@@ -443,55 +445,55 @@ class InstallerWorker(QThread):
else:
self.log("Skipping network configuration")
self.progress_updated.emit(40)
self.progress_updated.emit(15)
# Step 4: Find Preseed
# Step 4: Find Preseed (20%)
preseed_file = self.find_preseed()
self.config['preseed_file'] = preseed_file
self.step_completed.emit("Configuration loaded")
self.progress_updated.emit(50)
self.progress_updated.emit(20)
# Step 5: Confirmation (already handled in GUI)
# Step 5: Confirmation (25%)
self.status_updated.emit("User confirmed installation")
# Step 6: Partition Disk
# Step 6: Partition Disk (30%)
self.status_updated.emit("Partitioning target disk...")
self.partition_disk(target_disk)
self.step_completed.emit("Disk partitioned")
self.progress_updated.emit(60)
self.progress_updated.emit(30)
# Step 7: Mount Target
# Step 7: Mount Target (35%)
self.status_updated.emit("Mounting target filesystem...")
target_mount = "/target"
self.mount_target(target_disk, target_mount)
self.step_completed.emit("Target mounted")
self.progress_updated.emit(65)
self.progress_updated.emit(35)
# Step 8: Copy Live System
# Step 8: Copy Live System (85%) - Major step, 50% of total progress
self.status_updated.emit("Copying live system to disk...")
self.copy_live_system(target_mount)
self.step_completed.emit("System copied")
self.progress_updated.emit(75)
self.progress_updated.emit(85)
# Step 9: Configure Target System (includes network configuration)
# Step 9: Configure Target System (90%)
self.status_updated.emit("Configuring target system...")
self.configure_target_system(target_mount)
self.step_completed.emit("System configured")
self.progress_updated.emit(95)
self.progress_updated.emit(90)
# Step 12: Install Bootloader
# Step 10: Install Bootloader (95%)
self.status_updated.emit("Installing bootloader...")
self.install_bootloader(target_mount, target_disk)
self.step_completed.emit("Bootloader installed")
self.progress_updated.emit(98)
self.progress_updated.emit(95)
# Post-Install Setup
# Step 11: Post-Install Setup (100%)
self.status_updated.emit("Running post-installation setup...")
self.run_post_install(target_mount)
self.step_completed.emit("Post-install complete")
......@@ -719,12 +721,12 @@ class InstallerWorker(QThread):
self.log("Target mounted")
def copy_live_system(self, target_mount):
"""Copy live system with detailed progress output"""
self.log("Starting system copy with detailed progress...")
"""Copy live system with dynamic progress updates"""
self.log("Starting system copy with dynamic progress...")
# Use rsync with progress and verbose output
# Use rsync with progress output for dynamic updates
cmd = [
'rsync', '-av', '--progress', '--stats',
'rsync', '-av', '--progress', '--stats', '--human-readable',
'--exclude=/proc', '--exclude=/sys', '--exclude=/dev',
'--exclude=/tmp', '--exclude=/run', '--exclude=/mnt',
'--exclude=/media', '--exclude=/lost+found',
......@@ -745,17 +747,15 @@ class InstallerWorker(QThread):
universal_newlines=True
)
# Read output line by line and send to GUI (non-blocking)
import select
import fcntl
import os
# Make stdout non-blocking
fd = process.stdout.fileno()
fl = fcntl.fcntl(fd, fcntl.F_GETFL)
fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
# Progress tracking variables
copy_progress_start = 35 # Start at 35%
copy_progress_end = 85 # End at 85%
copy_progress_range = copy_progress_end - copy_progress_start
last_update_time = 0
total_files = 0
processed_files = 0
while True:
# Check if process is still running
if process.poll() is not None:
......@@ -777,8 +777,37 @@ class InstallerWorker(QThread):
if ready:
output = process.stdout.readline()
if output and output.strip():
self.copy_progress.emit(output.strip())
line = output.strip()
self.copy_progress.emit(line)
# Parse rsync progress for dynamic updates
# Look for lines like: "12,345 100% 123.45MB/s 0:12:34 (xfr#1234, to-chk=5678/9012)"
if '%' in line and ('xfr#' in line or 'to-chk=' in line):
try:
# Extract progress information
parts = line.split()
for part in parts:
if 'xfr#' in part:
# Extract transferred files count
xfr_part = part.split('xfr#')[1].split(',')[0]
processed_files = int(xfr_part) if xfr_part.isdigit() else processed_files
elif 'to-chk=' in part:
# Extract total files count
chk_part = part.split('to-chk=')[1].split('/')[1] if '/' in part else '0'
total_files = int(chk_part) if chk_part.isdigit() else total_files
# Calculate progress percentage within copy range
if total_files > 0:
file_progress = processed_files / total_files
current_progress = copy_progress_start + (file_progress * copy_progress_range)
current_progress = min(current_progress, copy_progress_end)
self.progress_updated.emit(int(current_progress))
except (ValueError, IndexError):
pass
last_update_time = time.time()
except:
# Handle non-blocking read errors gracefully
pass
......@@ -796,6 +825,8 @@ class InstallerWorker(QThread):
if process.returncode != 0:
raise subprocess.CalledProcessError(process.returncode, cmd)
# Ensure we reach the end of copy progress range
self.progress_updated.emit(copy_progress_end)
self.log("System copy completed successfully")
except subprocess.CalledProcessError as e:
......
......@@ -23,7 +23,7 @@ a = Analysis(
pathex=['.'],
binaries=[],
datas=[],
hiddenimports=['PyQt6.QtCore', 'PyQt6.QtGui', 'PyQt6.QtWidgets', 'PyQt6.QtGui.QShortcut', 'PyQt6.QtGui.QKeySequence', 'subprocess', 'threading'],
hiddenimports=['PyQt6.QtCore', 'PyQt6.QtGui', 'PyQt6.QtWidgets', 'PyQt6.QtGui.QShortcut', 'PyQt6.QtGui.QKeySequence', 'subprocess', 'threading', 'select', 'fcntl'],
hookspath=[],
hooksconfig={{}},
runtime_hooks=[],
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment