Code some part of the create profile page. Now it can load versions and types.
This commit is contained in:
@@ -14,6 +14,7 @@ from core_lib.qt.hack import move_window_to_center, is_light_theme, \
|
|||||||
recolor_icon
|
recolor_icon
|
||||||
from core_lib.qt import messagebox
|
from core_lib.qt import messagebox
|
||||||
from core_lib.launcher.object import Launcher as LauncherObject
|
from core_lib.launcher.object import Launcher as LauncherObject
|
||||||
|
from manager.game import GameManager
|
||||||
from manager.profile import ProfileManager
|
from manager.profile import ProfileManager
|
||||||
from manager.widgets import WidgetManager
|
from manager.widgets import WidgetManager
|
||||||
from window import LauncherMainWindow
|
from window import LauncherMainWindow
|
||||||
@@ -109,6 +110,7 @@ class Launcher(QApplication, LauncherObject):
|
|||||||
|
|
||||||
# Widget (Signal, Event)
|
# Widget (Signal, Event)
|
||||||
self.widget_manager = WidgetManager()
|
self.widget_manager = WidgetManager()
|
||||||
|
self.game_manager = GameManager(self)
|
||||||
|
|
||||||
# Manager
|
# Manager
|
||||||
self.profile_manager = ProfileManager(self, self.widget_manager)
|
self.profile_manager = ProfileManager(self, self.widget_manager)
|
||||||
@@ -116,8 +118,7 @@ class Launcher(QApplication, LauncherObject):
|
|||||||
# Window config
|
# Window config
|
||||||
self.setApplicationName("TestLauncher")
|
self.setApplicationName("TestLauncher")
|
||||||
self.main_window = LauncherMainWindow(
|
self.main_window = LauncherMainWindow(
|
||||||
app=self,
|
app=self
|
||||||
widget_manager=self.widget_manager,
|
|
||||||
)
|
)
|
||||||
self.main_window.setGeometry(0, 0, 800, 600)
|
self.main_window.setGeometry(0, 0, 800, 600)
|
||||||
move_window_to_center(self.main_window)
|
move_window_to_center(self.main_window)
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ class ProgressDialog(QDialog):
|
|||||||
row.finished = True
|
row.finished = True
|
||||||
|
|
||||||
if self.all_tasks_finished():
|
if self.all_tasks_finished():
|
||||||
self.close_timer.start(2000)
|
self.close_timer.start(600)
|
||||||
|
|
||||||
def all_tasks_finished(self):
|
def all_tasks_finished(self):
|
||||||
return bool(self.rows) and all(
|
return bool(self.rows) and all(
|
||||||
|
|||||||
+146
@@ -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)
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
from PySide6.QtCore import QObject
|
|
||||||
|
|
||||||
|
|
||||||
class RocketLauncher(QObject):
|
|
||||||
pass
|
|
||||||
+16
-16
@@ -1,24 +1,13 @@
|
|||||||
import queue
|
import queue
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
from PySide6.QtCore import QObject, Signal, Slot
|
from PySide6.QtCore import QObject, Signal
|
||||||
from PySide6.QtWidgets import QProgressDialog
|
|
||||||
|
|
||||||
from core_lib.qt import messagebox
|
class SignalTransferItem:
|
||||||
|
def __init__(self):
|
||||||
|
self.result_queue = queue.Queue()
|
||||||
class AskForRequest:
|
|
||||||
def __init__(self, title, message):
|
|
||||||
self.title = title
|
|
||||||
self.message = message
|
|
||||||
self.result_queue = queue.Queue(maxsize=1)
|
|
||||||
self.wait_event = threading.Event()
|
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):
|
def wait(self, timeout: int | None = 10):
|
||||||
return self.wait_event.wait(timeout)
|
return self.wait_event.wait(timeout)
|
||||||
|
|
||||||
@@ -28,12 +17,23 @@ class AskForRequest:
|
|||||||
except queue.Empty:
|
except queue.Empty:
|
||||||
return None
|
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):
|
class SelectPathRequest(AskForRequest):
|
||||||
def __init__(self, title, message, filter_raw=None):
|
def __init__(self, title, message, filter_raw=None):
|
||||||
super().__init__(title, message)
|
super().__init__(title, message)
|
||||||
self.filter = filter_raw
|
self.filter = filter_raw
|
||||||
|
|
||||||
|
|
||||||
class WidgetManager(QObject):
|
class WidgetManager(QObject):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(WidgetManager, self).__init__()
|
super(WidgetManager, self).__init__()
|
||||||
|
|||||||
+257
-14
@@ -1,14 +1,39 @@
|
|||||||
|
import threading
|
||||||
|
from collections.abc import Callable
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from PySide6.QtCore import QSize
|
from PySide6.QtCore import QSize, Qt, QObject, Signal, Slot
|
||||||
from PySide6.QtWidgets import QWidget, QVBoxLayout, QComboBox, QListWidget, QPushButton, QLabel, QHBoxLayout
|
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):
|
def __init__(self, app, parent=None):
|
||||||
super(CreateProfile, self).__init__(parent)
|
super(CreateProfilePage, self).__init__(parent)
|
||||||
self.app = app
|
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.main_layout = QVBoxLayout()
|
||||||
self.right_layout = QVBoxLayout()
|
self.right_layout = QVBoxLayout()
|
||||||
@@ -19,6 +44,7 @@ class CreateProfile(QWidget):
|
|||||||
self.version_type_dropdown = QComboBox()
|
self.version_type_dropdown = QComboBox()
|
||||||
self.version_type_dropdown.setObjectName("Dropdown")
|
self.version_type_dropdown.setObjectName("Dropdown")
|
||||||
|
|
||||||
|
# Mod loader
|
||||||
self.mod_loader_icon_label = QLabel()
|
self.mod_loader_icon_label = QLabel()
|
||||||
icon = self.app.get_icon(Path(self.app.icon_dir, "mod_loader_temp.png"))
|
icon = self.app.get_icon(Path(self.app.icon_dir, "mod_loader_temp.png"))
|
||||||
pixmap = icon.pixmap(QSize(100, 100))
|
pixmap = icon.pixmap(QSize(100, 100))
|
||||||
@@ -27,17 +53,33 @@ class CreateProfile(QWidget):
|
|||||||
self.mod_loader_dropdown = QComboBox()
|
self.mod_loader_dropdown = QComboBox()
|
||||||
self.mod_loader_dropdown.setObjectName("Dropdown")
|
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_type_label = QLabel("Version: Vanilla")
|
||||||
self.version_list_label = QLabel("Select a version to continue:")
|
self.version_list_label = QLabel("Select a version to continue:")
|
||||||
self.version_list = QListWidget()
|
self.version_list = QListWidget()
|
||||||
self.version_list.setObjectName("ListWidget")
|
self.version_list.setObjectName("ListWidget")
|
||||||
|
|
||||||
|
# Button
|
||||||
self.create_profile = QPushButton("Create Profile")
|
self.create_profile = QPushButton("Create Profile")
|
||||||
self.create_profile.setObjectName("Button")
|
self.create_profile.setObjectName("Button")
|
||||||
|
|
||||||
self.refresh_button = QPushButton("Refresh")
|
self.refresh_button = QPushButton("Refresh")
|
||||||
self.refresh_button.setObjectName("Button")
|
self.refresh_button.setObjectName("Button")
|
||||||
|
|
||||||
|
# Bind widget
|
||||||
self.main_layout.addWidget(self.version_list_label)
|
self.main_layout.addWidget(self.version_list_label)
|
||||||
self.main_layout.addWidget(self.version_list)
|
self.main_layout.addWidget(self.version_list)
|
||||||
self.operate_layout.addWidget(self.version_list_type_label)
|
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.refresh_button)
|
||||||
self.operate_layout.addWidget(self.create_profile)
|
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_version_label)
|
||||||
self.right_layout.addWidget(self.mod_loader_label)
|
self.right_layout.addWidget(
|
||||||
self.right_layout.addWidget(self.mod_loader_dropdown)
|
self.mod_loader_version_list,
|
||||||
self.right_layout.addWidget(self.version_type_label)
|
1,
|
||||||
self.right_layout.addWidget(self.version_type_dropdown)
|
)
|
||||||
self.right_layout.addStretch()
|
self.mod_loader_version_list.setSizePolicy(
|
||||||
|
QSizePolicy.Policy.Expanding,
|
||||||
|
QSizePolicy.Policy.Expanding,
|
||||||
|
)
|
||||||
self.right_layout.setContentsMargins(0, 20, 0, 0)
|
self.right_layout.setContentsMargins(0, 20, 0, 0)
|
||||||
|
|
||||||
self.setLayout(self.layout)
|
self.setLayout(self.layout)
|
||||||
self.layout.addLayout(self.main_layout)
|
self.content_layout.addLayout(
|
||||||
self.layout.addLayout(self.right_layout)
|
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)
|
||||||
|
|||||||
@@ -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
|
||||||
+17
-141
@@ -2,10 +2,8 @@ import json
|
|||||||
import subprocess
|
import subprocess
|
||||||
import textwrap
|
import textwrap
|
||||||
import threading
|
import threading
|
||||||
import time
|
|
||||||
import traceback
|
import traceback
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from uuid import uuid4
|
|
||||||
|
|
||||||
from PySide6.QtCore import Qt, QObject, Signal, Slot
|
from PySide6.QtCore import Qt, QObject, Signal, Slot
|
||||||
from PySide6.QtWidgets import QPushButton, QVBoxLayout, QPlainTextEdit, \
|
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.common import FileObject, download_multiple_files
|
||||||
from core_lib.common.exception import VersionNotSelectedException, UnsupportedPlatformException, \
|
from core_lib.common.exception import VersionNotSelectedException, UnsupportedPlatformException, \
|
||||||
NativeResolveException, DownloadException, DependencyException, VersionManifestFetchException, \
|
NativeResolveException, DownloadException, DependencyException, AssetManifestFetchException, \
|
||||||
VersionManifestSaveException, NoSpecifiedVersionKeyException, AssetManifestFetchException, \
|
|
||||||
AssetManifestSaveException, AssetDataKeyNotFoundException, VersionDataKeyNotFoundException
|
AssetManifestSaveException, AssetDataKeyNotFoundException, VersionDataKeyNotFoundException
|
||||||
from core_lib.game.argument import ArgumentMappings, parse_and_generate_arguments, find_game_and_jvm_args, \
|
from core_lib.game.argument import ArgumentMappings, parse_and_generate_arguments, find_game_and_jvm_args, \
|
||||||
generate_launch_command
|
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.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, \
|
from core_lib.game.version_info import (find_useful_part_in_specific_version_manifest, \
|
||||||
get_all_versions, get_available_version_types, \
|
get_all_versions, get_available_version_types,
|
||||||
fetch_and_save_version_manifest, get_specific_version_manifest_url_and_hash,
|
|
||||||
download_core_file)
|
download_core_file)
|
||||||
from core_lib.qt.hack import is_main_thread
|
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 core_lib.runtime.java import find_java_installations
|
||||||
|
from manager.game import ManifestManager
|
||||||
from manager.widgets import SelectPathRequest, AskForRequest
|
from manager.widgets import SelectPathRequest, AskForRequest
|
||||||
|
|
||||||
MAX_DOWNLOAD_ATTEMPTS = 3
|
MAX_DOWNLOAD_ATTEMPTS = 3
|
||||||
@@ -40,6 +36,7 @@ class TestPage(QWidget):
|
|||||||
self.app = app
|
self.app = app
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
self.widget_mgr = widget_manager
|
self.widget_mgr = widget_manager
|
||||||
|
self.manifest_mgr: ManifestManager = app.game_manager.manifest
|
||||||
|
|
||||||
self.resize(800, 600)
|
self.resize(800, 600)
|
||||||
self.setWindowFlags(self.windowFlags() | Qt.WindowType.WindowMaximizeButtonHint)
|
self.setWindowFlags(self.windowFlags() | Qt.WindowType.WindowMaximizeButtonHint)
|
||||||
@@ -262,7 +259,7 @@ class TestPage(QWidget):
|
|||||||
if not ver:
|
if not ver:
|
||||||
return
|
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 = (
|
arguments, asset_index, downloads, java_version, libraries, main_class, legacy_game_args = (
|
||||||
find_useful_part_in_specific_version_manifest(ver_data))
|
find_useful_part_in_specific_version_manifest(ver_data))
|
||||||
@@ -408,7 +405,7 @@ class TestPage(QWidget):
|
|||||||
def _load_all_types(self):
|
def _load_all_types(self):
|
||||||
self.signals.clean_ver_types_dropdown.emit()
|
self.signals.clean_ver_types_dropdown.emit()
|
||||||
|
|
||||||
data = self.get_cached_version_manifest()
|
data = self.manifest_mgr.get_cached_version_manifest()
|
||||||
if not data:
|
if not data:
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -418,7 +415,7 @@ class TestPage(QWidget):
|
|||||||
def _load_all_version(self, ver_type):
|
def _load_all_version(self, ver_type):
|
||||||
self.signals.clean_vers_dropdown.emit()
|
self.signals.clean_vers_dropdown.emit()
|
||||||
|
|
||||||
data = self.get_cached_version_manifest()
|
data = self.manifest_mgr.get_cached_version_manifest()
|
||||||
if not data:
|
if not data:
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -428,135 +425,14 @@ class TestPage(QWidget):
|
|||||||
)
|
)
|
||||||
self.signals.update_vers_dropdown.emit(vers)
|
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 ============
|
# ============ Game Files ============
|
||||||
#
|
#
|
||||||
# INFO: All are not in main thread
|
# INFO: All are not in main thread
|
||||||
def _download_client(self, ver: str):
|
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:
|
try:
|
||||||
ver_data = self.get_cached_version_manifest(ver)
|
ver_data = self.manifest_mgr.get_cached_version_manifest(ver)
|
||||||
|
|
||||||
if not ver_data:
|
if not ver_data:
|
||||||
self.widget_mgr.download_signal.failed.emit(
|
self.widget_mgr.download_signal.failed.emit(
|
||||||
@@ -611,11 +487,11 @@ class TestPage(QWidget):
|
|||||||
self.widget_mgr.signal.enable_widget.emit(self.button_2)
|
self.widget_mgr.signal.enable_widget.emit(self.button_2)
|
||||||
|
|
||||||
def _download_libraries(self, ver: str):
|
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:
|
try:
|
||||||
self.signals.clean_output.emit()
|
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:
|
if not ver_data:
|
||||||
self.widget_mgr.download_signal.failed.emit(
|
self.widget_mgr.download_signal.failed.emit(
|
||||||
@@ -694,11 +570,11 @@ class TestPage(QWidget):
|
|||||||
self.widget_mgr.signal.enable_widget.emit(self.button_3)
|
self.widget_mgr.signal.enable_widget.emit(self.button_3)
|
||||||
|
|
||||||
def _download_assets(self, ver: str):
|
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:
|
try:
|
||||||
self.signals.clean_output.emit()
|
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:
|
if not ver_data:
|
||||||
self.widget_mgr.download_signal.failed.emit(
|
self.widget_mgr.download_signal.failed.emit(
|
||||||
@@ -766,7 +642,7 @@ class TestPage(QWidget):
|
|||||||
|
|
||||||
def _parse_argument(self, ver):
|
def _parse_argument(self, ver):
|
||||||
try:
|
try:
|
||||||
ver_data = self.get_cached_version_manifest(ver)
|
ver_data = self.manifest_mgr.get_cached_version_manifest(ver)
|
||||||
|
|
||||||
if not ver_data:
|
if not ver_data:
|
||||||
return
|
return
|
||||||
@@ -815,7 +691,7 @@ class TestPage(QWidget):
|
|||||||
# , 3 = check assets
|
# , 3 = check assets
|
||||||
|
|
||||||
# Version manifest
|
# Version manifest
|
||||||
ver_data = self.get_cached_version_manifest(ver)
|
ver_data = self.manifest_mgr.get_cached_version_manifest(ver)
|
||||||
|
|
||||||
if not ver_data:
|
if not ver_data:
|
||||||
return
|
return
|
||||||
@@ -849,7 +725,7 @@ class TestPage(QWidget):
|
|||||||
current_status = 3
|
current_status = 3
|
||||||
default_assets_dir = Path(self.app.work_dir, "assets")
|
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):
|
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"
|
"Downloading missing assets"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -996,7 +872,7 @@ class TestPage(QWidget):
|
|||||||
# copy
|
# copy
|
||||||
pending = list(files)
|
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):
|
for retry_time in range(1, MAX_DOWNLOAD_ATTEMPTS + 1):
|
||||||
if not pending:
|
if not pending:
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ from core_lib.qt.messagebox import ask_yes_no_with_plain_text
|
|||||||
from constant import MAIN_REPO_URL
|
from constant import MAIN_REPO_URL
|
||||||
from manager.widgets import AskForRequest, SelectPathRequest
|
from manager.widgets import AskForRequest, SelectPathRequest
|
||||||
from pages.account import AccountPage
|
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
|
from pages.test import TestPage
|
||||||
|
|
||||||
class ToolBar(QtWidgets.QToolBar):
|
class ToolBar(QtWidgets.QToolBar):
|
||||||
@@ -97,10 +98,11 @@ class ToolBar(QtWidgets.QToolBar):
|
|||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
class LauncherMainWindow(QMainWindow):
|
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)
|
super(LauncherMainWindow, self).__init__(parent)
|
||||||
self.app = app
|
self.app = app
|
||||||
self.widget_manager = widget_manager
|
self.widget_manager = app.widget_manager
|
||||||
|
self.game_manager = app.game_manager
|
||||||
|
|
||||||
# Main layout
|
# Main layout
|
||||||
self.central = QStackedWidget()
|
self.central = QStackedWidget()
|
||||||
@@ -148,17 +150,23 @@ class LauncherMainWindow(QMainWindow):
|
|||||||
widget_manager=self.app.widget_manager
|
widget_manager=self.app.widget_manager
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.launch_profile_page = LaunchProfilePage(
|
||||||
|
app=self.app,
|
||||||
|
parent=self,
|
||||||
|
)
|
||||||
|
|
||||||
# Account page
|
# Account page
|
||||||
self.account_page = AccountPage(self.app, self)
|
self.account_page = AccountPage(self.app, self)
|
||||||
|
|
||||||
# Profile page
|
# Profile page
|
||||||
self.create_profile_page = CreateProfile(self.app, self)
|
self.create_profile_page = CreateProfilePage(self.app, self)
|
||||||
|
|
||||||
# Add page
|
# Add page
|
||||||
self.central.addWidget(self.home_page)
|
self.central.addWidget(self.home_page)
|
||||||
self.central.addWidget(self.test_page)
|
self.central.addWidget(self.test_page)
|
||||||
self.central.addWidget(self.account_page)
|
self.central.addWidget(self.account_page)
|
||||||
self.central.addWidget(self.create_profile_page)
|
self.central.addWidget(self.create_profile_page)
|
||||||
|
self.central.addWidget(self.launch_profile_page)
|
||||||
|
|
||||||
# Bind toolbar and center widget
|
# Bind toolbar and center widget
|
||||||
self.addToolBar(Qt.ToolBarArea.LeftToolBarArea, self.toolbar)
|
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 = 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")
|
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
|
# Home btn
|
||||||
self.home_button = self.toolbar.add_button(icon=self.app.get_icon(Path(self.app.icon_dir, "home.png")))
|
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.home_button)
|
||||||
self.toolbar.addWidget(self.open_test_page)
|
self.toolbar.addWidget(self.open_test_page)
|
||||||
self.toolbar.addWidget(self.open_create_profile_page)
|
self.toolbar.addWidget(self.open_create_profile_page)
|
||||||
|
self.toolbar.addWidget(self.open_launch_profile_page)
|
||||||
self.toolbar.setMovable(True)
|
self.toolbar.setMovable(True)
|
||||||
self.toolbar.setFloatable(False)
|
self.toolbar.setFloatable(False)
|
||||||
self.toolbar.setAllowedAreas(Qt.ToolBarArea.AllToolBarAreas)
|
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)
|
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(
|
self.open_account_page.clicked.connect(
|
||||||
lambda: self.set_current_page(self.account_page, self.open_account_page)
|
lambda: self.set_current_page(self.account_page, self.open_account_page)
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user