added actual progressbar to the version prepare menu

This commit is contained in:
Nixietab 2025-12-08 16:06:16 -03:00
parent da0f4449ea
commit 9bac8e2d6d
3 changed files with 171 additions and 45 deletions

BIN
drums.gif

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

View File

@ -3,6 +3,7 @@ import threading
import time import time
import shlex import shlex
import gc import gc
import re
from io import StringIO from io import StringIO
from PyQt5.QtWidgets import QDialog, QVBoxLayout, QLabel, QProgressBar, QPushButton, QHBoxLayout from PyQt5.QtWidgets import QDialog, QVBoxLayout, QLabel, QProgressBar, QPushButton, QHBoxLayout
from PyQt5.QtCore import Qt, pyqtSignal, QObject, QTimer, QEvent from PyQt5.QtCore import Qt, pyqtSignal, QObject, QTimer, QEvent
@ -40,7 +41,7 @@ class StreamingCapture(StringIO):
# Signal/Object might be deleted # Signal/Object might be deleted
pass pass
if not self.launch_detected: if not self.launch_detected and self.launch_signal:
lower_text = text.lower() lower_text = text.lower()
if "launching" in lower_text and ("game" in lower_text or "version" in lower_text or "minecraft" in lower_text): if "launching" in lower_text and ("game" in lower_text or "version" in lower_text or "minecraft" in lower_text):
self.launch_detected = True self.launch_detected = True
@ -193,8 +194,174 @@ class LaunchWindow(QDialog):
pass pass
class PrepareWindow(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("Preparing Version")
self.setModal(True)
self.resize(400, 150)
layout = QVBoxLayout()
self.status_label = QLabel("Initializing...")
self.status_label.setWordWrap(True)
self.status_label.setAlignment(Qt.AlignCenter)
layout.addWidget(self.status_label)
self.progress_bar = QProgressBar()
self.progress_bar.setRange(0, 0) # Indeterminate progress
self.progress_bar.setTextVisible(False)
layout.addWidget(self.progress_bar)
# Add a manual close/cancel button
button_layout = QHBoxLayout()
button_layout.addStretch()
self.cancel_button = QPushButton("Cancel")
self.cancel_button.clicked.connect(self.request_abort)
button_layout.addWidget(self.cancel_button)
layout.addLayout(button_layout)
self.setLayout(layout)
self.signals = LaunchSignals()
self.signals.log_update.connect(self.update_status)
self.signals.launch_complete.connect(self.on_prepare_complete)
self.signals.launch_aborted.connect(self.on_prepare_aborted)
self.signals.cleanup_done.connect(self.on_cleanup_done)
self.aborting = False
self.capture_streams = []
self.thread_running = False
self.success = False
def update_status(self, text):
# Parse output for progress updates
if "Downloading" in text and "libraries" in text:
try:
count = int(re.search(r'\d+', text).group())
self.status_label.setText(f"Downloading {count} libraries...")
except:
self.status_label.setText(text)
elif "Checking" in text and "assets" in text:
try:
count = int(re.search(r'\d+', text).group())
self.status_label.setText(f"Checking {count} assets...")
except:
self.status_label.setText(text)
elif "Jar file" in text and "downloaded" in text:
self.status_label.setText("Downloading game jar...")
elif "Checking libraries" in text:
self.status_label.setText("Checking libraries...")
else:
if len(text) > 100:
text = text[:97] + "..."
self.status_label.setText(text)
def on_prepare_complete(self):
if not self.aborting:
self.success = True
self.status_label.setText("Version prepared successfully!")
self.progress_bar.setRange(0, 100)
self.progress_bar.setValue(100)
self.cancel_button.setEnabled(False)
QTimer.singleShot(1500, self.accept)
def on_prepare_aborted(self):
self.status_label.setText("Preparation Aborted.")
self.progress_bar.setRange(0, 100)
self.progress_bar.setValue(0)
self.success = False
def on_cleanup_done(self):
self.thread_running = False
if not self.success:
super().reject()
def request_abort(self):
if self.thread_running and not self.aborting:
self.aborting = True
self.status_label.setText("Aborting...")
self.cancel_button.setEnabled(False)
# Signal streams to stop
for stream in self.capture_streams:
stream.abort_requested = True
elif not self.thread_running:
super().reject()
def reject(self):
self.request_abort()
def closeEvent(self, event):
if self.thread_running:
event.ignore()
self.request_abort()
else:
event.accept()
def prepare_version(self, version):
command = f"version prepare {version}"
self.thread_running = True
thread = threading.Thread(target=self._run_prepare, args=(command,), daemon=True)
thread.start()
def _run_prepare(self, command):
try:
modules_to_remove = [mod for mod in sys.modules if mod.startswith('zucaro')]
for mod in modules_to_remove:
del sys.modules[mod]
gc.collect()
from zucaro.cli.main import zucaro_cli
old_stdout, old_stderr = sys.stdout, sys.stderr
stdout_capture = StreamingCapture(self.signals.log_update, None)
stderr_capture = StreamingCapture(self.signals.log_update, None)
self.capture_streams = [stdout_capture, stderr_capture]
sys.stdout = stdout_capture
sys.stderr = stderr_capture
try:
zucaro_cli.main(args=shlex.split(command), standalone_mode=False)
except AbortException:
self.signals.launch_aborted.emit()
except SystemExit:
pass
except Exception as e:
self.signals.log_update.emit(f"Error: {str(e)}")
finally:
sys.stdout = old_stdout
sys.stderr = old_stderr
if not stdout_capture.abort_requested:
self.signals.launch_complete.emit()
modules_to_remove = [mod for mod in sys.modules if mod.startswith('zucaro')]
for mod in modules_to_remove:
del sys.modules[mod]
gc.collect()
self.signals.cleanup_done.emit()
except Exception as e:
try:
self.signals.log_update.emit(f"Error preparing version: {str(e)}")
self.signals.cleanup_done.emit()
except:
pass
def launch_instance_with_window(command, parent=None): def launch_instance_with_window(command, parent=None):
window = LaunchWindow(parent) window = LaunchWindow(parent)
window.launch_game(command) window.launch_game(command)
window.exec_() window.exec_()
return window return window
def prepare_version_with_window(version, parent=None):
window = PrepareWindow(parent)
window.prepare_version(version)
result = window.exec_()
return window.success

View File

@ -1402,21 +1402,7 @@ class zucaroVersionSelector(QWidget):
dialog.finished.connect(self.populate_installed_versions) dialog.finished.connect(self.populate_installed_versions)
dialog.exec_() dialog.exec_()
class DownloadThread(QThread):
completed = pyqtSignal(bool, str)
def __init__(self, version):
super().__init__()
self.version = version
def run(self):
try:
command = f"version prepare {self.version}"
modulecli.run_command(command)
self.completed.emit(True, f"Version {self.version} prepared successfully!")
except Exception as e:
error_message = f"Error preparing {self.version}: {str(e)}"
self.completed.emit(False, error_message)
class ModLoaderAndVersionMenu(QDialog): class ModLoaderAndVersionMenu(QDialog):
def __init__(self, parent=None): def __init__(self, parent=None):
@ -1795,39 +1781,12 @@ class ModLoaderAndVersionMenu(QDialog):
def update_download_button_state(self): def update_download_button_state(self):
self.download_button.setEnabled(self.version_combo.currentIndex() != -1) self.download_button.setEnabled(self.version_combo.currentIndex() != -1)
def show_popup(self):
self.popup = QDialog(self)
self.popup.setWindowTitle("Installing Version")
layout = QVBoxLayout(self.popup)
label = QLabel("The version is being installed...")
layout.addWidget(label)
movie = QMovie("drums.gif")
gif_label = QLabel()
gif_label.setMovie(movie)
layout.addWidget(gif_label)
movie.start()
self.popup.setGeometry(200, 200, 300, 200)
self.popup.setWindowModality(Qt.ApplicationModal)
self.popup.show()
def download_version(self, version): def download_version(self, version):
# Show the popup in the main thread success = loaddaemon.prepare_version_with_window(version, self)
self.show_popup()
self.download_thread = DownloadThread(version)
self.download_thread.completed.connect(self.on_download_completed)
self.download_thread.start()
def on_download_completed(self, success, message):
self.popup.close()
if success: if success:
QMessageBox.information(self, "Success", message) QMessageBox.information(self, "Success", f"Version {version} prepared successfully!")
else: else:
QMessageBox.critical(self, "Error", message) QMessageBox.critical(self, "Error", f"Failed to prepare version {version}.")
logging.error(message)
def populate_available_releases(self, version_combo, install_forge, install_fabric): def populate_available_releases(self, version_combo, install_forge, install_fabric):
try: try: