Skip to content

Commit 50dd978

Browse files
authored
Adds Layers tab to project widget (#56)
* added layers widget fixed absorption saving fixed tests * added error reporting for invalid layers * made layers tab hide if not standard layers * review fixes
1 parent f8e884c commit 50dd978

File tree

6 files changed

+436
-40
lines changed

6 files changed

+436
-40
lines changed

rascal2/widgets/delegates.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,28 @@ def setEditorData(self, editor: AdaptiveDoubleSpinBox, index):
7676
def setModelData(self, editor, model, index):
7777
data = editor.value()
7878
model.setData(index, data, QtCore.Qt.ItemDataRole.EditRole)
79+
80+
81+
class ParametersDelegate(QtWidgets.QStyledItemDelegate):
82+
"""Item delegate to choose from existing draft project parameters."""
83+
84+
def __init__(self, project_widget, parent):
85+
super().__init__(parent)
86+
self.project_widget = project_widget
87+
88+
def createEditor(self, parent, option, index):
89+
widget = QtWidgets.QComboBox(parent)
90+
parameters = self.project_widget.draft_project["parameters"]
91+
names = [p.name for p in parameters]
92+
widget.addItems(names)
93+
widget.setCurrentText(index.data(QtCore.Qt.ItemDataRole.DisplayRole))
94+
95+
return widget
96+
97+
def setEditorData(self, editor: QtWidgets.QWidget, index):
98+
data = index.data(QtCore.Qt.ItemDataRole.DisplayRole)
99+
editor.setCurrentText(data)
100+
101+
def setModelData(self, editor, model, index):
102+
data = editor.currentText()
103+
model.setData(index, data, QtCore.Qt.ItemDataRole.EditRole)

rascal2/widgets/project/models.py

Lines changed: 118 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from RATapi.utils.enums import Procedures
99

1010
from rascal2.config import path_for
11-
from rascal2.widgets.delegates import ValidatedInputDelegate, ValueSpinBoxDelegate
11+
from rascal2.widgets.delegates import ParametersDelegate, ValidatedInputDelegate, ValueSpinBoxDelegate
1212

1313

1414
class ClassListModel(QtCore.QAbstractTableModel):
@@ -28,12 +28,21 @@ class ClassListModel(QtCore.QAbstractTableModel):
2828
def __init__(self, classlist: RATapi.ClassList, parent: QtWidgets.QWidget):
2929
super().__init__(parent)
3030
self.parent = parent
31+
32+
self.classlist: RATapi.ClassList
33+
self.item_type: type
34+
self.headers: list[str]
35+
36+
self.setup_classlist(classlist)
37+
self.edit_mode = False
38+
39+
def setup_classlist(self, classlist: RATapi.ClassList):
40+
"""Setup the ClassList, type and headers for the model."""
3141
self.classlist = classlist
3242
self.item_type = classlist._class_handle
3343
if not issubclass(self.item_type, pydantic.BaseModel):
3444
raise NotImplementedError("ClassListModel only works for classlists of Pydantic models!")
3545
self.headers = list(self.item_type.model_fields)
36-
self.edit_mode = False
3746

3847
def rowCount(self, parent=None) -> int:
3948
return len(self.classlist)
@@ -80,7 +89,12 @@ def headerData(self, section, orientation, role=QtCore.Qt.ItemDataRole.DisplayRo
8089
and role == QtCore.Qt.ItemDataRole.DisplayRole
8190
and section != 0
8291
):
83-
return self.headers[section - 1].replace("_", " ").title()
92+
header = self.headers[section - 1]
93+
if "SLD" in header:
94+
header = header.replace("_", " ")
95+
else:
96+
header = header.replace("_", " ").title()
97+
return header
8498
return None
8599

86100
def append_item(self):
@@ -296,3 +310,104 @@ def edit(self):
296310
for i in range(0, self.model.rowCount()):
297311
if i in self.model.protected_indices:
298312
self.table.setIndexWidget(self.model.index(i, 0), None)
313+
314+
315+
class LayersModel(ClassListModel):
316+
"""Classlist model for Layers."""
317+
318+
def __init__(self, classlist: RATapi.ClassList, parent: QtWidgets.QWidget):
319+
super().__init__(classlist, parent)
320+
self.absorption = classlist._class_handle == RATapi.models.AbsorptionLayer
321+
self.SLD_imags = {}
322+
323+
def flags(self, index):
324+
flags = super().flags(index)
325+
if self.edit_mode:
326+
flags |= QtCore.Qt.ItemFlag.ItemIsEditable
327+
return flags
328+
329+
def append_item(self):
330+
kwargs = {"thickness": "", "SLD": "", "roughness": ""}
331+
if self.absorption:
332+
kwargs["SLD_imaginary"] = ""
333+
self.classlist.append(self.item_type(**kwargs))
334+
self.endResetModel()
335+
336+
def set_absorption(self, absorption: bool):
337+
"""Set whether the project is using absorption or not.
338+
339+
Parameters
340+
----------
341+
absorption : bool
342+
Whether the project is using absorption.
343+
344+
"""
345+
if self.absorption != absorption:
346+
self.beginResetModel()
347+
self.absorption = absorption
348+
if absorption:
349+
classlist = RATapi.ClassList(
350+
[
351+
RATapi.models.AbsorptionLayer(
352+
**dict(layer),
353+
SLD_imaginary=self.SLD_imags.get(layer.name, ""),
354+
)
355+
for layer in self.classlist
356+
]
357+
)
358+
# set handle manually for if classlist is empty
359+
classlist._class_handle = RATapi.models.AbsorptionLayer
360+
else:
361+
# we save the SLD_imaginary values so that they aren't lost if the
362+
# user accidentally toggles absorption off and on!
363+
self.SLD_imags = {layer.name: layer.SLD_imaginary for layer in self.classlist}
364+
classlist = RATapi.ClassList(
365+
[
366+
RATapi.models.Layer(
367+
name=layer.name,
368+
thickness=layer.thickness,
369+
SLD=layer.SLD_real,
370+
roughness=layer.roughness,
371+
hydration=layer.hydration,
372+
hydrate_with=layer.hydrate_with,
373+
)
374+
for layer in self.classlist
375+
]
376+
)
377+
classlist._class_handle = RATapi.models.Layer
378+
self.setup_classlist(classlist)
379+
self.parent.parent.parent.update_draft_project({"layers": classlist})
380+
self.endResetModel()
381+
382+
383+
class LayerFieldWidget(ProjectFieldWidget):
384+
"""Project field widget for Layer objects."""
385+
386+
classlist_model = LayersModel
387+
388+
def __init__(self, field, parent):
389+
super().__init__(field, parent)
390+
self.project_widget = parent.parent
391+
392+
def set_item_delegates(self):
393+
for i in range(1, self.model.columnCount()):
394+
if i in [1, self.model.columnCount() - 1]:
395+
header = self.model.headers[i - 1]
396+
self.table.setItemDelegateForColumn(
397+
i, ValidatedInputDelegate(self.model.item_type.model_fields[header], self.table)
398+
)
399+
else:
400+
self.table.setItemDelegateForColumn(i, ParametersDelegate(self.project_widget, self.table))
401+
402+
def set_absorption(self, absorption: bool):
403+
"""Set whether the classlist uses AbsorptionLayers.
404+
405+
Parameters
406+
----------
407+
absorption : bool
408+
Whether the classlist should use AbsorptionLayers.
409+
410+
"""
411+
self.model.set_absorption(absorption)
412+
if self.model.edit_mode:
413+
self.edit()

0 commit comments

Comments
 (0)