From d9f5d2ae6ef5fa7686408d9c0471d9e90d0f961f Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 12 Jun 2026 08:26:44 +0000 Subject: [PATCH] =?UTF-8?q?Oprava=20p=C3=A1d=C5=AF:=20chyb=C4=9Bj=C3=ADc?= =?UTF-8?q?=C3=AD=20locale=20p=C5=99i=20startu=20a=20=C5=BEivotn=C3=AD=20c?= =?UTF-8?q?yklus=20=C3=BAlohy=20aktualizace=20hesl=C3=A1=C5=99=C5=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - amcr_viewer.py: QSettings().value('locale/userLocale') může být None na čisté instalaci QGIS – ošetřen fallback na 'en', jinak plugin spadne na TypeError hned při načtení. - amcr_viewer.py: odstraněn nepoužívaný hvězdičkový import resources. - amcr_dialog.py: úloha aktualizace heslářů se drží v modulové referenci, aby GC neuklidil Python wrapper QgsTask před dokončením (známá příčina pádů QGIS). - amcr_dialog.py: tlačítko aktualizace se po spuštění zakáže – nelze už spustit několik stahování paralelně přepisujících heslar.csv. - amcr_dialog.py: závěrečné QMessageBoxy se parentují na hlavní okno QGIS místo dialogu, který může být v době dokončení úlohy už zavřený (smazaný C++ objekt → pád). --- amcr_viewer/amcr_dialog.py | 45 +++++++++++++++++++++++++++++--------- amcr_viewer/amcr_viewer.py | 6 ++--- 2 files changed, 38 insertions(+), 13 deletions(-) diff --git a/amcr_viewer/amcr_dialog.py b/amcr_viewer/amcr_dialog.py index a91cc0a..2c3a154 100644 --- a/amcr_viewer/amcr_dialog.py +++ b/amcr_viewer/amcr_dialog.py @@ -7,6 +7,7 @@ from qgis.PyQt.QtWidgets import (QDialog, QVBoxLayout, from qgis.PyQt.QtCore import Qt, QSettings from qgis.core import (QgsTask, QgsApplication, QgsMessageLog, Qgis, QgsAuthMethodConfig) +from qgis.utils import iface from .amcr_codelists import (OBDOBI, TYP_AKCE, KRAJE, AREAL, ORGANIZACE, OKRESY, KATASTRY, VEDOUCI, PIAN_PRESNOST, TYP_LOKALITY, DRUH_LOKALITY, JISTOTA, @@ -14,6 +15,12 @@ from .amcr_codelists import (OBDOBI, TYP_AKCE, KRAJE, AREAL, ORGANIZACE, download_heslare, refresh_globals) +# Keep Python references to running tasks. QgsTaskManager only holds the +# C++ object; without a Python-side reference the wrapper can be garbage +# collected before the task finishes, which crashes QGIS. +_ACTIVE_TASKS = [] + + class UpdateCodelistsTask(QgsTask): def __init__(self, description): super().__init__(description, QgsTask.CanCancel) @@ -370,21 +377,38 @@ class AmcrFilterDialog(QDialog): return row_widget def action_update_heslare(self): - # Create the task instance + # Create the task instance and keep a reference so the Python + # wrapper survives until the task finishes task = UpdateCodelistsTask("Aktualizace heslářů AMČR") + _ACTIVE_TASKS.append(task) - # Re-enable the button regardless of the outcome - task.taskCompleted.connect(lambda: self.btn_update.setEnabled(True)) - task.taskTerminated.connect(lambda: self.btn_update.setEnabled(True)) + # Prevent parallel downloads overwriting heslar.csv + self.btn_update.setEnabled(False) - task.taskCompleted.connect(lambda: QMessageBox.information( - self, - "Hotovo", - "Hesláře byly úspěšně aktualizovány." - )) + # Message boxes are parented to the main window, not to this dialog – + # the dialog may already be closed (and its C++ object deleted) + # by the time the minute-long task finishes. + parent_win = iface.mainWindow() if iface else None + + def _cleanup(): + if task in _ACTIVE_TASKS: + _ACTIVE_TASKS.remove(task) + try: + self.btn_update.setEnabled(True) + except RuntimeError: + pass # dialog already closed + + def on_completed(): + _cleanup() + QMessageBox.information( + parent_win, + "Hotovo", + "Hesláře byly úspěšně aktualizovány." + ) # Show the exact error if the task fails def on_error(): + _cleanup() if task.exception: # This will show exactly what went wrong (e.g. PermissionError) msg = ( @@ -393,8 +417,9 @@ class AmcrFilterDialog(QDialog): ) else: msg = "Aktualizace byla zrušena uživatelem." - QMessageBox.warning(self, "Chyba / Zrušeno", msg) + QMessageBox.warning(parent_win, "Chyba / Zrušeno", msg) + task.taskCompleted.connect(on_completed) task.taskTerminated.connect(on_error) QgsApplication.taskManager().addTask(task) diff --git a/amcr_viewer/amcr_viewer.py b/amcr_viewer/amcr_viewer.py index ac1e44c..e28de5b 100644 --- a/amcr_viewer/amcr_viewer.py +++ b/amcr_viewer/amcr_viewer.py @@ -6,7 +6,6 @@ from qgis.core import Qgis from .amcr_tools import load_amcr_data, login_to_api from .amcr_dialog import AmcrFilterDialog, LoginDialog -from .resources import * import os.path @@ -24,8 +23,9 @@ class AmcrViewer: self.iface = iface self.plugin_dir = os.path.dirname(__file__) - # Determine the user's locale to load appropriate translation files - locale = QSettings().value('locale/userLocale')[0:2] + # Determine the user's locale to load appropriate translation files. + # The setting may be missing (None) on a fresh QGIS install. + locale = str(QSettings().value('locale/userLocale') or 'en')[0:2] locale_path = os.path.join( self.plugin_dir, 'i18n',