diff --git a/loopstructural/3d_icon.png b/loopstructural/3d_icon.png
new file mode 100644
index 0000000..bc2bc71
Binary files /dev/null and b/loopstructural/3d_icon.png differ
diff --git a/loopstructural/gui/dlg_settings.py b/loopstructural/gui/dlg_settings.py
index 5f7bef2..c4caaaf 100644
--- a/loopstructural/gui/dlg_settings.py
+++ b/loopstructural/gui/dlg_settings.py
@@ -85,6 +85,7 @@ def apply(self):
# misc
settings.debug_mode = self.opt_debug.isChecked()
+ settings.separate_dock_widgets = self.opt_separate_dock_widgets.isChecked()
settings.interpolator_nelements = self.n_elements_spin_box.value()
settings.interpolator_npw = self.npw_spin_box.value()
settings.interpolator_cpw = self.cpw_spin_box.value()
@@ -106,6 +107,7 @@ def load_settings(self):
# global
self.opt_debug.setChecked(settings.debug_mode)
+ self.opt_separate_dock_widgets.setChecked(settings.separate_dock_widgets)
self.lbl_version_saved_value.setText(settings.version)
# self.interpolator_type_combo.setCurrentText(settings.interpolator_type)
self.n_elements_spin_box.setValue(settings.interpolator_nelements)
diff --git a/loopstructural/gui/dlg_settings.ui b/loopstructural/gui/dlg_settings.ui
index b910f9f..edba4d3 100644
--- a/loopstructural/gui/dlg_settings.ui
+++ b/loopstructural/gui/dlg_settings.ui
@@ -226,6 +226,37 @@
+ -
+
+
+
+ 0
+ 25
+
+
+
+
+ 16777215
+ 30
+
+
+
+ Show modelling and visualisation in separate dock widgets.
+
+
+ true
+
+
+
+
+
+ Use separate dock widgets for modelling and visualisation
+
+
+ false
+
+
+
-
diff --git a/loopstructural/gui/loop_widget.py b/loopstructural/gui/loop_widget.py
index a0fb574..bf78f50 100644
--- a/loopstructural/gui/loop_widget.py
+++ b/loopstructural/gui/loop_widget.py
@@ -51,3 +51,23 @@ def __init__(
)
tabWidget.addTab(self.modelling_widget, "Modelling")
tabWidget.addTab(self.visualisation_widget, "Visualisation")
+
+ def get_modelling_widget(self):
+ """Return the modelling widget instance.
+
+ Returns
+ -------
+ ModellingWidget
+ The modelling widget.
+ """
+ return self.modelling_widget
+
+ def get_visualisation_widget(self):
+ """Return the visualisation widget instance.
+
+ Returns
+ -------
+ VisualisationWidget
+ The visualisation widget.
+ """
+ return self.visualisation_widget
diff --git a/loopstructural/plugin_main.py b/loopstructural/plugin_main.py
index 12bbd18..795eef1 100644
--- a/loopstructural/plugin_main.py
+++ b/loopstructural/plugin_main.py
@@ -35,7 +35,7 @@
from loopstructural.gui.loop_widget import LoopWidget
from loopstructural.main.data_manager import ModellingDataManager
from loopstructural.main.model_manager import GeologicalModelManager
-from loopstructural.toolbelt import PlgLogger
+from loopstructural.toolbelt import PlgLogger, PlgOptionsManager
# ############################################################################
# ########## Classes ###############
@@ -127,9 +127,13 @@ def initGui(self):
self.tr("LoopStructural Modelling"),
self.iface.mainWindow(),
)
+ self.action_visualisation = QAction(
+ QIcon(os.path.dirname(__file__) + "/3D_icon.png"),
+ self.tr("LoopStructural Visualisation"),
+ self.iface.mainWindow(),
+ )
self.toolbar.addAction(self.action_modelling)
-
# -- Menu
self.iface.addPluginToMenu(__title__, self.action_settings)
self.iface.addPluginToMenu(__title__, self.action_help)
@@ -150,36 +154,102 @@ def initGui(self):
self.iface.pluginHelpMenu().addAction(self.action_help_plugin_menu_documentation)
## --- dock widget
- self.loop_dockwidget = QDockWidget(self.tr("Loop"), self.iface.mainWindow())
- self.loop_widget = LoopWidget(
- self.iface.mainWindow(),
- mapCanvas=self.iface.mapCanvas(),
- logger=self.log,
- data_manager=self.data_manager,
- model_manager=self.model_manager,
- )
+ # Get the setting for separate dock widgets
+ settings = PlgOptionsManager.get_plg_settings()
+
+ if settings.separate_dock_widgets:
+ # Create separate dock widgets for modelling and visualisation
+ self.loop_widget = LoopWidget(
+ self.iface.mainWindow(),
+ mapCanvas=self.iface.mapCanvas(),
+ logger=self.log,
+ data_manager=self.data_manager,
+ model_manager=self.model_manager,
+ )
+ self.toolbar.addAction(self.action_visualisation)
- self.loop_dockwidget.setWidget(self.loop_widget)
- self.iface.addDockWidget(Qt.RightDockWidgetArea, self.loop_dockwidget)
- right_docks = [
- d
- for d in self.iface.mainWindow().findChildren(QDockWidget)
- if self.iface.mainWindow().dockWidgetArea(d) == Qt.RightDockWidgetArea
- ]
- # If there are other dock widgets, tab this one with the first one found
- if right_docks:
- for dock in right_docks:
- if dock != self.loop_dockwidget:
- self.iface.mainWindow().tabifyDockWidget(dock, self.loop_dockwidget)
- # Optionally, bring your plugin tab to the front
- self.loop_dockwidget.raise_()
- break
- self.loop_dockwidget.show()
-
- self.loop_dockwidget.close()
-
- # -- Connect actions
- self.action_modelling.triggered.connect(self.loop_dockwidget.toggleViewAction().trigger)
+ # Create modelling dock
+ self.modelling_dockwidget = QDockWidget(
+ self.tr("Loop - Modelling"), self.iface.mainWindow()
+ )
+ self.modelling_dockwidget.setWidget(self.loop_widget.get_modelling_widget())
+ self.iface.addDockWidget(Qt.RightDockWidgetArea, self.modelling_dockwidget)
+
+ # Create visualisation dock
+ self.visualisation_dockwidget = QDockWidget(
+ self.tr("Loop - Visualisation"), self.iface.mainWindow()
+ )
+ self.visualisation_dockwidget.setWidget(self.loop_widget.get_visualisation_widget())
+ self.iface.addDockWidget(Qt.RightDockWidgetArea, self.visualisation_dockwidget)
+
+ # Tab them with other right docks if available
+ right_docks = [
+ d
+ for d in self.iface.mainWindow().findChildren(QDockWidget)
+ if self.iface.mainWindow().dockWidgetArea(d) == Qt.RightDockWidgetArea
+ ]
+ if right_docks:
+ for dock in right_docks:
+ if dock != self.modelling_dockwidget and dock != self.visualisation_dockwidget:
+ self.iface.mainWindow().tabifyDockWidget(dock, self.modelling_dockwidget)
+ self.modelling_dockwidget.raise_()
+ break
+
+ # Tab visualisation with modelling
+ self.iface.mainWindow().tabifyDockWidget(
+ self.modelling_dockwidget, self.visualisation_dockwidget
+ )
+
+ self.modelling_dockwidget.show()
+ self.visualisation_dockwidget.show()
+ self.modelling_dockwidget.close()
+ self.visualisation_dockwidget.close()
+
+ # Connect action to toggle modelling dock
+ self.action_modelling.triggered.connect(
+ self.modelling_dockwidget.toggleViewAction().trigger
+ )
+ self.action_visualisation.triggered.connect(
+ self.visualisation_dockwidget.toggleViewAction().trigger
+ )
+ # Store reference to main dock as None for unload compatibility
+ self.loop_dockwidget = None
+ else:
+ # Create single dock widget with tabs (default behavior)
+ self.loop_dockwidget = QDockWidget(self.tr("Loop"), self.iface.mainWindow())
+ self.loop_widget = LoopWidget(
+ self.iface.mainWindow(),
+ mapCanvas=self.iface.mapCanvas(),
+ logger=self.log,
+ data_manager=self.data_manager,
+ model_manager=self.model_manager,
+ )
+
+ self.loop_dockwidget.setWidget(self.loop_widget)
+ self.iface.addDockWidget(Qt.RightDockWidgetArea, self.loop_dockwidget)
+ right_docks = [
+ d
+ for d in self.iface.mainWindow().findChildren(QDockWidget)
+ if self.iface.mainWindow().dockWidgetArea(d) == Qt.RightDockWidgetArea
+ ]
+ # If there are other dock widgets, tab this one with the first one found
+ if right_docks:
+ for dock in right_docks:
+ if dock != self.loop_dockwidget:
+ self.iface.mainWindow().tabifyDockWidget(dock, self.loop_dockwidget)
+ # Optionally, bring your plugin tab to the front
+ self.loop_dockwidget.raise_()
+ break
+ self.loop_dockwidget.show()
+
+ self.loop_dockwidget.close()
+
+ # -- Connect actions
+ self.action_modelling.triggered.connect(self.loop_dockwidget.toggleViewAction().trigger)
+
+ # Store references to separate docks as None for unload compatibility
+ self.modelling_dockwidget = None
+ self.visualisation_dockwidget = None
def tr(self, message: str) -> str:
"""Translate a string using Qt translation API.
@@ -198,6 +268,17 @@ def tr(self, message: str) -> str:
def unload(self):
"""Clean up when plugin is disabled or uninstalled."""
+ # -- Clean up dock widgets
+ if self.loop_dockwidget:
+ self.iface.removeDockWidget(self.loop_dockwidget)
+ del self.loop_dockwidget
+ if self.modelling_dockwidget:
+ self.iface.removeDockWidget(self.modelling_dockwidget)
+ del self.modelling_dockwidget
+ if self.visualisation_dockwidget:
+ self.iface.removeDockWidget(self.visualisation_dockwidget)
+ del self.visualisation_dockwidget
+
# -- Clean up menu
self.iface.removePluginMenu(__title__, self.action_help)
self.iface.removePluginMenu(__title__, self.action_settings)
diff --git a/loopstructural/toolbelt/preferences.py b/loopstructural/toolbelt/preferences.py
index 108ac87..f40cfd4 100644
--- a/loopstructural/toolbelt/preferences.py
+++ b/loopstructural/toolbelt/preferences.py
@@ -33,6 +33,7 @@ class PlgSettingsStructure:
interpolator_regularisation: float = 1.0
interpolator_cpw: float = 1.0
interpolator_npw: float = 1.0
+ separate_dock_widgets: bool = False
class PlgOptionsManager:
diff --git a/tests/qgis/test_plg_preferences.py b/tests/qgis/test_plg_preferences.py
index c76ab46..f951749 100644
--- a/tests/qgis/test_plg_preferences.py
+++ b/tests/qgis/test_plg_preferences.py
@@ -36,6 +36,11 @@ def test_plg_preferences_structure(self):
self.assertIsInstance(settings.version, str)
self.assertEqual(settings.version, __version__)
+ # dock widgets setting
+ self.assertTrue(hasattr(settings, "separate_dock_widgets"))
+ self.assertIsInstance(settings.separate_dock_widgets, bool)
+ self.assertEqual(settings.separate_dock_widgets, False)
+
# ############################################################################
# ####### Stand-alone run ########