qgis-tem-loader

qgis plugin for loading TEM geophysical inversion XYZ files as 3D objects
git clone git://src.adamsgaard.dk/qgis-tem-loader # fast
git clone https://src.adamsgaard.dk/qgis-tem-loader.git # slow
Log | Files | Refs | README | LICENSE Back to index

commit c9ed72db057932ab4fed649bd24a0fa78b32f892
parent 0bd26dc511ebf9a2d1649541296409c8d0b4bb1c
Author: Anders Damsgaard <anders@adamsgaard.dk>
Date:   Fri, 15 May 2026 23:01:56 +0200

fix(qgis): support Qt6 dialog enums

Diffstat:
Mtem_loader/tem_loader.py | 24++++++++++++++++++++----
Mtest/test_core.py | 85++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
2 files changed, 96 insertions(+), 13 deletions(-)

diff --git a/tem_loader/tem_loader.py b/tem_loader/tem_loader.py @@ -72,6 +72,24 @@ def _writer_no_error(): return writer_error.NoError +def _dialog_button(name): + standard_button = getattr( + QDialogButtonBox, + 'StandardButton', + QDialogButtonBox, + ) + return getattr(standard_button, name) + + +def _dialog_buttons(): + return _dialog_button('Ok') | _dialog_button('Cancel') + + +def _dialog_code(name): + dialog_code = getattr(QDialog, 'DialogCode', QDialog) + return getattr(dialog_code, name) + + def _write_geopackage_layer(rows, gpkg_path, layer_name, wkb_type, crs, transform_context, action): fields = _build_fields(rows) @@ -126,9 +144,7 @@ class _ImportOptionsDialog(QDialog): form = QFormLayout() form.addRow('Opacity', self._opacity_spinbox) - buttons = QDialogButtonBox( - QDialogButtonBox.Ok | QDialogButtonBox.Cancel - ) + buttons = QDialogButtonBox(_dialog_buttons()) buttons.accepted.connect(self.accept) buttons.rejected.connect(self.reject) @@ -177,7 +193,7 @@ class TEMLoaderPlugin: return dialog = _ImportOptionsDialog(self.iface.mainWindow()) - if _exec_dialog(dialog) != QDialog.Accepted: + if _exec_dialog(dialog) != _dialog_code('Accepted'): return options = dialog.options() diff --git a/test/test_core.py b/test/test_core.py @@ -432,7 +432,7 @@ class ProcessXYZTests(unittest.TestCase): class PluginTests(unittest.TestCase): - def _import_plugin_module(self): + def _import_plugin_module(self, qt6_enums=False): class FakeSignal: def __init__(self): self._callbacks = [] @@ -462,10 +462,7 @@ class PluginTests(unittest.TestCase): def warning(*args): FakeMessageBox.warnings.append(args) - class FakeDialog: - Accepted = 1 - Rejected = 0 - + class FakeDialogBase: def __init__(self, *_args, **_kwargs): self.title = None self.layout = None @@ -482,6 +479,16 @@ class PluginTests(unittest.TestCase): def reject(self): pass + if qt6_enums: + class FakeDialog(FakeDialogBase): + class DialogCode: + Accepted = 1 + Rejected = 0 + else: + class FakeDialog(FakeDialogBase): + Accepted = 1 + Rejected = 0 + class FakeCheckBox: def __init__(self, text): self.text = text @@ -498,15 +505,22 @@ class PluginTests(unittest.TestCase): def isChecked(self): return self._checked - class FakeDialogButtonBox: - Ok = 1 - Cancel = 2 - + class FakeDialogButtonBoxBase: def __init__(self, buttons): self.buttons = buttons self.accepted = FakeSignal() self.rejected = FakeSignal() + if qt6_enums: + class FakeDialogButtonBox(FakeDialogButtonBoxBase): + class StandardButton: + Ok = 4 + Cancel = 8 + else: + class FakeDialogButtonBox(FakeDialogButtonBoxBase): + Ok = 1 + Cancel = 2 + class FakeFormLayout: def __init__(self): self.rows = [] @@ -876,6 +890,59 @@ class PluginTests(unittest.TestCase): }, ) + def test_import_options_dialog_supports_qt6_button_namespace(self): + module, _, _ = self._import_plugin_module(qt6_enums=True) + + dialog = module._ImportOptionsDialog(object()) + button_box = dialog.layout.items[-1] + + self.assertFalse(hasattr(module.QDialogButtonBox, "Ok")) + self.assertEqual( + button_box.buttons, + module.QDialogButtonBox.StandardButton.Ok + | module.QDialogButtonBox.StandardButton.Cancel, + ) + + def test_run_accepts_qt6_dialog_code_namespace(self): + module, file_dialog, _ = self._import_plugin_module(qt6_enums=True) + file_dialog.paths = ["/tmp/model.xyz"] + iface = Mock() + iface.mainWindow.return_value = object() + dialog = Mock() + dialog.options.return_value = { + "mask_below_doi": True, + "below_doi_opacity": 35, + } + module._ImportOptionsDialog = Mock(return_value=dialog) + module._exec_dialog = Mock(return_value=module.QDialog.DialogCode.Accepted) + plugin = module.TEMLoaderPlugin(iface) + plugin._load_xyz = Mock() + + plugin.run() + + self.assertFalse(hasattr(module.QDialog, "Accepted")) + plugin._load_xyz.assert_called_once_with( + Path("/tmp/model.xyz"), + mask_below_doi=True, + below_doi_opacity=35, + ) + + def test_run_rejects_qt6_dialog_code_namespace(self): + module, file_dialog, _ = self._import_plugin_module(qt6_enums=True) + file_dialog.paths = ["/tmp/model.xyz"] + iface = Mock() + iface.mainWindow.return_value = object() + dialog = Mock() + module._ImportOptionsDialog = Mock(return_value=dialog) + module._exec_dialog = Mock(return_value=module.QDialog.DialogCode.Rejected) + plugin = module.TEMLoaderPlugin(iface) + plugin._load_xyz = Mock() + + plugin.run() + + plugin._load_xyz.assert_not_called() + dialog.options.assert_not_called() + def test_exec_dialog_supports_exec_and_exec_apis(self): module, _, _ = self._import_plugin_module() exec_dialog = Mock()