Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
272 changes: 266 additions & 6 deletions docs/development/usability-plan.md

Large diffs are not rendered by default.

52 changes: 7 additions & 45 deletions loopstructural/gui/modelling/base_tab.py
Original file line number Diff line number Diff line change
@@ -1,54 +1,16 @@
from qgis.gui import QgsCollapsibleGroupBox
from qgis.PyQt.QtCore import Qt
from qgis.PyQt.QtWidgets import QScrollArea, QSizePolicy, QVBoxLayout, QWidget
from qgis.PyQt.QtWidgets import QVBoxLayout, QWidget


class BaseTab(QWidget):
def __init__(self, parent=None, data_manager=None, scrollable=False):
"""A tab with a vertical layout. The page that holds the tab has the scroll area."""

def __init__(self, parent=None, data_manager=None):
super().__init__(parent)
self.data_manager = data_manager
# Initialize a default layout for all tabs
if scrollable:
self.setAttribute(Qt.WidgetAttribute.WA_TransparentForMouseEvents, True)
self.scroll_area = QScrollArea(self)
self.scroll_area.setWidgetResizable(True)
self.scroll_area.setAttribute(Qt.WidgetAttribute.WA_TransparentForMouseEvents, False)
# Create a container widget for the scroll area
self.container_widget = QWidget()
self.scroll_area.setWidget(self.container_widget)
# Ensure the scroll area and its container widget can handle focus and mouse events
self.scroll_area.setFocusPolicy(Qt.FocusPolicy.NoFocus)
self.scroll_area.setFrameShape(
QScrollArea.Shape.NoFrame
) # Remove any unnecessary frame

# Explicitly set size policies to ensure proper interaction
self.scroll_area.setSizePolicy(
QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding
)
# Set up a layout for the container widget
self.container_layout = QVBoxLayout(self.container_widget)
# Set the main layout for the BaseTab
self.main_layout = QVBoxLayout(self)
self.setAttribute(Qt.WidgetAttribute.WA_TransparentForMouseEvents, False)
self.main_layout.addWidget(self.scroll_area)

self.container_widget.setSizePolicy(
QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred
)

# Ensure the container widget propagates mouse events properly
self.container_widget.setAttribute(
Qt.WidgetAttribute.WA_TransparentForMouseEvents, False
)

self.setLayout(self.main_layout)
else:
# If not scrollable, use a simple layout
self.container_layout = QVBoxLayout(self)
self.setLayout(self.container_layout)

# Set the layout for the tab
self.container_layout = QVBoxLayout(self)
self.container_layout.setContentsMargins(0, 0, 0, 0)
self.setLayout(self.container_layout)

def add_widget(self, widget, name=None, group_box=True):
"""Add a widget to the tab."""
Expand Down
2 changes: 1 addition & 1 deletion loopstructural/gui/modelling/geological_history_tab.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

class GeologialHistoryTab(BaseTab):
def __init__(self, parent=None, data_manager=None):
super().__init__(parent, data_manager, scrollable=False)
super().__init__(parent, data_manager)
# Load the UI file for Tab 1
self.stratigraphic_column_widget = StratColumnWidget(self, data_manager=data_manager)
# Add the loaded UI widget to the container layout
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
QLabel,
QMessageBox,
QPushButton,
QScrollArea,
QSpinBox,
QVBoxLayout,
QWidget,
Expand All @@ -23,6 +22,7 @@

from ....background_task import finish_background_task, start_background_task
from ....messages import push_info, push_warning
from ...steps.section_stack import SectionStack
from ..bounding_box_widget import BoundingBoxWidget
from ..layer_selection_table import LayerSelectionTable

Expand Down Expand Up @@ -85,24 +85,13 @@ def __init__(self, parent=None, *, feature=None, model_manager=None, data_manage
self.feature = feature
self.model_manager = model_manager
self.data_manager = data_manager
# Create a scroll area for horizontal scrolling
scroll = QScrollArea(self)
scroll.setWidgetResizable(True)
scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
scroll.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)

# Create content widget to hold the form layout
content = QWidget()
self.layout = QVBoxLayout(content)
# Set the content widget as the scroll area's widget
scroll.setWidget(content)

# Add scroll area to main layout
# The sections of the panel. At most two are open. The page that holds
# the panel has the scroll area.
mainLayout = QVBoxLayout(self)
mainLayout.addWidget(scroll)

# Set the main layout
self.setLayout(mainLayout)
mainLayout.setContentsMargins(0, 0, 0, 0)
self.layout = SectionStack(self, 'feature_details', data_manager)
mainLayout.addWidget(self.layout)
mainLayout.addStretch(1)

## define interpolator parameters
# Regularisation spin box
Expand Down Expand Up @@ -176,9 +165,17 @@ def __init__(self, parent=None, *, feature=None, model_manager=None, data_manage
form_layout.addRow('Orientation point weight', self.npw_spin_box)
group_box = QgsCollapsibleGroupBox('Interpolator Settings')
group_box.setLayout(form_layout)
self.layout.addWidget(group_box)
self.layout.addWidget(self._build_preview_widget())
self.layout.addWidget(table_group_box)
self.layout.add_section(
group_box,
'interpolator',
'Interpolator Settings',
summary=lambda: (
f"{self.interpolator_type_combo.currentText()}, "
f"{int(self.n_elements_spinbox.value())} elements"
),
)
self.layout.add_section(self._build_preview_widget(), 'preview', 'Preview', collapsed=True)
self.layout.add_section(table_group_box, 'data_layers', 'Data Layers')
# this will call the addMidBlock and addExportBlock methods
self.addMidBlock()
self.addExportBlock()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,4 +115,3 @@ def update_dip(value):

self.layout.addWidget(orientation_group)
self.layout.addWidget(ellipsoid_group)
self.setLayout(self.layout)
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,6 @@ def addMidBlock(self):
group_box.setLayout(form_layout)
self.layout.addWidget(group_box)
# Remove redundant layout setting
self.setLayout(self.layout)

def open_splot_dialog(self):
dialog = SPlotDialog(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,5 @@ def addMidBlock(self):
group_box.setLayout(form_layout)
self.layout.addWidget(group_box)

# Remove redundant layout setting
self.setLayout(self.layout)

def on_fold_frame_changed(self, text):
self.model_manager.add_fold_to_feature(self.feature.name, fold_frame_name=text)
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from ....main.workflow_mode import DEFAULT_WORKFLOW_MODE, WORKFLOW_MODE_CONSTRAINTS
from ...messages import push_info
from ..steps import build_plan
from ..steps.section_stack import page_scroll_area
from .add_fault_dialog import AddFaultDialog
from .add_foliation_dialog import AddFoliationDialog
from .add_unconformity_dialog import AddUnconformityDialog
Expand Down Expand Up @@ -187,7 +188,7 @@ def __init__(self, parent=None, *, model_manager=None, data_manager=None):
# The problems of all steps. They show before the build, so the user
# sees why a build can fail or can give a poor model.
self.problemsLabel = QLabel()
self.problemsLabel.setWordWrap(True)
self.problemsLabel.setWordWrap(False)
self.problemsLabel.setTextFormat(Qt.TextFormat.RichText)
self.problemsLabel.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed)
mainLayout.insertWidget(1, self.problemsLabel, 0)
Expand Down Expand Up @@ -316,9 +317,11 @@ def refresh_primary_action(self, *args, **kwargs):
problems = self._problems_provider() if self._problems_provider is not None else []
if problems:
lines = "".join(f"<li>{html.escape(message)}</li>" for _key, message in problems)
count = len(problems)
self.problemsLabel.setText(
f"<b>Check before the build:</b><ul style='margin:0'>{lines}</ul>"
f"<b>Check before the build:</b> {count} problem{'s' if count != 1 else ''}"
)
self.problemsLabel.setToolTip(f"<ul style='margin:0'>{lines}</ul>")
self.problemsLabel.show()
else:
self.problemsLabel.hide()
Expand Down Expand Up @@ -756,7 +759,8 @@ def _set_details_panel(self, panel, feature_name, feature):
# Dynamically replace the featureDetailsPanel widget
splitter = self._splitter
splitter.widget(1).deleteLater() # Remove the existing widget
splitter.addWidget(panel) # Add the new widget
# The page has one scroll area, here. The panel does not have its own.
splitter.addWidget(page_scroll_area(panel))

def _on_model_update_started(self):
"""Show a non-blocking indeterminate progress dialog for model updates.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from qgis.PyQt.QtWidgets import QSizePolicy

from loopstructural.gui.modelling.base_tab import BaseTab
from loopstructural.gui.modelling.steps.section_stack import SectionStack

from .bounding_box import BoundingBoxWidget
from .dem import DEMWidget
Expand All @@ -14,14 +15,34 @@ class ModelDefinitionTab(BaseTab):
"""

def __init__(self, parent=None, data_manager=None):
super().__init__(parent, data_manager, scrollable=True)
# Add widgets to the QToolBox
super().__init__(parent, data_manager)
self.bounding_box = BoundingBoxWidget(self, data_manager)
self.dem = DEMWidget(self, data_manager)

# Set uniform size policy for all widgets
for widget in [self.bounding_box, self.dem]:
widget.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)

self.add_widget(self.bounding_box, 'Bounding Box')
self.add_widget(self.dem, 'DEM')
self.sections = SectionStack(self, 'data', data_manager)
self.sections.add_section(
self.bounding_box,
'bounding_box',
'Bounding Box',
summary=self._bounding_box_summary,
)
self.sections.add_section(self.dem, 'dem', 'DEM', summary=self._dem_summary)
self.container_layout.addWidget(self.sections)
self.container_layout.addStretch(1)

def _bounding_box_summary(self):
if self.data_manager is None or not self.data_manager.is_bounding_box_set():
return "not set"
return "set"

def _dem_summary(self):
if self.data_manager is None:
return ""
layer = getattr(self.data_manager, 'dem_layer', None)
if getattr(self.data_manager, 'use_dem', False) and layer is not None:
return layer.name()
return "flat elevation"
11 changes: 10 additions & 1 deletion loopstructural/gui/modelling/modelling_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
QHBoxLayout,
QLabel,
QPushButton,
QSizePolicy,
QStackedWidget,
QVBoxLayout,
QWidget,
Expand All @@ -18,6 +19,7 @@
StratigraphyStep,
ViewStep,
)
from loopstructural.gui.modelling.steps.section_stack import SectionStack
from loopstructural.gui.modelling.steps.step_bar import StepBar

# How often the checks of the steps run again, in milliseconds. Several
Expand Down Expand Up @@ -88,7 +90,10 @@ def __init__(
self.stack.addWidget(page)

self.footer_label = QLabel(self)
self.footer_label.setWordWrap(True)
# One line. The tooltip has all messages. The label can be shorter than
# its text, so a long message does not make the dock wider or taller.
self.footer_label.setWordWrap(False)
self.footer_label.setSizePolicy(QSizePolicy.Policy.Ignored, QSizePolicy.Policy.Preferred)
self.back_button = QPushButton("< Back", self)
self.next_button = QPushButton("Next >", self)
self.back_button.clicked.connect(lambda _checked=False: self.go_to(self._neighbour(-1)))
Expand All @@ -99,6 +104,8 @@ def __init__(
footer.addWidget(self.next_button)

mainLayout = QVBoxLayout(self)
mainLayout.setContentsMargins(4, 2, 4, 2)
mainLayout.setSpacing(2)
mainLayout.addWidget(self.header)
mainLayout.addWidget(self.step_bar)
mainLayout.addWidget(self.stack, 1)
Expand Down Expand Up @@ -203,6 +210,8 @@ def refresh_status(self):
self.model_step.tab.refresh_primary_action()
self.view_step.refresh()
self.next_button.setText(self._next_text(self.current_index))
for section_stack in self.findChildren(SectionStack):
section_stack.refresh_summaries()
page = self.pages[self.current_index]
check = results[page.key]
if check.messages:
Expand Down
Loading
Loading