From 8d865c92a902c44efe79ca52996612281eac6250 Mon Sep 17 00:00:00 2001 From: Wei Hong Date: Thu, 30 Jul 2026 21:43:19 +0800 Subject: [PATCH] Code some part of the create profile page. Now it can load versions and types. --- app.py | 5 +- core_lib/qt/progress_dialog.py | 2 +- manager/game.py | 146 ++++++++++++++++++ manager/launcher.py | 5 - manager/widgets.py | 32 ++-- pages/create_profile.py | 271 +++++++++++++++++++++++++++++++-- pages/launch_profile.py | 7 + pages/test.py | 158 +++---------------- window.py | 25 ++- 9 files changed, 468 insertions(+), 183 deletions(-) create mode 100644 manager/game.py delete mode 100644 manager/launcher.py create mode 100644 pages/launch_profile.py diff --git a/app.py b/app.py index f66b2d8..b4a708a 100644 --- a/app.py +++ b/app.py @@ -14,6 +14,7 @@ from core_lib.qt.hack import move_window_to_center, is_light_theme, \ recolor_icon from core_lib.qt import messagebox from core_lib.launcher.object import Launcher as LauncherObject +from manager.game import GameManager from manager.profile import ProfileManager from manager.widgets import WidgetManager from window import LauncherMainWindow @@ -109,6 +110,7 @@ class Launcher(QApplication, LauncherObject): # Widget (Signal, Event) self.widget_manager = WidgetManager() + self.game_manager = GameManager(self) # Manager self.profile_manager = ProfileManager(self, self.widget_manager) @@ -116,8 +118,7 @@ class Launcher(QApplication, LauncherObject): # Window config self.setApplicationName("TestLauncher") self.main_window = LauncherMainWindow( - app=self, - widget_manager=self.widget_manager, + app=self ) self.main_window.setGeometry(0, 0, 800, 600) move_window_to_center(self.main_window) diff --git a/core_lib/qt/progress_dialog.py b/core_lib/qt/progress_dialog.py index ec2d8f3..6b72f9d 100644 --- a/core_lib/qt/progress_dialog.py +++ b/core_lib/qt/progress_dialog.py @@ -129,7 +129,7 @@ class ProgressDialog(QDialog): row.finished = True if self.all_tasks_finished(): - self.close_timer.start(2000) + self.close_timer.start(600) def all_tasks_finished(self): return bool(self.rows) and all( diff --git a/manager/game.py b/manager/game.py new file mode 100644 index 0000000..8ab9d85 --- /dev/null +++ b/manager/game.py @@ -0,0 +1,146 @@ +import json +import time +import traceback +from pathlib import Path +from uuid import uuid4 +from PySide6.QtCore import QObject + +from core_lib.common.common import FileObject +from core_lib.common.exception import VersionManifestFetchException, VersionManifestSaveException, \ + NoSpecifiedVersionKeyException +from core_lib.game.version_info import fetch_and_save_version_manifest, get_specific_version_manifest_url_and_hash + + +class ManifestManager: + def __init__(self, app): + self.app = app + self.widget_mgr = app.widget_manager + + # + # ============ Version Data Cache ============ + # + @property + def version_manifest_path(self) -> Path: + return Path(self.app.work_dir, "versions", "version_manifest_v2.json") + + def get_specific_version_manifest_path(self, target_version: str) -> Path | None: + return Path(self.app.work_dir, "versions", target_version, f"{target_version}.json") + + def switch_between_specific_path(self, specified_ver: str | None = None): + if specified_ver is None: + path = self.version_manifest_path + else: + path = self.get_specific_version_manifest_path(specified_ver) + + return path + + def _cache_version_manifest(self, specified_ver: str | None = None, root_manifest: dict | None = None) \ + -> tuple[bool, dict | None]: + data = None + path = self.switch_between_specific_path(specified_ver) + try: + data = fetch_and_save_version_manifest(path, target_version=specified_ver, root_manifest=root_manifest) + except VersionManifestFetchException as e: + self.widget_mgr.signal.show_error_message.emit( + e.user_message, + ) + except VersionManifestSaveException as e: + self.widget_mgr.signal.show_error_message.emit( + e.user_message, + ) + except Exception as e: + self.widget_mgr.signal.show_and_print_error_message.emit( + { + "error": traceback.format_exception(e), + "title": "Cache Error", + "message": "An unexpected error occurred while caching version manifest." + } + ) + + return True if data else False, data + + def _read_cached_version_manifest(self, specified_ver: str = None) -> dict | None: + path = self.switch_between_specific_path(specified_ver) + if not path.exists(): + self.widget_mgr.signal.show_error_message.emit( + "Version manifest missing after check. Please try again later.", + ) + return None + + try: + version_manifest = json.loads(path.read_text()) + return version_manifest + except Exception as e: + self.widget_mgr.signal.show_and_print_error_message.emit( + { + "error": traceback.format_exception(e), + "title": "Cache Error", + "message": "Unable to read version manifest." + } + ) + return None + + def get_cached_version_manifest(self, specified_ver: str = None, max_age: int = 3600) -> dict | None: + path = self.switch_between_specific_path(specified_ver) + file = FileObject(path) + sha1 = None + root_manifest = None + + if specified_ver: + root_manifest = self.get_cached_version_manifest() + + if root_manifest is None: + return None + + try: + _, sha1 = get_specific_version_manifest_url_and_hash(root_manifest, specified_ver) + except NoSpecifiedVersionKeyException as e: + self.widget_mgr.signal.show_error_message.emit( + f"Version {specified_ver} does not exist in version manifest.\n{e.user_message}" + ) + return None + + if file.exists: + age = time.time() - path.stat().st_mtime + + # For root manifest + if not specified_ver and age < max_age: + return self._read_cached_version_manifest() + + # Below are all for specific ver + if specified_ver and sha1 is None: + self.app.logger.warning("Specified version '{}' hash doesn't exist.".format(specified_ver)) + + # Also check sha1 + if specified_ver and age < max_age and file.verify("sha1", sha1): + return self._read_cached_version_manifest(specified_ver) + + result, data = self._cache_version_manifest(specified_ver, root_manifest=root_manifest) + + if not result: + return None + + return data + + def create_download_progress_callback(self, progress_title: str): + task_id = uuid4().hex + signals = self.widget_mgr.download_signal + + signals.started.emit(task_id, progress_title) + + def callback(name, percent): + signals.progress.emit( + task_id, + f"Downloading {name}", + percent, + ) + + return callback, task_id + + +class GameManager(QObject): + def __init__(self, app, parent=None): + super(GameManager, self).__init__(parent) + self.app = app + self.widget_mgr = app.widget_manager + self.manifest = ManifestManager(self.app) \ No newline at end of file diff --git a/manager/launcher.py b/manager/launcher.py deleted file mode 100644 index b7732cd..0000000 --- a/manager/launcher.py +++ /dev/null @@ -1,5 +0,0 @@ -from PySide6.QtCore import QObject - - -class RocketLauncher(QObject): - pass \ No newline at end of file diff --git a/manager/widgets.py b/manager/widgets.py index 5be683e..b9636e0 100644 --- a/manager/widgets.py +++ b/manager/widgets.py @@ -1,24 +1,13 @@ import queue import threading -from PySide6.QtCore import QObject, Signal, Slot -from PySide6.QtWidgets import QProgressDialog +from PySide6.QtCore import QObject, Signal -from core_lib.qt import messagebox - - -class AskForRequest: - def __init__(self, title, message): - self.title = title - self.message = message - self.result_queue = queue.Queue(maxsize=1) +class SignalTransferItem: + def __init__(self): + self.result_queue = queue.Queue() self.wait_event = threading.Event() - self.details = None # Set this if flag use_plain_text is enabled - - # flags - self.use_plain_text = True - def wait(self, timeout: int | None = 10): return self.wait_event.wait(timeout) @@ -28,12 +17,23 @@ class AskForRequest: except queue.Empty: return None +class AskForRequest(SignalTransferItem): + def __init__(self, title, message): + super(AskForRequest, self).__init__() + self.title = title + self.message = message + self.result_queue = queue.Queue(maxsize=1) + + self.details = None # Set this if flag use_plain_text is enabled + + # flags + self.use_plain_text = True + class SelectPathRequest(AskForRequest): def __init__(self, title, message, filter_raw=None): super().__init__(title, message) self.filter = filter_raw - class WidgetManager(QObject): def __init__(self): super(WidgetManager, self).__init__() diff --git a/pages/create_profile.py b/pages/create_profile.py index 62ba92c..87836f0 100644 --- a/pages/create_profile.py +++ b/pages/create_profile.py @@ -1,14 +1,39 @@ +import threading +from collections.abc import Callable from pathlib import Path -from PySide6.QtCore import QSize -from PySide6.QtWidgets import QWidget, QVBoxLayout, QComboBox, QListWidget, QPushButton, QLabel, QHBoxLayout +from PySide6.QtCore import QSize, Qt, QObject, Signal, Slot +from PySide6.QtWidgets import QWidget, QVBoxLayout, QComboBox, QListWidget, QPushButton, QLabel, QHBoxLayout, \ + QSizePolicy -class CreateProfile(QWidget): +from core_lib.game.version_info import get_all_versions, get_available_version_types +from manager.game import ManifestManager, GameManager +from manager.widgets import SignalTransferItem, WidgetManager + +LOADER_LIST = { + "fabric": { + "type": "modded", + "loadVersionMethod": None, + }, + "Don't Install": { + "type": "vanilla", + "loadVersionMethod": "official", + } +} + +class CreateProfilePage(QWidget): def __init__(self, app, parent=None): - super(CreateProfile, self).__init__(parent) + super(CreateProfilePage, self).__init__(parent) self.app = app - self.layout = QHBoxLayout() + # Manager + self.widget_mgr: WidgetManager = app.widget_manager + self.game_mgr: GameManager = app.game_manager + self.manifest_mgr: ManifestManager = self.game_mgr.manifest + + # Layout + self.layout = QVBoxLayout() + self.content_layout = QHBoxLayout() self.main_layout = QVBoxLayout() self.right_layout = QVBoxLayout() @@ -19,6 +44,7 @@ class CreateProfile(QWidget): self.version_type_dropdown = QComboBox() self.version_type_dropdown.setObjectName("Dropdown") + # Mod loader self.mod_loader_icon_label = QLabel() icon = self.app.get_icon(Path(self.app.icon_dir, "mod_loader_temp.png")) pixmap = icon.pixmap(QSize(100, 100)) @@ -27,17 +53,33 @@ class CreateProfile(QWidget): self.mod_loader_dropdown = QComboBox() self.mod_loader_dropdown.setObjectName("Dropdown") + # Mod loader layout + self.mod_config_layout = QHBoxLayout() + self.mod_config_fields_layout = QVBoxLayout() + + self.mod_loader_version_label = QLabel( + "Mod Loader Version" + ) + + self.mod_loader_version_list = QListWidget() + self.mod_loader_version_list.setObjectName( + "ListWidget" + ) + + # Version list self.version_list_type_label = QLabel("Version: Vanilla") self.version_list_label = QLabel("Select a version to continue:") self.version_list = QListWidget() self.version_list.setObjectName("ListWidget") + # Button self.create_profile = QPushButton("Create Profile") self.create_profile.setObjectName("Button") self.refresh_button = QPushButton("Refresh") self.refresh_button.setObjectName("Button") + # Bind widget self.main_layout.addWidget(self.version_list_label) self.main_layout.addWidget(self.version_list) self.operate_layout.addWidget(self.version_list_type_label) @@ -45,17 +87,218 @@ class CreateProfile(QWidget): self.operate_layout.addWidget(self.refresh_button) self.operate_layout.addWidget(self.create_profile) - self.main_layout.addLayout(self.operate_layout) + self.mod_config_fields_layout.addWidget( + self.mod_loader_label + ) + self.mod_config_fields_layout.addWidget( + self.mod_loader_dropdown + ) + self.mod_config_fields_layout.addWidget( + self.version_type_label + ) + self.mod_config_fields_layout.addWidget( + self.version_type_dropdown + ) + self.mod_config_fields_layout.addStretch() + self.mod_config_layout.addWidget( + self.mod_loader_icon_label, + alignment=Qt.AlignmentFlag.AlignTop, + ) + self.mod_config_layout.addLayout( + self.mod_config_fields_layout + ) + self.right_layout.addLayout( + self.mod_config_layout + ) - self.right_layout.addWidget(self.mod_loader_icon_label) - self.right_layout.addWidget(self.mod_loader_label) - self.right_layout.addWidget(self.mod_loader_dropdown) - self.right_layout.addWidget(self.version_type_label) - self.right_layout.addWidget(self.version_type_dropdown) - self.right_layout.addStretch() + self.right_layout.addWidget(self.mod_loader_version_label) + self.right_layout.addWidget( + self.mod_loader_version_list, + 1, + ) + self.mod_loader_version_list.setSizePolicy( + QSizePolicy.Policy.Expanding, + QSizePolicy.Policy.Expanding, + ) self.right_layout.setContentsMargins(0, 20, 0, 0) self.setLayout(self.layout) - self.layout.addLayout(self.main_layout) - self.layout.addLayout(self.right_layout) + self.content_layout.addLayout( + self.main_layout, + stretch=2, + ) + self.content_layout.addLayout( + self.right_layout, + stretch=1, + ) + self.layout.addLayout( + self.content_layout, + stretch=1, + ) + self.layout.addLayout( + self.operate_layout, + ) + + # Signals + self.signals = self.PageSignal() + + self.signals.get_selected_type.connect(self.get_selected_type) + self.signals.get_selected_version.connect(self.get_selected_version) + self.signals.update_type_dropdown.connect(self.update_type_dropdown) + self.signals.update_version_dropdown.connect(self.update_version_dropdown) + + # events + self.type_list_loading = threading.Event() + self.version_list_loading = threading.Event() + + self.init_all() + + class PageSignal(QObject): + mod_loader_dropdown_changed = Signal(str) + load_version_list = Signal() + + # widget + clean_list = Signal(object) + update_version_dropdown = Signal(list) + update_type_dropdown = Signal(list) + update_mod_loader_icon_label = Signal(object) + update_mod_loader_dropdown = Signal(list) + update_mod_loader_version_list = Signal(list) + + get_selected_version = Signal(object) + get_selected_type = Signal(object) + + # + # Signal Handler + # + @staticmethod + def clean_list(target_list: QListWidget | QComboBox): + if not isinstance(target_list, QListWidget | QComboBox): + raise TypeError("Target list is not a QListWidget") + + target_list.clear() + + @Slot(dict) + def get_selected_type(self, item: SignalTransferItem): + if not isinstance(item, SignalTransferItem): + raise TypeError("Item is not a SignalTransferItem") + + selected_type = self.version_type_dropdown.currentText() + + if not selected_type: + item.result_queue.put(None) + else: + item.result_queue.put(selected_type) + + item.wait_event.set() + + @Slot(object) + def get_selected_version(self, item: SignalTransferItem): + if not isinstance(item, SignalTransferItem): + raise TypeError("Item is not a SignalTransferItem") + + selected_item = self.version_list.currentItem() + + if selected_item is None: + item.result_queue.put(None) + else: + item.result_queue.put(selected_item.text()) + + item.wait_event.set() + + def update_type_dropdown(self, types: list): + # Move release to top + if "release" in types: + types.pop(types.index("release")) + types.insert(0, "release") + + self.version_type_dropdown.addItems(types) + + if len(self.version_type_dropdown.currentText()) == 0: + self.version_type_dropdown.setCurrentIndex(0) + + def update_version_dropdown(self, versions: list): + ids = [] + + for version in versions: + ver_id = version.get("id", None) + if ver_id is not None: + ids.append(ver_id) + + self.version_list.addItems(ids) + + selected_item = self.version_list.currentItem() + + if selected_item is None and len(ids) > 0: + self.version_list.setCurrentRow(0) + + # + # Thread Starter + # + def init_all(self): + self.start_load_type_list(callback=self.start_load_version_list) + + def start_load_type_list(self, callback=None): + thread = threading.Thread(target=self.load_type_list, args=(callback,)) + thread.start() + + def start_load_version_list(self): + thread = threading.Thread(target=self.load_version_list) + thread.start() + + def start_load_mod_loader_list_and_version(self): + pass + + # + # Background Thread + # + def load_type_list(self, callback: Callable[None]=None): + # Cleanup + self.signals.clean_list.emit(self.version_type_dropdown) + + manifest = self.manifest_mgr.get_cached_version_manifest() + if not manifest: + return + + ver_types = get_available_version_types(manifest) + + self.signals.update_type_dropdown.emit(ver_types) + + if callable(callback): + callback() + + def load_version_list(self, callback: Callable[Callable[None] | None]=None): + # Cleanup + self.version_list.clear() + + # Get selected version type + request = SignalTransferItem() + self.signals.get_selected_type.emit(request) + + if not request.wait(10): + self.widget_mgr.signal.show_error_message.emit( + "Timeout while waiting for selected type.", + ) + return + + selected_type = request.result() + + if not selected_type: + self.widget_mgr.signal.show_warning_message.emit( + "No type selected.", + ) + return + + # Fetch (or read) manifest + manifest = self.manifest_mgr.get_cached_version_manifest() + versions = get_all_versions( + manifest, + specified_type=selected_type + ) + + # Update list + self.update_version_dropdown(versions) + + if callable(callback): + callback(None) diff --git a/pages/launch_profile.py b/pages/launch_profile.py new file mode 100644 index 0000000..c7c78b7 --- /dev/null +++ b/pages/launch_profile.py @@ -0,0 +1,7 @@ +from PySide6.QtWidgets import QWidget + + +class LaunchProfilePage(QWidget): + def __init__(self, app, parent=None): + super(LaunchProfilePage, self).__init__(parent) + self.app = app \ No newline at end of file diff --git a/pages/test.py b/pages/test.py index 38dc5a9..da0998f 100644 --- a/pages/test.py +++ b/pages/test.py @@ -2,10 +2,8 @@ import json import subprocess import textwrap import threading -import time import traceback from pathlib import Path -from uuid import uuid4 from PySide6.QtCore import Qt, QObject, Signal, Slot from PySide6.QtWidgets import QPushButton, QVBoxLayout, QPlainTextEdit, \ @@ -13,8 +11,7 @@ from PySide6.QtWidgets import QPushButton, QVBoxLayout, QPlainTextEdit, \ from core_lib.common.common import FileObject, download_multiple_files from core_lib.common.exception import VersionNotSelectedException, UnsupportedPlatformException, \ - NativeResolveException, DownloadException, DependencyException, VersionManifestFetchException, \ - VersionManifestSaveException, NoSpecifiedVersionKeyException, AssetManifestFetchException, \ + NativeResolveException, DownloadException, DependencyException, AssetManifestFetchException, \ AssetManifestSaveException, AssetDataKeyNotFoundException, VersionDataKeyNotFoundException from core_lib.game.argument import ArgumentMappings, parse_and_generate_arguments, find_game_and_jvm_args, \ generate_launch_command @@ -22,12 +19,11 @@ from core_lib.game.asset import download_assets, check_assets_exist, find_correc from core_lib.game.library_core import generate_classpath, download_full_libraries, check_full_libraries_exists from core_lib.game.version_info import (find_useful_part_in_specific_version_manifest, \ - get_all_versions, get_available_version_types, \ - fetch_and_save_version_manifest, get_specific_version_manifest_url_and_hash, + get_all_versions, get_available_version_types, download_core_file) from core_lib.qt.hack import is_main_thread -from core_lib.qt.progress_dialog import ProgressDialog from core_lib.runtime.java import find_java_installations +from manager.game import ManifestManager from manager.widgets import SelectPathRequest, AskForRequest MAX_DOWNLOAD_ATTEMPTS = 3 @@ -40,6 +36,7 @@ class TestPage(QWidget): self.app = app self.parent = parent self.widget_mgr = widget_manager + self.manifest_mgr: ManifestManager = app.game_manager.manifest self.resize(800, 600) self.setWindowFlags(self.windowFlags() | Qt.WindowType.WindowMaximizeButtonHint) @@ -262,7 +259,7 @@ class TestPage(QWidget): if not ver: return - ver_data = self.get_cached_version_manifest(ver) + ver_data = self.manifest_mgr.get_cached_version_manifest(ver) arguments, asset_index, downloads, java_version, libraries, main_class, legacy_game_args = ( find_useful_part_in_specific_version_manifest(ver_data)) @@ -408,7 +405,7 @@ class TestPage(QWidget): def _load_all_types(self): self.signals.clean_ver_types_dropdown.emit() - data = self.get_cached_version_manifest() + data = self.manifest_mgr.get_cached_version_manifest() if not data: return @@ -418,7 +415,7 @@ class TestPage(QWidget): def _load_all_version(self, ver_type): self.signals.clean_vers_dropdown.emit() - data = self.get_cached_version_manifest() + data = self.manifest_mgr.get_cached_version_manifest() if not data: return @@ -428,135 +425,14 @@ class TestPage(QWidget): ) self.signals.update_vers_dropdown.emit(vers) - # - # ============ Version Data Cache ============ - # - @property - def version_manifest_path(self) -> Path: - return Path(self.app.work_dir, "versions", "version_manifest_v2.json") - - def get_specific_version_manifest_path(self, target_version: str) -> Path | None: - return Path(self.app.work_dir, "versions", target_version, f"{target_version}.json") - - def switch_between_specific_path(self, specified_ver: str | None = None): - if specified_ver is None: - path = self.version_manifest_path - else: - path = self.get_specific_version_manifest_path(specified_ver) - - return path - - def _cache_version_manifest(self, specified_ver: str | None = None, root_manifest: dict | None = None) \ - -> tuple[bool, dict | None]: - data = None - path = self.switch_between_specific_path(specified_ver) - try: - data = fetch_and_save_version_manifest(path, target_version=specified_ver, root_manifest=root_manifest) - except VersionManifestFetchException as e: - self.widget_mgr.signal.show_error_message.emit( - e.user_message, - ) - except VersionManifestSaveException as e: - self.widget_mgr.signal.show_error_message.emit( - e.user_message, - ) - except Exception as e: - self.widget_mgr.signal.show_and_print_error_message.emit( - { - "error": traceback.format_exception(e), - "title": "Cache Error", - "message": "An unexpected error occurred while caching version manifest." - } - ) - - return True if data else False, data - - def _read_cached_version_manifest(self, specified_ver: str = None) -> dict | None: - path = self.switch_between_specific_path(specified_ver) - if not path.exists(): - self.widget_mgr.signal.show_error_message.emit( - "Version manifest missing after check. Please try again later.", - ) - return None - - try: - version_manifest = json.loads(path.read_text()) - return version_manifest - except Exception as e: - self.widget_mgr.signal.show_and_print_error_message.emit( - { - "error": traceback.format_exception(e), - "title": "Cache Error", - "message": "Unable to read version manifest." - } - ) - return None - - def get_cached_version_manifest(self, specified_ver: str = None, max_age: int = 3600) -> dict | None: - path = self.switch_between_specific_path(specified_ver) - file = FileObject(path) - sha1 = None - root_manifest = None - - if specified_ver: - root_manifest = self.get_cached_version_manifest() - - if root_manifest is None: - return None - - try: - _, sha1 = get_specific_version_manifest_url_and_hash(root_manifest, specified_ver) - except NoSpecifiedVersionKeyException as e: - self.widget_mgr.signal.show_error_message.emit( - f"Version {specified_ver} does not exist in version manifest.\n{e.user_message}" - ) - return None - - if file.exists: - age = time.time() - path.stat().st_mtime - - # For root manifest - if not specified_ver and age < max_age: - return self._read_cached_version_manifest() - - # Below are all for specific ver - if specified_ver and sha1 is None: - self.app.logger.warning("Specified version '{}' hash doesn't exist.".format(specified_ver)) - - # Also check sha1 - if specified_ver and age < max_age and file.verify("sha1", sha1): - return self._read_cached_version_manifest(specified_ver) - - result, data = self._cache_version_manifest(specified_ver, root_manifest=root_manifest) - - if not result: - return None - - return data - - def create_download_progress_callback(self, progress_title: str): - task_id = uuid4().hex - signals = self.widget_mgr.download_signal - - signals.started.emit(task_id, progress_title) - - def callback(name, percent): - signals.progress.emit( - task_id, - f"Downloading {name}", - percent, - ) - - return callback, task_id - # # ============ Game Files ============ # # INFO: All are not in main thread def _download_client(self, ver: str): - callback, task_id = self.create_download_progress_callback(f"Downloading {ver} client") + callback, task_id = self.manifest_mgr.create_download_progress_callback(f"Downloading {ver} client") try: - ver_data = self.get_cached_version_manifest(ver) + ver_data = self.manifest_mgr.get_cached_version_manifest(ver) if not ver_data: self.widget_mgr.download_signal.failed.emit( @@ -611,11 +487,11 @@ class TestPage(QWidget): self.widget_mgr.signal.enable_widget.emit(self.button_2) def _download_libraries(self, ver: str): - callback, task_id = self.create_download_progress_callback(f"Downloading libraries for version {ver}") + callback, task_id = self.manifest_mgr.create_download_progress_callback(f"Downloading libraries for version {ver}") try: self.signals.clean_output.emit() - ver_data = self.get_cached_version_manifest(ver) + ver_data = self.manifest_mgr.get_cached_version_manifest(ver) if not ver_data: self.widget_mgr.download_signal.failed.emit( @@ -694,11 +570,11 @@ class TestPage(QWidget): self.widget_mgr.signal.enable_widget.emit(self.button_3) def _download_assets(self, ver: str): - callback, task_id = self.create_download_progress_callback(f"Downloading assets for version {ver}") + callback, task_id = self.manifest_mgr.create_download_progress_callback(f"Downloading assets for version {ver}") try: self.signals.clean_output.emit() - ver_data = self.get_cached_version_manifest(ver) + ver_data = self.manifest_mgr.get_cached_version_manifest(ver) if not ver_data: self.widget_mgr.download_signal.failed.emit( @@ -766,7 +642,7 @@ class TestPage(QWidget): def _parse_argument(self, ver): try: - ver_data = self.get_cached_version_manifest(ver) + ver_data = self.manifest_mgr.get_cached_version_manifest(ver) if not ver_data: return @@ -815,7 +691,7 @@ class TestPage(QWidget): # , 3 = check assets # Version manifest - ver_data = self.get_cached_version_manifest(ver) + ver_data = self.manifest_mgr.get_cached_version_manifest(ver) if not ver_data: return @@ -849,7 +725,7 @@ class TestPage(QWidget): current_status = 3 default_assets_dir = Path(self.app.work_dir, "assets") if not check_assets_exist(ver_data, default_assets_dir, launcher_root=self.app.work_dir): - callback, task_id = self.create_download_progress_callback( + callback, task_id = self.manifest_mgr.create_download_progress_callback( "Downloading missing assets" ) @@ -996,7 +872,7 @@ class TestPage(QWidget): # copy pending = list(files) - callback, task_id = self.create_download_progress_callback(f"Redownloading {len(files)} files.") + callback, task_id = self.manifest_mgr.create_download_progress_callback(f"Redownloading {len(files)} files.") for retry_time in range(1, MAX_DOWNLOAD_ATTEMPTS + 1): if not pending: diff --git a/window.py b/window.py index a430a91..9cdf6d9 100644 --- a/window.py +++ b/window.py @@ -14,7 +14,8 @@ from core_lib.qt.messagebox import ask_yes_no_with_plain_text from constant import MAIN_REPO_URL from manager.widgets import AskForRequest, SelectPathRequest from pages.account import AccountPage -from pages.create_profile import CreateProfile +from pages.create_profile import CreateProfilePage +from pages.launch_profile import LaunchProfilePage from pages.test import TestPage class ToolBar(QtWidgets.QToolBar): @@ -97,10 +98,11 @@ class ToolBar(QtWidgets.QToolBar): self.update() class LauncherMainWindow(QMainWindow): - def __init__(self, / ,app, widget_manager, parent: QWidget | None=None): + def __init__(self, / ,app, parent: QWidget | None=None): super(LauncherMainWindow, self).__init__(parent) self.app = app - self.widget_manager = widget_manager + self.widget_manager = app.widget_manager + self.game_manager = app.game_manager # Main layout self.central = QStackedWidget() @@ -148,17 +150,23 @@ class LauncherMainWindow(QMainWindow): widget_manager=self.app.widget_manager ) + self.launch_profile_page = LaunchProfilePage( + app=self.app, + parent=self, + ) + # Account page self.account_page = AccountPage(self.app, self) # Profile page - self.create_profile_page = CreateProfile(self.app, self) + self.create_profile_page = CreateProfilePage(self.app, self) # Add page self.central.addWidget(self.home_page) self.central.addWidget(self.test_page) self.central.addWidget(self.account_page) self.central.addWidget(self.create_profile_page) + self.central.addWidget(self.launch_profile_page) # Bind toolbar and center widget self.addToolBar(Qt.ToolBarArea.LeftToolBarArea, self.toolbar) @@ -174,6 +182,10 @@ class LauncherMainWindow(QMainWindow): self.open_test_page = self.toolbar.add_button(" Tests", icon=self.app.get_icon(Path(self.app.icon_dir, "debug.png"))) self.open_test_page.setObjectName("open_test_page") + # Launch profile btn + self.open_launch_profile_page = self.toolbar.add_button(" Launch Profile", + icon=self.app.get_icon(Path(self.app.icon_dir, "profile_default.png"))) + # Home btn self.home_button = self.toolbar.add_button(icon=self.app.get_icon(Path(self.app.icon_dir, "home.png"))) @@ -187,6 +199,7 @@ class LauncherMainWindow(QMainWindow): self.toolbar.addWidget(self.home_button) self.toolbar.addWidget(self.open_test_page) self.toolbar.addWidget(self.open_create_profile_page) + self.toolbar.addWidget(self.open_launch_profile_page) self.toolbar.setMovable(True) self.toolbar.setFloatable(False) self.toolbar.setAllowedAreas(Qt.ToolBarArea.AllToolBarAreas) @@ -204,6 +217,10 @@ class LauncherMainWindow(QMainWindow): lambda: self.set_current_page(self.test_page, self.open_test_page) ) + self.open_launch_profile_page.clicked.connect( + lambda: self.set_current_page(self.launch_profile_page, self.open_launch_profile_page) + ) + self.open_account_page.clicked.connect( lambda: self.set_current_page(self.account_page, self.open_account_page) )