Merge pull request #61 from ARUP-CAS/fix/qt6-kompatibilita

fix: kompatibilita s Qt6 / QGIS 4 a úklid lintů
This commit is contained in:
david-spacil authored and GitHub committed 2026-09-01 23:22:56 +02:00
commit 39dac12bad
7 files changed
+197 -86

No files matched your search

+52
View File
@@ -83,6 +83,58 @@ Další pravidla:
definice pole (`QgsField`), naplnění hodnoty z dokumentu, překlad hlavičky definice pole (`QgsField`), naplnění hodnoty z dokumentu, překlad hlavičky
sloupce a export atributů. sloupce a export atributů.
### Kompatibilita s Qt6 / QGIS 4
Plugin cílí na QGIS 3.44 i na QGIS 4 (`qgisMaximumVersion=4.99.0`), tedy na
Qt5 i Qt6 zároveň. **Tohle se drží rigorózně** – ne až před releasem, ale při
každé změně kódu. Chování obou větví se liší tiše: pod Qt5 projde i to, co
QGIS 4 odmítne, takže lokální „funguje mi to“ nic nedokazuje.
Závazná pravidla:
- **Nikdy neimportuj přímo z `PyQt5` ani z `PyQt6`.** Vždy přes shim
`qgis.PyQt.*`. Ten mimo jiné pod Qt6 přetahuje `QAction`, `QActionGroup`
a `QShortcut` z `QtGui`, takže import z `qgis.PyQt.QtWidgets` je správně.
- **Enumy vždy plně kvalifikované (scoped).** `Qgis.MessageLevel.Info`, ne
`Qgis.Info`; `QgsTask.Flag.CanCancel`, ne `QgsTask.CanCancel`;
`QgsWkbTypes.GeometryType.PointGeometry`, ne `QgsWkbTypes.PointGeometry`.
Totéž pro Qt: `Qt.CheckState.Checked`, `QDialogButtonBox.StandardButton.Ok`.
Zkrácené tvary sice v QGIS 4.2 zatím fungují, ale oficiální kontrola je
hlásí a do budoucna mizí.
- **Zdrojové `.py` soubory ukládej bez BOM.** Kontrolní skript čte soubor
jako UTF-8 bez `utf-8-sig` a na BOM spadne s
`SyntaxError: invalid non-printable character U+FEFF`, takže se takový
soubor **vůbec nezkontroluje**. (`codelists/heslar.csv` BOM mít smí, tam je
kvůli Excelu.)
- Nepoužívej API zrušená v Qt6: `exec_()`, `QRegExp`, `QDesktopWidget`,
`QApplication.desktop()`, `Qt.MidButton`, `QFontMetrics.width()`,
`setResizeMode`, atributy `AA_EnableHighDpiScaling` / `AA_UseHighDpiPixmaps`.
- `supportsQt6=True` v `metadata.txt` **nepatří** – bylo zrušeno; o zařazení
mezi „QGIS 4 Ready“ rozhoduje rozsah `qgisMinimumVersion` až
`qgisMaximumVersion`.
Ověření před PR, který mění Python kód:
```sh
# oficiální kontrola, kterou pouští i plugins.qgis.org (pyqgis4-checker)
docker run --rm --pull always --user $(id -u):$(id -g) \
--workdir /workspace/ -v "$(pwd):/workspace/" \
ghcr.io/qgis/pyqgis4-checker:main-ubuntu \
pyqt5_to_pyqt6.py --dry_run --logfile /workspace/pyqt6_checker.log .
```
Prázdný log = čisté. Kontrola je na plugins.qgis.org informativní
(neblokuje schválení), ale nález znamená, že plugin v QGIS 4 dříve nebo
později přestane fungovat.
Když je po ruce QGIS 4 (např. flatpak `org.qgis.qgis`), ověř navíc, že se
plugin pod Qt6 opravdu načte:
```sh
flatpak run --command=sh org.qgis.qgis -c \
'PYTHONPATH=/app/share/qgis/python python3 -c "import qgis.core"'
```
## Verzování a release ## Verzování a release
- Verze pluginu žije v **`amcr_viewer/metadata.txt`** (`version=`). - Verze pluginu žije v **`amcr_viewer/metadata.txt`** (`version=`).
-1
View File
@@ -31,6 +31,5 @@ def classFactory(iface): # pylint: disable=invalid-name
:param iface: A QGIS interface instance. :param iface: A QGIS interface instance.
:type iface: QgsInterface :type iface: QgsInterface
""" """
#
from .amcr_viewer import AmcrViewer from .amcr_viewer import AmcrViewer
return AmcrViewer(iface) return AmcrViewer(iface)
+16 -13
View File
@@ -1,10 +1,11 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import os
import csv import csv
import requests import os
import xml.etree.ElementTree as ET # nosec
import time import time
from qgis.core import QgsMessageLog, Qgis import xml.etree.ElementTree as ET # nosec
import requests
from qgis.core import Qgis, QgsMessageLog
# Define paths for the plugin and its codelists directory # Define paths for the plugin and its codelists directory
PLUGIN_DIR = os.path.dirname(__file__) PLUGIN_DIR = os.path.dirname(__file__)
@@ -90,7 +91,7 @@ def parse_codelist_file(filename, target_dict=None):
except Exception as e: except Exception as e:
QgsMessageLog.logMessage( QgsMessageLog.logMessage(
f"AMČR Codelist Read Error for {filename}: {e}", f"AMČR Codelist Read Error for {filename}: {e}",
"AMČR", Qgis.Critical) "AMČR", Qgis.MessageLevel.Critical)
return target_dict return target_dict
@@ -98,7 +99,7 @@ def parse_codelist_file(filename, target_dict=None):
def load_all_data(): def load_all_data():
"""Loads the codelist during plugin startup.""" """Loads the codelist during plugin startup."""
ensure_codelists_dir() ensure_codelists_dir()
categorized_data = {k: {} for k in slovnicek.keys()} categorized_data = {k: {} for k in slovnicek}
parse_codelist_file('heslar.csv', categorized_data) parse_codelist_file('heslar.csv', categorized_data)
return categorized_data return categorized_data
@@ -124,7 +125,9 @@ def fetch_set(base_url, internal_name, api_set, task=None):
try: try:
if "digiarchiv" not in base_url: if "digiarchiv" not in base_url:
response = requests.get(base_url, params=params_amcr, timeout=30) response = requests.get(
base_url, params=params_amcr, timeout=30
)
response.raise_for_status() response.raise_for_status()
root = ET.fromstring(response.content) # nosec root = ET.fromstring(response.content) # nosec
@@ -193,7 +196,7 @@ def fetch_set(base_url, internal_name, api_set, task=None):
time.sleep(0.5) time.sleep(0.5)
else: else:
break break
else: else:
response = requests.get(base_url, params=params_da, timeout=30) response = requests.get(base_url, params=params_da, timeout=30)
response.raise_for_status() response.raise_for_status()
@@ -210,13 +213,13 @@ def fetch_set(base_url, internal_name, api_set, task=None):
'Kód': nazev, 'Kód': nazev,
'Kategorie': internal_name 'Kategorie': internal_name
}) })
break break
except Exception as e: except Exception as e:
QgsMessageLog.logMessage( QgsMessageLog.logMessage(
f"Chyba u setu {api_set}: {e}", f"Chyba u setu {api_set}: {e}",
"AMČR", Qgis.Warning) "AMČR", Qgis.MessageLevel.Warning)
break break
return dataset return dataset
@@ -240,7 +243,7 @@ def download_heslare(task=None):
QgsMessageLog.logMessage( QgsMessageLog.logMessage(
f"Zpracovávám kategorii: {interni}...", f"Zpracovávám kategorii: {interni}...",
"AMČR", Qgis.Info) "AMČR", Qgis.MessageLevel.Info)
# Pass the task correctly to the updated fetch function # Pass the task correctly to the updated fetch function
data = fetch_set(base_url, interni, api_nazev, task=task) data = fetch_set(base_url, interni, api_nazev, task=task)
+71 -29
View File
@@ -1,23 +1,57 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from qgis.PyQt.QtWidgets import (QDialog, QVBoxLayout, from qgis.core import (
QLineEdit, QDialogButtonBox, Qgis,
QCheckBox, QGroupBox, QPushButton, QgsApplication,
QListWidget, QListWidgetItem, QHBoxLayout, QgsAuthMethodConfig,
QMessageBox, QLabel, QFormLayout, QgsMessageLog,
QGridLayout, QScrollArea, QFrame, QWidget) QgsTask,
from qgis.PyQt.QtCore import Qt, QSettings )
from qgis.core import (QgsTask, QgsApplication,
QgsMessageLog, Qgis, QgsAuthMethodConfig)
from qgis.gui import QgsDateEdit from qgis.gui import QgsDateEdit
from qgis.PyQt.QtCore import QSettings, Qt
from qgis.PyQt.QtWidgets import (
QCheckBox,
QDialog,
QDialogButtonBox,
QFormLayout,
QFrame,
QGridLayout,
QGroupBox,
QHBoxLayout,
QLabel,
QLineEdit,
QListWidget,
QListWidgetItem,
QMessageBox,
QPushButton,
QScrollArea,
QVBoxLayout,
QWidget,
)
from qgis.utils import iface 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,
LOKALITA_ZACHOVALOST, PRISTUPNOST,
NALEZ_KATEGORIE, DRUH_NALEZU, SPECIFIKACE,
NALEZOVE_OKOLNOSTI, NALEZCE,
download_heslare, refresh_globals)
from .amcr_codelists import (
AREAL,
DRUH_LOKALITY,
DRUH_NALEZU,
JISTOTA,
KATASTRY,
KRAJE,
LOKALITA_ZACHOVALOST,
NALEZ_KATEGORIE,
NALEZCE,
NALEZOVE_OKOLNOSTI,
OBDOBI,
OKRESY,
ORGANIZACE,
PIAN_PRESNOST,
PRISTUPNOST,
SPECIFIKACE,
TYP_AKCE,
TYP_LOKALITY,
VEDOUCI,
download_heslare,
refresh_globals,
)
# The date filter of the API requires both bounds; a one-sided range makes # The date filter of the API requires both bounds; a one-sided range makes
# the server fail with an ArrayIndexOutOfBoundsException and '*' is not # the server fail with an ArrayIndexOutOfBoundsException and '*' is not
@@ -38,7 +72,7 @@ _ACTIVE_TASKS = []
class UpdateCodelistsTask(QgsTask): class UpdateCodelistsTask(QgsTask):
def __init__(self, description): def __init__(self, description):
super().__init__(description, QgsTask.CanCancel) super().__init__(description, QgsTask.Flag.CanCancel)
self.success = False self.success = False
self.exception = None self.exception = None
@@ -59,18 +93,18 @@ class UpdateCodelistsTask(QgsTask):
refresh_globals() refresh_globals()
QgsMessageLog.logMessage( QgsMessageLog.logMessage(
"Hesláře AMČR byly úspěšně aktualizovány.", "Hesláře AMČR byly úspěšně aktualizovány.",
"AMČR", Qgis.Info "AMČR", Qgis.MessageLevel.Info
) )
else: else:
if self.isCanceled(): if self.isCanceled():
QgsMessageLog.logMessage( QgsMessageLog.logMessage(
"Aktualizace heslářů byla zrušena.", "Aktualizace heslářů byla zrušena.",
"AMČR", Qgis.Warning "AMČR", Qgis.MessageLevel.Warning
) )
else: else:
QgsMessageLog.logMessage( QgsMessageLog.logMessage(
f"Chyba aktualizace: {self.exception}", f"Chyba aktualizace: {self.exception}",
"AMČR", Qgis.Critical "AMČR", Qgis.MessageLevel.Critical
) )
@@ -608,7 +642,7 @@ class AmcrFilterDialog(QDialog):
# This will show exactly what went wrong (e.g. PermissionError) # This will show exactly what went wrong (e.g. PermissionError)
msg = ( msg = (
"Aktualizace selhala z důvodu chyby:\n" "Aktualizace selhala z důvodu chyby:\n"
f"{str(task.exception)}" f"{task.exception!s}"
) )
else: else:
msg = "Aktualizace byla zrušena uživatelem." msg = "Aktualizace byla zrušena uživatelem."
@@ -660,7 +694,7 @@ class AmcrFilterDialog(QDialog):
filters['f_vedouci'] = self.selection_cache['vedouci'] filters['f_vedouci'] = self.selection_cache['vedouci']
if self.selection_cache['organizace']: if self.selection_cache['organizace']:
filters['f_organizace'] = self.selection_cache['organizace'] filters['f_organizace'] = self.selection_cache['organizace']
if self.selection_cache['typ_lokality']: if self.selection_cache['typ_lokality']:
filters['f_typ_lokality'] = self.selection_cache['typ_lokality'] filters['f_typ_lokality'] = self.selection_cache['typ_lokality']
@@ -669,7 +703,9 @@ class AmcrFilterDialog(QDialog):
if self.selection_cache['jistota']: if self.selection_cache['jistota']:
filters['f_jistota'] = self.selection_cache['jistota'] filters['f_jistota'] = self.selection_cache['jistota']
if self.selection_cache['lokalita_zachovalost']: if self.selection_cache['lokalita_zachovalost']:
filters['f_lokalita_zachovalost'] = self.selection_cache['lokalita_zachovalost'] filters['f_lokalita_zachovalost'] = (
self.selection_cache['lokalita_zachovalost']
)
# Samostatné nálezy # Samostatné nálezy
if self.selection_cache['nalez_kategorie']: if self.selection_cache['nalez_kategorie']:
@@ -679,7 +715,9 @@ class AmcrFilterDialog(QDialog):
if self.selection_cache['specifikace']: if self.selection_cache['specifikace']:
filters['f_specifikace'] = self.selection_cache['specifikace'] filters['f_specifikace'] = self.selection_cache['specifikace']
if self.selection_cache['nalezove_okolnosti']: if self.selection_cache['nalezove_okolnosti']:
filters['f_nalezove_okolnosti'] = self.selection_cache['nalezove_okolnosti'] filters['f_nalezove_okolnosti'] = (
self.selection_cache['nalezove_okolnosti']
)
if self.selection_cache['nalezce']: if self.selection_cache['nalezce']:
filters['f_nalezce'] = self.selection_cache['nalezce'] filters['f_nalezce'] = self.selection_cache['nalezce']
@@ -718,7 +756,8 @@ class LoginDialog(QDialog):
- storeAuthenticationConfig() and loadAuthenticationConfig() both have - storeAuthenticationConfig() and loadAuthenticationConfig() both have
SIP_INOUT on their config parameter, so Python bindings return a tuple SIP_INOUT on their config parameter, so Python bindings return a tuple
(bool, QgsAuthMethodConfig) rather than just bool. Always unpack both. (bool, QgsAuthMethodConfig) rather than just bool. Always unpack both.
- loadAuthenticationConfig() with full=False loads only metadata (name, method, - loadAuthenticationConfig() with full=False loads only metadata
(name, method,
id) but NOT the config() values like username/password. Use full=True to id) but NOT the config() values like username/password. Use full=True to
access those. access those.
""" """
@@ -949,7 +988,7 @@ class LoginDialog(QDialog):
# We skip hasConfigId() as it may return False # We skip hasConfigId() as it may return False
# despite the config existing # despite the config existing
# (in-memory cache may not be populated yet in QGIS 4). # (in-memory cache may not be populated yet in QGIS 4).
ok_load, existing_cfg = ( ok_load, _ = (
self._load_config(existing_id, full=False) self._load_config(existing_id, full=False)
if existing_id if existing_id
else (False, None) else (False, None)
@@ -978,7 +1017,9 @@ class LoginDialog(QDialog):
settings = QSettings() settings = QSettings()
existing_id = settings.value(self.SETTINGS_KEY, "") existing_id = settings.value(self.SETTINGS_KEY, "")
if existing_id: if existing_id:
QgsApplication.authManager().removeAuthenticationConfig(existing_id) QgsApplication.authManager().removeAuthenticationConfig(
existing_id
)
settings.remove(self.SETTINGS_KEY) settings.remove(self.SETTINGS_KEY)
QMessageBox.information( QMessageBox.information(
self, self,
@@ -1013,4 +1054,5 @@ class LoginDialog(QDialog):
if not ok: if not ok:
return "", "" return "", ""
return cfg.config("username", ""), cfg.config("password", "") # nosec B106 return (cfg.config("username", ""),
cfg.config("password", "")) # nosec B106
+37 -27
View File
@@ -1,15 +1,24 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from qgis.core import (QgsProject, QgsVectorLayer, QgsFeature, QgsGeometry,
QgsField, QgsCoordinateReferenceSystem,
QgsCoordinateTransform, QgsWkbTypes, Qgis,
QgsMessageLog)
from qgis.utils import iface
from qgis.PyQt.QtCore import Qt, QMetaType
from qgis.PyQt.QtWidgets import QApplication
from qgis.PyQt.QtGui import QCursor
import requests
import json import json
import requests
from qgis.core import (
Qgis,
QgsCoordinateReferenceSystem,
QgsCoordinateTransform,
QgsFeature,
QgsField,
QgsGeometry,
QgsMessageLog,
QgsProject,
QgsVectorLayer,
QgsWkbTypes,
)
from qgis.PyQt.QtCore import QMetaType, Qt
from qgis.PyQt.QtGui import QCursor
from qgis.PyQt.QtWidgets import QApplication
from qgis.utils import iface
# Global cache to store translated terms from the Digital Archive # Global cache to store translated terms from the Digital Archive
TRANSLATIONS = {} TRANSLATIONS = {}
@@ -109,8 +118,9 @@ def login_to_api(username: str, password: str):
except requests.exceptions.HTTPError as e: except requests.exceptions.HTTPError as e:
status = e.response.status_code if e.response is not None else None status = e.response.status_code if e.response is not None else None
_log(f"CHYBA HTTP {status if status else '?'}: " telo = (e.response.text[:300] if e.response is not None
f"{e.response.text[:300] if e.response is not None else 'žádná odpověď'}", else "žádná odpověď")
_log(f"CHYBA HTTP {status if status else '?'}: {telo}",
Qgis.MessageLevel.Critical) Qgis.MessageLevel.Critical)
LAST_LOGIN_ERROR = 'auth' if status in (401, 403) else 'network' LAST_LOGIN_ERROR = 'auth' if status in (401, 403) else 'network'
return None return None
@@ -623,7 +633,8 @@ def load_amcr_data(canvas, bb, filters=None,
for dj in djs: for dj in djs:
# Skip negative evidence units if requested # Skip negative evidence units if requested
if skip_negativni and dj.get('dj_negativni_jednotka') is True: if (skip_negativni
and dj.get('dj_negativni_jednotka') is True):
continue continue
komps = dj.get('dj_komponenta', []) komps = dj.get('dj_komponenta', [])
@@ -643,7 +654,8 @@ def load_amcr_data(canvas, bb, filters=None,
dj_id = dj.get('ident_cely') dj_id = dj.get('ident_cely')
dj_typ = dj.get('dj_typ') dj_typ = dj.get('dj_typ')
# Merge shared metadata with documentation unit-specific fields # Merge shared metadata with documentation
# unit-specific fields
dj_meta = { dj_meta = {
**meta, **meta,
'dj_id': dj_id, 'dj_id': dj_id,
@@ -690,7 +702,8 @@ def load_amcr_data(canvas, bb, filters=None,
or {} or {}
).get('value', ""), ).get('value', ""),
} }
pian_lookup[dj_pian_value].append(komp_meta) pian_lookup[dj_pian_value].append(
komp_meta)
target_pian_ids_count += 1 target_pian_ids_count += 1
else: else:
# DJ without components — still include # DJ without components — still include
@@ -704,7 +717,8 @@ def load_amcr_data(canvas, bb, filters=None,
'komponenta_areal': "", 'komponenta_areal': "",
'komponenta_obdobi': "", 'komponenta_obdobi': "",
} }
pian_lookup[dj_pian_value].append(empty_meta) pian_lookup[dj_pian_value].append(
empty_meta)
target_pian_ids_count += 1 target_pian_ids_count += 1
else: else:
target_pian_ids_count += 1 target_pian_ids_count += 1
@@ -907,9 +921,6 @@ def load_amcr_data(canvas, bb, filters=None,
) )
return return
# ========================================== # ==========================================
# D) LAYER CREATION (QGIS Memory Layers) # D) LAYER CREATION (QGIS Memory Layers)
# ========================================== # ==========================================
@@ -947,7 +958,6 @@ def load_amcr_data(canvas, bb, filters=None,
QgsField("typ_dj", QMetaType.Type.QString), QgsField("typ_dj", QMetaType.Type.QString),
] ]
cols += [ cols += [
QgsField(typ_dat, QMetaType.Type.QString), QgsField(typ_dat, QMetaType.Type.QString),
QgsField("definicni_body", QMetaType.Type.QString), QgsField("definicni_body", QMetaType.Type.QString),
@@ -1138,11 +1148,11 @@ def load_amcr_data(canvas, bb, filters=None,
if geom.isGeosValid(): if geom.isGeosValid():
t = geom.type() t = geom.type()
target_list = None target_list = None
if t == QgsWkbTypes.PolygonGeometry: if t == QgsWkbTypes.GeometryType.PolygonGeometry:
target_list = feats_p target_list = feats_p
elif t == QgsWkbTypes.LineGeometry: elif t == QgsWkbTypes.GeometryType.LineGeometry:
target_list = feats_l target_list = feats_l
elif t == QgsWkbTypes.PointGeometry: elif t == QgsWkbTypes.GeometryType.PointGeometry:
target_list = feats_pt target_list = feats_pt
if target_list is None: if target_list is None:
@@ -1227,11 +1237,11 @@ def load_amcr_data(canvas, bb, filters=None,
if geom.isGeosValid(): if geom.isGeosValid():
t = geom.type() t = geom.type()
target_list = None target_list = None
if t == QgsWkbTypes.PolygonGeometry: if t == QgsWkbTypes.GeometryType.PolygonGeometry:
target_list = feats_p target_list = feats_p
elif t == QgsWkbTypes.LineGeometry: elif t == QgsWkbTypes.GeometryType.LineGeometry:
target_list = feats_l target_list = feats_l
elif t == QgsWkbTypes.PointGeometry: elif t == QgsWkbTypes.GeometryType.PointGeometry:
target_list = feats_pt target_list = feats_pt
if target_list is None: if target_list is None:
@@ -1265,7 +1275,7 @@ def load_amcr_data(canvas, bb, filters=None,
] ]
feat.setAttributes(atributy) feat.setAttributes(atributy)
target_list.append(feat) target_list.append(feat)
except Exception as ex: except Exception as ex:
QgsMessageLog.logMessage( QgsMessageLog.logMessage(
f"Chyba při tvorbě feature: {ex}", f"Chyba při tvorbě feature: {ex}",
+17 -15
View File
@@ -1,13 +1,14 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from qgis.PyQt.QtCore import QSettings, QTranslator, QCoreApplication, QUrl
from qgis.PyQt.QtGui import QIcon, QDesktopServices
from qgis.PyQt.QtWidgets import QMenu, QAction, QToolButton, QDialog
from qgis.core import Qgis
from .amcr_tools import load_amcr_data, login_to_api
from .amcr_dialog import AmcrFilterDialog, LoginDialog
import os.path import os.path
from qgis.core import Qgis
from qgis.PyQt.QtCore import QCoreApplication, QSettings, QTranslator, QUrl
from qgis.PyQt.QtGui import QDesktopServices, QIcon
from qgis.PyQt.QtWidgets import QAction, QDialog, QMenu, QToolButton
from .amcr_dialog import AmcrFilterDialog, LoginDialog
from .amcr_tools import load_amcr_data, login_to_api
class AmcrViewer: class AmcrViewer:
""" """
@@ -29,7 +30,7 @@ class AmcrViewer:
locale_path = os.path.join( locale_path = os.path.join(
self.plugin_dir, self.plugin_dir,
'i18n', 'i18n',
'AmcrViewer_{}.qm'.format(locale) f'AmcrViewer_{locale}.qm'
) )
# Install the translator if a translation file # Install the translator if a translation file
@@ -41,7 +42,7 @@ class AmcrViewer:
# Initialize internal state # Initialize internal state
self.actions = [] self.actions = []
self.menu = self.tr(u'&AMČR Viewer') self.menu = self.tr('&AMČR Viewer')
self.first_start = None self.first_start = None
def tr(self, message): def tr(self, message):
@@ -102,7 +103,7 @@ class AmcrViewer:
# custom dropdown menu # custom dropdown menu
self.action_download_akce = self.add_action( self.action_download_akce = self.add_action(
icon_path=icon_akce_path, icon_path=icon_akce_path,
text=self.tr(u'Stáhnout data akcí | AMČR Viewer'), text=self.tr('Stáhnout data akcí | AMČR Viewer'),
callback=lambda checked=False: self.run_download('akce'), callback=lambda checked=False: self.run_download('akce'),
parent=self.iface.mainWindow(), parent=self.iface.mainWindow(),
add_to_menu=False, add_to_menu=False,
@@ -112,8 +113,9 @@ class AmcrViewer:
self.action_download_pas = self.add_action( self.action_download_pas = self.add_action(
icon_path=icon_pas_path, icon_path=icon_pas_path,
text=self.tr(u'Stáhnout data samostatných nálezů | AMČR Viewer'), text=self.tr('Stáhnout data samostatných nálezů | AMČR Viewer'),
callback=lambda checked=False: self.run_download('samostatny_nalez'), callback=lambda checked=False: self.run_download(
'samostatny_nalez'),
parent=self.iface.mainWindow(), parent=self.iface.mainWindow(),
add_to_menu=False, add_to_menu=False,
add_to_toolbar=False add_to_toolbar=False
@@ -122,7 +124,7 @@ class AmcrViewer:
self.action_download_lokality = self.add_action( self.action_download_lokality = self.add_action(
icon_path=icon_lokality_path, icon_path=icon_lokality_path,
text=self.tr(u'Stáhnout data lokalit | AMČR Viewer'), text=self.tr('Stáhnout data lokalit | AMČR Viewer'),
callback=lambda checked=False: self.run_download('lokalita'), callback=lambda checked=False: self.run_download('lokalita'),
parent=self.iface.mainWindow(), parent=self.iface.mainWindow(),
add_to_menu=False, add_to_menu=False,
@@ -132,7 +134,7 @@ class AmcrViewer:
self.action_login_dialog = self.add_action( self.action_login_dialog = self.add_action(
icon_path=icon_pas_path, icon_path=icon_pas_path,
text=self.tr(u'Přihlásit se | AMČR Viewer'), text=self.tr('Přihlásit se | AMČR Viewer'),
callback=lambda checked=False: self.login(), callback=lambda checked=False: self.login(),
parent=self.iface.mainWindow(), parent=self.iface.mainWindow(),
add_to_menu=False, add_to_menu=False,
@@ -142,7 +144,7 @@ class AmcrViewer:
self.action_amcr_help = self.add_action( self.action_amcr_help = self.add_action(
icon_path=icon_amcr_help_path, icon_path=icon_amcr_help_path,
text=self.tr(u'Nápověda AMČR Help | AMČR Viewer'), text=self.tr('Nápověda AMČR Help | AMČR Viewer'),
callback=lambda checked=False: self.open_help(), callback=lambda checked=False: self.open_help(),
parent=self.iface.mainWindow(), parent=self.iface.mainWindow(),
add_to_menu=False, add_to_menu=False,
+4 -1
View File
@@ -8,7 +8,7 @@ name=AMČR Viewer
qgisMinimumVersion=3.44.0 qgisMinimumVersion=3.44.0
qgisMaximumVersion=4.99.0 qgisMaximumVersion=4.99.0
description=Viewing and downloading the AMČR data. description=Viewing and downloading the AMČR data.
version=2.1.1 version=2.1.2
author=David Spáčil author=David Spáčil
email=spacil@arub.cz email=spacil@arub.cz
@@ -24,6 +24,9 @@ hasProcessingProvider=no
# Uncomment the following line and add your changelog: # Uncomment the following line and add your changelog:
changelog= changelog=
Plný seznam změn v češtině je dostupný zde: https://github.com/ARUP-CAS/aiscr-qgis-amcr-viewer/releases/tag/v2.1.1 Plný seznam změn v češtině je dostupný zde: https://github.com/ARUP-CAS/aiscr-qgis-amcr-viewer/releases/tag/v2.1.1
v2.1.2 (2026-09-01)
* Qt6 compatibility
* Code clean-up
v2.1.1 (2026-09-01) v2.1.1 (2026-09-01)
* Added download of Individual finds (PAS), including a dedicated menu entry * Added download of Individual finds (PAS), including a dedicated menu entry
* Added filtering by date * Added filtering by date