fix(ui): prevent segfault by validating widget existence in async callbacks

Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
This commit is contained in:
2025-11-24 16:26:32 +05:00
parent e9c75b998f
commit 17f0a6b0ea

View File

@@ -2314,7 +2314,8 @@ class MainWindow(QMainWindow):
def openGameDetailPage(self, name, description, cover_path=None, appid="", exec_line="", controller_support="", last_launch="", formatted_playtime="", protondb_tier="", game_source="", anticheat_status=""): def openGameDetailPage(self, name, description, cover_path=None, appid="", exec_line="", controller_support="", last_launch="", formatted_playtime="", protondb_tier="", game_source="", anticheat_status=""):
detailPage = QWidget() detailPage = QWidget()
self._animations = {} if not hasattr(self, '_animations'):
self._animations = {}
imageLabel = QLabel() imageLabel = QLabel()
imageLabel.setFixedSize(300, 450) imageLabel.setFixedSize(300, 450)
self._detail_page_active = True self._detail_page_active = True
@@ -2322,38 +2323,53 @@ class MainWindow(QMainWindow):
# Функция загрузки изображения и обновления стилей # Функция загрузки изображения и обновления стилей
def load_image_and_restore_effect(): def load_image_and_restore_effect():
if not detailPage or detailPage.isHidden(): # Check if detail page still exists and is valid
logger.warning("Detail page is None or hidden, skipping image load") if not detailPage or detailPage.isHidden() or detailPage.parent() is None:
logger.warning("Detail page is None, hidden, or no longer valid, skipping image load")
return return
detailPage.setWindowOpacity(1.0) try:
detailPage.setWindowOpacity(1.0)
except RuntimeError:
logger.warning("Detail page already deleted, skipping opacity set")
return
if cover_path: if cover_path:
def on_pixmap_ready(pixmap): def on_pixmap_ready(pixmap):
if not detailPage or detailPage.isHidden(): # Check if detail page still exists and is valid
logger.warning("Detail page is None or hidden, skipping pixmap update") if not detailPage or detailPage.isHidden() or detailPage.parent() is None:
logger.warning("Detail page is None, hidden, or no longer valid, skipping pixmap update")
return return
rounded = round_corners(pixmap, 10) try:
imageLabel.setPixmap(rounded) rounded = round_corners(pixmap, 10)
logger.debug("Pixmap set for imageLabel") imageLabel.setPixmap(rounded)
logger.debug("Pixmap set for imageLabel")
def on_palette_ready(palette): def on_palette_ready(palette):
if not detailPage or detailPage.isHidden(): # Check if detail page still exists and is valid
logger.warning("Detail page is None or hidden, skipping palette update") if not detailPage or detailPage.isHidden() or detailPage.parent() is None:
return logger.warning("Detail page is None, hidden, or no longer valid, skipping palette update")
dark_palette = [self.darkenColor(color, factor=200) for color in palette] return
stops = ",\n".join( try:
[f"stop:{i/(len(dark_palette)-1):.2f} {dark_palette[i].name()}" for i in range(len(dark_palette))] dark_palette = [self.darkenColor(color, factor=200) for color in palette]
) stops = ",\n".join(
detailPage.setStyleSheet(self.theme.detail_page_style(stops)) [f"stop:{i/(len(dark_palette)-1):.2f} {dark_palette[i].name()}" for i in range(len(dark_palette))]
detailPage.update() )
logger.debug("Stylesheet updated with palette") detailPage.setStyleSheet(self.theme.detail_page_style(stops))
detailPage.update()
self.getColorPalette_async(cover_path, num_colors=5, callback=on_palette_ready) logger.debug("Stylesheet updated with palette")
except RuntimeError:
logger.warning("Detail page already deleted, skipping palette stylesheet update")
self.getColorPalette_async(cover_path, num_colors=5, callback=on_palette_ready)
except RuntimeError:
logger.warning("Detail page already deleted, skipping pixmap update")
load_pixmap_async(cover_path, 300, 450, on_pixmap_ready) load_pixmap_async(cover_path, 300, 450, on_pixmap_ready)
else: else:
detailPage.setStyleSheet(self.theme.DETAIL_PAGE_NO_COVER_STYLE) try:
detailPage.update() detailPage.setStyleSheet(self.theme.DETAIL_PAGE_NO_COVER_STYLE)
detailPage.update()
except RuntimeError:
logger.warning("Detail page already deleted, skipping no-cover stylesheet update")
def cleanup_animation(): def cleanup_animation():
if detailPage in self._animations: if detailPage in self._animations:
@@ -2594,6 +2610,9 @@ class MainWindow(QMainWindow):
return return
if not self._current_detail_page or self._current_detail_page.isHidden() or not self._current_detail_page.parent(): if not self._current_detail_page or self._current_detail_page.isHidden() or not self._current_detail_page.parent():
return return
# Additional check: make sure the detail page in the stacked widget is still our current detail page
if self.stackedWidget.currentWidget() != self._current_detail_page and self._current_detail_page not in [self.stackedWidget.widget(i) for i in range(self.stackedWidget.count())]:
return
if results: if results:
game = results[0] # Берем первый результат game = results[0] # Берем первый результат
@@ -2617,33 +2636,42 @@ class MainWindow(QMainWindow):
has_data = False has_data = False
if main_story_time is not None: if main_story_time is not None:
mainStoryTitle = QLabel(_("MAIN STORY")) try:
mainStoryTitle.setStyleSheet(self.theme.LAST_LAUNCH_TITLE_STYLE) mainStoryTitle = QLabel(_("MAIN STORY"))
mainStoryValue = QLabel(main_story_time) mainStoryTitle.setStyleSheet(self.theme.LAST_LAUNCH_TITLE_STYLE)
mainStoryValue.setStyleSheet(self.theme.LAST_LAUNCH_VALUE_STYLE) mainStoryValue = QLabel(main_story_time)
hltbLayout.addWidget(mainStoryTitle) mainStoryValue.setStyleSheet(self.theme.LAST_LAUNCH_VALUE_STYLE)
hltbLayout.addWidget(mainStoryValue) hltbLayout.addWidget(mainStoryTitle)
hltbLayout.addSpacing(30) hltbLayout.addWidget(mainStoryValue)
has_data = True hltbLayout.addSpacing(30)
has_data = True
except RuntimeError:
logger.warning("Detail page already deleted, skipping main story time update")
if main_extra_time is not None: if main_extra_time is not None:
mainExtraTitle = QLabel(_("MAIN + SIDES")) try:
mainExtraTitle.setStyleSheet(self.theme.PLAY_TIME_TITLE_STYLE) mainExtraTitle = QLabel(_("MAIN + SIDES"))
mainExtraValue = QLabel(main_extra_time) mainExtraTitle.setStyleSheet(self.theme.PLAY_TIME_TITLE_STYLE)
mainExtraValue.setStyleSheet(self.theme.PLAY_TIME_VALUE_STYLE) mainExtraValue = QLabel(main_extra_time)
hltbLayout.addWidget(mainExtraTitle) mainExtraValue.setStyleSheet(self.theme.PLAY_TIME_VALUE_STYLE)
hltbLayout.addWidget(mainExtraValue) hltbLayout.addWidget(mainExtraTitle)
hltbLayout.addSpacing(30) hltbLayout.addWidget(mainExtraValue)
has_data = True hltbLayout.addSpacing(30)
has_data = True
except RuntimeError:
logger.warning("Detail page already deleted, skipping main extra time update")
if completionist_time is not None: if completionist_time is not None:
completionistTitle = QLabel(_("COMPLETIONIST")) try:
completionistTitle.setStyleSheet(self.theme.LAST_LAUNCH_TITLE_STYLE) completionistTitle = QLabel(_("COMPLETIONIST"))
completionistValue = QLabel(completionist_time) completionistTitle.setStyleSheet(self.theme.LAST_LAUNCH_TITLE_STYLE)
completionistValue.setStyleSheet(self.theme.LAST_LAUNCH_VALUE_STYLE) completionistValue = QLabel(completionist_time)
hltbLayout.addWidget(completionistTitle) completionistValue.setStyleSheet(self.theme.LAST_LAUNCH_VALUE_STYLE)
hltbLayout.addWidget(completionistValue) hltbLayout.addWidget(completionistTitle)
has_data = True hltbLayout.addWidget(completionistValue)
has_data = True
except RuntimeError:
logger.warning("Detail page already deleted, skipping completionist time update")
# Если есть данные, добавляем layout во вторую строку # Если есть данные, добавляем layout во вторую строку
if has_data: if has_data: