Now launch game process no longer blocking main thread.

This commit is contained in:
wei
2026-08-03 18:04:40 +08:00
parent e657a9642e
commit ed9d25023d
5 changed files with 468 additions and 97 deletions
+101 -52
View File
@@ -1,13 +1,13 @@
from pathlib import Path
from PySide6.QtCore import Qt, Signal
from PySide6.QtCore import Qt, Signal, Slot, QTimer
from PySide6.QtGui import QIcon, QMouseEvent, QHideEvent, QPixmap, QPainter
from PySide6.QtWidgets import (QWidget, QVBoxLayout, QLabel, QHBoxLayout, QPushButton, QScrollArea, QFrame,
QSizePolicy, QApplication)
from manager.game import LaunchManager
from manager.game import LaunchManager, LaunchResult
from core_lib.game.version_info import LATEST_VERSION_ALIASES
from manager.profile import ProfileSummary
from manager.profile import ProfileSummary, LaunchProfile, ProfileManager
from manager.widgets import AskForRequest, WidgetManager
from pages.process_log import ProcessLogWindow
@@ -161,6 +161,9 @@ class ProfilePopup(QFrame):
)
def load_profiles(self, profiles: list[ProfileSummary]) -> None:
# Clean old profiles
self.clean_profiles()
for profile in profiles:
icon_path = profile.icon
@@ -176,6 +179,14 @@ class ProfilePopup(QFrame):
item.set_icon(icon_path)
self.content_layout.addWidget(item)
def clean_profiles(self) -> None:
while self.content_layout.count():
layout_item = self.content_layout.takeAt(0)
widget = layout_item.widget()
if widget is not None:
widget.hide()
widget.deleteLater()
def hideEvent(self, event: QHideEvent) -> None:
self.closed.emit()
@@ -187,7 +198,7 @@ class LaunchProfilePage(QWidget):
super(LaunchProfilePage, self).__init__(parent)
self.app = app
self.widget_mgr: WidgetManager = app.widget_manager
self.profile_manager = app.profile_manager
self.profile_manager: ProfileManager = app.profile_manager
self.game_manager = app.game_manager
self.launch_manager: LaunchManager = self.game_manager.launch
self.process_log_windows: list[ProcessLogWindow] = []
@@ -271,7 +282,15 @@ class LaunchProfilePage(QWidget):
self.app.apply_qss(Path("profile.qss"), self.setStyleSheet)
self.load_profiles()
# Handle launch() result
self.launch_manager.signal.finished.connect(
self.launch_finished,
Qt.ConnectionType.QueuedConnection,
)
self.profile_manager.profiles_changed.connect(
self.load_profiles,
)
class ProfileBackgroundFrame(QFrame):
def __init__(self, image_path, parent=None):
@@ -294,6 +313,51 @@ class LaunchProfilePage(QWidget):
y = (self.height() - scaled.height()) // 2
painter.drawPixmap(x, y, scaled)
@Slot(object, object)
def launch_finished(self, launch_profile: LaunchProfile, result: LaunchResult) -> None:
"""
Handle launch finished signal
:param launch_profile:
:param result:
:return:
"""
if not result.success:
self.status_label.setText("Launch Failed")
self.widget_mgr.signal.show_error_message.emit(
f"Unable to launch profile "
f"{launch_profile.display_name}: "
f"{result.error_message}"
)
return
# Create log view
log_window = ProcessLogWindow(
result.process,
launch_profile.profile_id,
title=(
f"Minecraft Log - {launch_profile.display_name} "
f"({launch_profile.version_id})"
),
parent=None,
finished_callback=self.launch_manager.profile_finished,
)
for warning in result.warnings:
log_window.append_message(
f"[launcher warning] {warning}"
)
self.process_log_windows.append(log_window)
log_window.show()
self.status_label.setText("Launched!")
# Cleanup for status label
timer = QTimer(self)
timer.setSingleShot(True)
timer.timeout.connect(lambda: self.status_label.setText("Select a profile to launch"))
timer.start(3000)
def load_profiles(self):
profiles: list[ProfileSummary] = self.profile_manager.list_profiles()
self.profile_popup.load_profiles(profiles)
@@ -363,54 +427,39 @@ class LaunchProfilePage(QWidget):
self.profile_button.set_popup_open(False)
def launch_profile(self) -> None:
try:
profile_id = self.profile_button.property("profile_id")
self.status_label.setText("Preparing to launch...")
launch_profile = self.app.profile_manager.get_launch_profile(profile_id)
profile_id = self.profile_button.property("profile_id")
if launch_profile is None:
self.status_label.setText("Unable to load the selected profile.")
if profile_id is None:
self.widget_mgr.signal.show_warning_message.emit(
"There is no profile is selected."
" Please create one first."
)
return
self.status_label.setText("Preparing to launch...")
launch_profile = self.app.profile_manager.get_launch_profile(profile_id)
if launch_profile is None:
self.status_label.setText("Unable to load the selected profile.")
return
if self.launch_manager.is_profile_running(launch_profile.profile_id):
request = AskForRequest(
"Launch Request",
"Target profile {} is running. Are you sure you want to launch it again?".format(
launch_profile.display_name
),
)
self.widget_mgr.signal.askyesno.emit(request)
if not request.wait(60):
self.widget_mgr.signal.show_error_message.emit(
"Time out waiting for request for launch."
)
return
if self.launch_manager.is_profile_running(launch_profile.profile_id):
request = AskForRequest(
"Launch Request",
"Target profile {} is running. Are you sure you want to launch it again?".format(
launch_profile.display_name
),
)
self.widget_mgr.signal.askyesno.emit(request)
if not request.result() is True:
return
if not request.wait(60):
self.widget_mgr.signal.show_error_message.emit(
"Time out waiting for request for launch."
)
return
if not request.result() is True:
return
result = self.launch_manager.launch(launch_profile)
if result.success:
self.status_label.setText("Launched!")
log_window = ProcessLogWindow(
result.process,
launch_profile.profile_id,
title=f"Minecraft Log - {launch_profile.display_name} ({launch_profile.version_id})",
parent=None,
finished_callback=self.launch_manager.profile_finished,
)
for warning in result.warnings:
log_window.append_message(f"[launcher warning] {warning}")
self.process_log_windows.append(log_window)
log_window.show()
else:
self.widget_mgr.signal.show_error_message.emit(
"Unable to launch profile {}: {}".format(
launch_profile.display_name,
result.error_message,
)
)
finally:
self.status_label.setText("Select a profile to launch")
self.launch_manager.start_launch_task(launch_profile)
# The log view window creation process has been moved to the method self.launch_finished, move to there!