Skip to content

Commit ad01dac

Browse files
authored
Merge pull request #36 from Loop3D/fix/add-folds
Fix/add folds
2 parents 4286e07 + 67b06ba commit ad01dac

25 files changed

+1486
-1830
lines changed

loopstructural/gui/modelling/geological_model_tab.py

Lines changed: 0 additions & 92 deletions
This file was deleted.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .geological_model_tab import GeologicalModelTab
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import os
2+
3+
from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QVBoxLayout
4+
from PyQt5.uic import loadUi
5+
6+
7+
class AddFaultDialog(QDialog):
8+
def __init__(self, parent=None):
9+
super().__init__(parent)
10+
ui_path = os.path.join(os.path.dirname(__file__), 'add_fault_dialog.ui')
11+
loadUi(ui_path, self)
12+
self.setWindowTitle('Add Fault Feature')
13+
# You can access widgets by their objectName from the .ui file
14+
# Example: self.strike_input, self.dip_input, etc.
15+
16+
def get_fault_data(self):
17+
return {
18+
'strike': self.strike_input.value(),
19+
'dip': self.dip_input.value(),
20+
'centre': (
21+
self.centre_x_input.value(),
22+
self.centre_y_input.value(),
23+
self.centre_z_input.value(),
24+
),
25+
'ellipsoid_extents': (
26+
self.extent_x_input.value(),
27+
self.extent_y_input.value(),
28+
self.extent_z_input.value(),
29+
),
30+
'displacement': self.displacement_input.value(),
31+
'pitch': self.pitch_input.value(),
32+
'name': self.name_input.text(),
33+
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<ui version="4.0">
3+
<class>AddFaultDialog</class>
4+
<widget class="QDialog" name="AddFaultDialog">
5+
<property name="windowTitle">
6+
<string>Add Fault Feature</string>
7+
</property>
8+
<layout class="QVBoxLayout" name="verticalLayout">
9+
<item>
10+
<layout class="QFormLayout" name="formLayout">
11+
<item row="0" column="0">
12+
<widget class="QLabel" name="labelStrike">
13+
<property name="text">
14+
<string>Strike (°)</string>
15+
</property>
16+
</widget>
17+
</item>
18+
<item row="0" column="1">
19+
<widget class="QDoubleSpinBox" name="strike_input"/>
20+
</item>
21+
<item row="1" column="0">
22+
<widget class="QLabel" name="labelDip">
23+
<property name="text">
24+
<string>Dip (°)</string>
25+
</property>
26+
</widget>
27+
</item>
28+
<item row="1" column="1">
29+
<widget class="QDoubleSpinBox" name="dip_input"/>
30+
</item>
31+
<item row="2" column="0">
32+
<widget class="QLabel" name="labelCentre">
33+
<property name="text">
34+
<string>Centre (X, Y, Z)</string>
35+
</property>
36+
</widget>
37+
</item>
38+
<item row="2" column="1">
39+
<layout class="QHBoxLayout" name="centreLayout">
40+
<item>
41+
<widget class="QDoubleSpinBox" name="centre_x_input"/>
42+
</item>
43+
<item>
44+
<widget class="QDoubleSpinBox" name="centre_y_input"/>
45+
</item>
46+
<item>
47+
<widget class="QDoubleSpinBox" name="centre_z_input"/>
48+
</item>
49+
</layout>
50+
</item>
51+
<item row="3" column="0">
52+
<widget class="QLabel" name="labelExtents">
53+
<property name="text">
54+
<string>Ellipsoid Extents (X, Y, Z)</string>
55+
</property>
56+
</widget>
57+
</item>
58+
<item row="3" column="1">
59+
<layout class="QHBoxLayout" name="extentsLayout">
60+
<item>
61+
<widget class="QDoubleSpinBox" name="extent_x_input"/>
62+
</item>
63+
<item>
64+
<widget class="QDoubleSpinBox" name="extent_y_input"/>
65+
</item>
66+
<item>
67+
<widget class="QDoubleSpinBox" name="extent_z_input"/>
68+
</item>
69+
</layout>
70+
</item>
71+
<item row="4" column="0">
72+
<widget class="QLabel" name="labelDisplacement">
73+
<property name="text">
74+
<string>Displacement</string>
75+
</property>
76+
</widget>
77+
</item>
78+
<item row="4" column="1">
79+
<widget class="QDoubleSpinBox" name="displacement_input"/>
80+
</item>
81+
<item row="5" column="0">
82+
<widget class="QLabel" name="labelPitch">
83+
<property name="text">
84+
<string>Pitch (°)</string>
85+
</property>
86+
</widget>
87+
</item>
88+
<item row="5" column="1">
89+
<widget class="QDoubleSpinBox" name="pitch_input"/>
90+
</item>
91+
<item row="6" column="0">
92+
<widget class="QLabel" name="labelName">
93+
<property name="text">
94+
<string>Name</string>
95+
</property>
96+
</widget>
97+
</item>
98+
<item row="6" column="1">
99+
<widget class="QLineEdit" name="name_input"/>
100+
</item>
101+
</layout>
102+
</item>
103+
<item>
104+
<widget class="QDialogButtonBox" name="buttonBox">
105+
<property name="standardButtons">
106+
<set>QDialogButtonBox::Ok|QDialogButtonBox::Cancel</set>
107+
</property>
108+
</widget>
109+
</item>
110+
</layout>
111+
</widget>
112+
<resources/>
113+
<connections/>
114+
</ui>
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
import os
2+
3+
from PyQt5.QtWidgets import QDialog, QDialogButtonBox
4+
from PyQt5.uic import loadUi
5+
6+
from .layer_selection_table import LayerSelectionTable
7+
8+
9+
class AddFoliationDialog(QDialog):
10+
def __init__(self, parent=None, *, data_manager=None, model_manager=None):
11+
super().__init__(parent)
12+
self.data_manager = data_manager
13+
self.model_manager = model_manager
14+
ui_path = os.path.join(os.path.dirname(__file__), 'add_foliation_dialog.ui')
15+
loadUi(ui_path, self)
16+
self.setWindowTitle('Add Foliation')
17+
18+
# Create the layer selection table widget
19+
self.layer_table = LayerSelectionTable(
20+
data_manager=self.data_manager,
21+
feature_name_provider=lambda: self.name,
22+
name_validator=lambda: (self.name_valid, self.name_error)
23+
)
24+
25+
# Replace or integrate with existing UI
26+
self._integrate_layer_table()
27+
28+
self.buttonBox.accepted.connect(self.add_foliation)
29+
self.buttonBox.rejected.connect(self.cancel)
30+
31+
self.modelFeatureComboBox.addItems(
32+
[f.name for f in self.model_manager.features() if not f.name.startswith("__")]
33+
)
34+
self.name_valid = False
35+
self.name_error = ""
36+
37+
def validate_name_field(text):
38+
"""Validate the feature name field."""
39+
valid = True
40+
old_name = self.name
41+
new_name = text.strip()
42+
43+
if not new_name:
44+
valid = False
45+
self.name_error = "Feature name cannot be empty."
46+
elif new_name in [f.name for f in self.model_manager.features()]:
47+
valid = False
48+
self.name_error = "Feature name must be unique."
49+
elif new_name in self.data_manager.feature_data:
50+
valid = False
51+
self.name_error = "Layer already exists in the data manager."
52+
53+
if not valid:
54+
self.name_valid = False
55+
self.feature_name_input.setStyleSheet("border: 1px solid red;")
56+
else:
57+
self.feature_name_input.setStyleSheet("")
58+
self.name_valid = True
59+
60+
# Enable/disable the OK button based on validation
61+
self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(self.name_valid)
62+
63+
# If the name changes, update the data manager key and reinitialize table
64+
if old_name != new_name and old_name in self.data_manager.feature_data:
65+
# Save current table data
66+
old_data = self.layer_table.get_table_data()
67+
68+
# Remove old key and set new key
69+
self.data_manager.feature_data.pop(old_name, None)
70+
if new_name and valid:
71+
self.data_manager.feature_data[new_name] = old_data
72+
73+
# Update table to reflect new feature name
74+
self.layer_table.initialize_feature_data()
75+
self.layer_table.restore_table_state()
76+
77+
self.feature_name_input.textChanged.connect(validate_name_field)
78+
79+
@property
80+
def name(self):
81+
return self.feature_name_input.text().strip()
82+
83+
def add_foliation(self):
84+
if not self.name_valid:
85+
self.data_manager.logger(f'Name is invalid: {self.name_error}', log_level=2)
86+
return
87+
88+
# Ensure table state is synchronized with data manager
89+
self.layer_table.sync_table_with_data()
90+
91+
# Check if we have any layers selected
92+
if not self.layer_table.has_layers():
93+
self.data_manager.logger("No layers selected for the foliation.", log_level=2)
94+
return
95+
96+
folded_feature_name = None
97+
if self.modelFeatureComboBox.currentText() != "":
98+
folded_feature_name = self.modelFeatureComboBox.currentText()
99+
100+
self.data_manager.add_foliation_to_model(self.name, folded_feature_name=folded_feature_name)
101+
self.accept() # Close the dialog
102+
103+
def cancel(self):
104+
# Clean up any temporary data if necessary
105+
if self.name in self.data_manager.feature_data:
106+
self.data_manager.feature_data.pop(self.name, None)
107+
self.reject()
108+
109+
def _integrate_layer_table(self):
110+
"""Integrate the layer table widget with the existing UI."""
111+
# Try to replace existing table widget if it exists
112+
if hasattr(self, 'items_table'):
113+
table_parent = self.items_table.parent()
114+
115+
# Get the position of the original table
116+
if hasattr(table_parent, 'layout') and table_parent.layout():
117+
layout = table_parent.layout()
118+
119+
# Find the index of the original table in the layout
120+
table_index = -1
121+
for i in range(layout.count()):
122+
item = layout.itemAt(i)
123+
if item and item.widget() == self.items_table:
124+
table_index = i
125+
break
126+
127+
# Remove original widgets
128+
if hasattr(self, 'items_table'):
129+
self.items_table.setParent(None)
130+
if hasattr(self, 'add_item_button'):
131+
self.add_item_button.setParent(None)
132+
133+
# Insert new widget at the same position
134+
if table_index >= 0:
135+
layout.insertWidget(table_index, self.layer_table)
136+
else:
137+
layout.addWidget(self.layer_table)
138+
else:
139+
# Fallback: add to parent widget directly
140+
if hasattr(table_parent, 'layout') and not table_parent.layout():
141+
from PyQt5.QtWidgets import QVBoxLayout
142+
layout = QVBoxLayout(table_parent)
143+
table_parent.setLayout(layout)
144+
layout.addWidget(self.layer_table)
145+
146+
# If no existing table found, try to add to main layout
147+
elif hasattr(self, 'layout') and self.layout():
148+
self.layout().addWidget(self.layer_table)

0 commit comments

Comments
 (0)