Skip to content

Commit 59c69bc

Browse files
committed
fix(tools_gw): open help links using Qt to prevent crashes on Python 3.12+ and skip DB lookup when no schema is loaded
1 parent c35ea7d commit 59c69bc

2 files changed

Lines changed: 38 additions & 17 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3434

3535
### Fixed
3636

37+
- Open Help from Create project (and other dialogs) via Qt instead of `webbrowser`/`subprocess`, which crashed QGIS on Python 3.12+ (ResourceWarning from `Popen.__del__`). Skip `config_param_system` lookup when no schema is loaded.
3738
- Open catalog from Replace feature without crashing when combo fields have no `comboIds` (async `queryText` path).
3839
- Show human-readable tooltips for Network Utilities and File Transfer toolbar buttons.
3940
- Clear `selector_inp_dscenario` when creating a sample (same as `selector_psector`); fix `gw_fct_setinitproject` to delete from `selector_inp_dscenario`.

core/utils/tools_gw.py

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
import shutil
1616
import sys
1717
import sqlite3
18-
import webbrowser
1918
import xml.etree.ElementTree as ET
2019

2120
from typing import Literal, Dict, Optional, Union, Any, List, Tuple
@@ -27,9 +26,9 @@
2726
from datetime import datetime
2827

2928
from qgis.PyQt.QtCore import Qt, QStringListModel, QVariant, QDate, QRegularExpression, \
30-
QItemSelectionModel, QTimer, QSettings
29+
QItemSelectionModel, QTimer, QSettings, QUrl
3130
from qgis.PyQt.QtGui import QCursor, QPixmap, QColor, QStandardItemModel, QIcon, QStandardItem, \
32-
QIntValidator, QDoubleValidator, QRegularExpressionValidator, QPalette, QFont
31+
QIntValidator, QDoubleValidator, QRegularExpressionValidator, QPalette, QFont, QDesktopServices
3332
from qgis.PyQt.QtSql import QSqlTableModel
3433
from qgis.PyQt.QtWidgets import QSpacerItem, QSizePolicy, QLineEdit, QLabel, QComboBox, QGridLayout, QTabWidget, \
3534
QCompleter, QPushButton, QTableView, QFrame, QCheckBox, QDoubleSpinBox, QSpinBox, QDateEdit, QTextEdit, \
@@ -606,11 +605,30 @@ def add_btn_help(dlg):
606605
btn_help.clicked.connect(partial(open_help_link, context, uiname, dlg))
607606

608607

608+
def _open_external_url(url):
609+
"""Open a URL via Qt instead of Python webbrowser/subprocess.
610+
611+
webbrowser.open() leaves a live Popen; on Python 3.12+ its __del__ emits
612+
ResourceWarning, and QGIS logging that from a destructor can abort the process.
613+
"""
614+
if not url:
615+
return False
616+
try:
617+
return QDesktopServices.openUrl(QUrl(str(url)))
618+
except Exception as e:
619+
msg = "Could not open URL: {0}"
620+
msg_params = (e,)
621+
tools_log.log_warning(msg, msg_params=msg_params)
622+
return False
623+
624+
609625
def open_help_link(context, uiname, dlg=None):
610626
""" Opens the help link for the given dialog, or a default link if not found. """
611627

612-
# Base URL for the documentation
613-
domain = get_config_value('help_domain', table='config_param_system')
628+
# Skip the DB lookup when no schema is loaded (admin / create project).
629+
domain = None
630+
if lib_vars.schema_name:
631+
domain = get_config_value('help_domain', table='config_param_system')
614632
if domain is None:
615633
domain = "https://docs.giswater.org"
616634
else:
@@ -621,21 +639,24 @@ def open_help_link(context, uiname, dlg=None):
621639
# Always use 'latest' to avoid errors when plugin version is diferent than docs
622640
base_url = f"{domain}/latest/{language}/docs/giswater/for-users"
623641

624-
uiname = uiname.replace("_", "-").replace(" ", "-").lower() + ".html" # sanitize uiname
642+
if uiname:
643+
uiname = uiname.replace("_", "-").replace(" ", "-").lower() + ".html"
625644

626645
tabname = 'tab_none'
627-
tab_widgets = dlg.findChildren(QTabWidget)
628-
if tab_widgets:
629-
tab_widget = tab_widgets[0]
630-
index_tab = tab_widget.currentIndex()
631-
tabname = tab_widget.widget(index_tab).objectName()
646+
if dlg is not None:
647+
tab_widgets = dlg.findChildren(QTabWidget)
648+
if tab_widgets:
649+
tab_widget = tab_widgets[0]
650+
index_tab = tab_widget.currentIndex()
651+
tab = tab_widget.widget(index_tab)
652+
if tab is not None and tab.objectName():
653+
tabname = tab.objectName()
632654

633655
# Construct the path dynamically
634656
if uiname:
635657
if uiname == 'info-feature.html':
636658
feature = dlg.windowTitle().split(' ')[0]
637659
sql = f"SELECT feature_type FROM {lib_vars.schema_name}.cat_feature WHERE id = '{feature}'"
638-
print(sql)
639660
feature_type = tools_db.get_rows(sql)[0]['feature_type']
640661
if tabname.lower() == 'tab_data':
641662
file_path = f"{base_url}/dialogs/info_feature/{global_vars.project_type.lower()}/{feature_type.lower()}/{feature.lower()}/tab_data.html"
@@ -651,8 +672,7 @@ def open_help_link(context, uiname, dlg=None):
651672
# Fallback to the general manual link if context and uiname are missing
652673
file_path = f"{base_url}/index.html"
653674

654-
print(file_path)
655-
tools_os.open_file(file_path)
675+
_open_external_url(file_path)
656676

657677

658678
def open_dialog(dlg, dlg_name=None, stay_on_top=False, title=None, title_params=None, hide_config_widgets=False, plugin_dir=lib_vars.plugin_dir, plugin_name=lib_vars.plugin_name, skip_db_check=False):
@@ -7566,15 +7586,15 @@ def open_dlg_help():
75667586
parser = configparser.ConfigParser(comment_prefixes=";", allow_no_value=True, strict=False)
75677587
path = f"{lib_vars.plugin_dir}{os.sep}config{os.sep}giswater.config"
75687588
if not os.path.exists(path):
7569-
webbrowser.open_new_tab('https://giswater.gitbook.io/giswater-manual')
7589+
_open_external_url('https://giswater.gitbook.io/giswater-manual')
75707590
return True
75717591

75727592
try:
75737593
parser.read(path)
75747594
web_tag = parser.get('web_tag', lib_vars.session_vars['last_focus'])
7575-
webbrowser.open_new_tab(f'https://giswater.gitbook.io/giswater-manual/{web_tag}')
7595+
_open_external_url(f'https://giswater.gitbook.io/giswater-manual/{web_tag}')
75767596
except Exception:
7577-
webbrowser.open_new_tab('https://giswater.gitbook.io/giswater-manual')
7597+
_open_external_url('https://giswater.gitbook.io/giswater-manual')
75787598
finally:
75797599
return True
75807600

0 commit comments

Comments
 (0)