From ba08fe7d8d1d000e6c2bc9b8f50997d985689324 Mon Sep 17 00:00:00 2001 From: Lukasz Debek Date: Wed, 4 Dec 2024 13:52:12 +0100 Subject: [PATCH] Schema 300 forms with timeseries (#285) * More schema 219 -> 300 plugin refactoring. --------- Co-authored-by: lukasz.debek --- threedi_schematisation_editor/data_models.py | 40 +- .../forms/custom_forms.py | 314 ++++++++- .../forms/ui/boundary_condition_1d.ui | 597 +++++++++++++++++ .../forms/ui/boundary_condition_2d.ui | 398 +++++++++++ .../forms/ui/lateral_1d.ui | 633 ++++++++++++++++++ .../forms/ui/lateral_2d.ui | 434 ++++++++++++ .../vector/boundary_condition_1d/default.qml | 555 +++++++++++---- .../vector/boundary_condition_2d/default.qml | 542 +++++++++++---- .../styles/vector/lateral_1d/default.qml | 549 ++++++++++++--- .../styles/vector/lateral_2d/default.qml | 537 ++++++++++++--- .../styles/vector/surface/default.qml | 562 ++++++++-------- .../user_layer_handlers.py | 66 +- 12 files changed, 4406 insertions(+), 821 deletions(-) create mode 100644 threedi_schematisation_editor/forms/ui/boundary_condition_1d.ui create mode 100644 threedi_schematisation_editor/forms/ui/boundary_condition_2d.ui create mode 100644 threedi_schematisation_editor/forms/ui/lateral_1d.ui create mode 100644 threedi_schematisation_editor/forms/ui/lateral_2d.ui diff --git a/threedi_schematisation_editor/data_models.py b/threedi_schematisation_editor/data_models.py index d1be5b0..998d0e0 100644 --- a/threedi_schematisation_editor/data_models.py +++ b/threedi_schematisation_editor/data_models.py @@ -447,6 +447,7 @@ class BoundaryCondition2D(ModelObject): __geometrytype__ = GeometryType.Linestring id: int + code: str display_name: str type: BoundaryType timeseries: str @@ -923,20 +924,6 @@ class CrossSectionDefinition(ModelObject): vegetation_drag_coefficients: Optional[str] -@dataclass -class Timeseries(ModelObject): - __tablename__ = "timeseries" - __layername__ = "Timeseries" - __geometrytype__ = GeometryType.NoGeometry - - id: int - reference_layer: str - reference_id: int - offset: int # seconds - duration: int # seconds - value: float - - @dataclass class MeasureLocation(ModelObject): __tablename__ = "measure_location" @@ -1030,13 +1017,6 @@ class VegetationDrag2DSettings(ModelObject): vegetation_drag_coefficient_file: Optional[str] -MODEL_BOUNDARY_CONDITION_ELEMENTS = ( - BoundaryCondition1D, - BoundaryCondition2D, - Lateral1D, - Lateral2D, -) - MODEL_1D_ELEMENTS = ( ConnectionNode, BoundaryCondition1D, @@ -1054,6 +1034,8 @@ class VegetationDrag2DSettings(ModelObject): ) MODEL_2D_ELEMENTS = ( + BoundaryCondition2D, + Lateral2D, DEMAverageArea, GridRefinementArea, GridRefinementLine, @@ -1068,9 +1050,9 @@ class VegetationDrag2DSettings(ModelObject): MODEL_0D_INFLOW_ELEMENTS = ( Surface, SurfaceMap, + SurfaceParameters, DryWeatherFlow, DryWeatherFlowMap, - SurfaceParameters, DryWeatherFlowDistribution, ) @@ -1099,18 +1081,8 @@ class VegetationDrag2DSettings(ModelObject): HIDDEN_ELEMENTS = tuple() -ALL_MODELS = ( - MODEL_BOUNDARY_CONDITION_ELEMENTS - + MODEL_1D_ELEMENTS - + MODEL_2D_ELEMENTS - + MODEL_1D2D_ELEMENTS - + MODEL_0D_INFLOW_ELEMENTS - + SETTINGS_ELEMENTS -) -ALL_MODELS = ALL_MODELS + ( - Timeseries, - CrossSectionDefinition, -) +ALL_MODELS = MODEL_1D_ELEMENTS + MODEL_2D_ELEMENTS + MODEL_1D2D_ELEMENTS + MODEL_0D_INFLOW_ELEMENTS + SETTINGS_ELEMENTS +ALL_MODELS = ALL_MODELS + (CrossSectionDefinition,) ALL_MODELS = ALL_MODELS + STRUCTURE_CONTROL_ELEMENTS + HIDDEN_ELEMENTS ELEMENTS_WITH_XS_DEF = ( diff --git a/threedi_schematisation_editor/forms/custom_forms.py b/threedi_schematisation_editor/forms/custom_forms.py index b3c3acc..11fba1d 100644 --- a/threedi_schematisation_editor/forms/custom_forms.py +++ b/threedi_schematisation_editor/forms/custom_forms.py @@ -420,6 +420,182 @@ def setup_tag_widgets(self): self.tags.setText(", ".join(tag_descriptions)) +class FormWithTimeseries(BaseForm): + """Base edit form for user layers with timeseries table reference.""" + + def __init__(self, *args, **kwargs): + super().__init__(*args, *kwargs) + self.timeseries_table_field_name = "timeseries" + self.timeseries_table = None + self.cross_section_table = None + self.timeseries_table_add = None + self.timeseries_table_delete = None + self.timeseries_table_copy = None + self.timeseries_table_paste = None + self.setup_timeseries_table_widgets() + + @property + def table_header(self): + return ["Time", "Value"] + + def setup_form_widgets(self): + """Setting up all form widgets.""" + super().setup_form_widgets() + self.update_timeseries_table_header() + + def setup_timeseries_table_widgets(self): + """Setup timeseries widgets.""" + timeseries_table_widget_name = "timeseries_table" + self.timeseries_table = self.dialog.findChild(QTableWidget, timeseries_table_widget_name) + self.timeseries_table_add = self.dialog.findChild(QPushButton, f"{timeseries_table_widget_name}_add") + self.timeseries_table_delete = self.dialog.findChild(QPushButton, f"{timeseries_table_widget_name}_delete") + self.timeseries_table_copy = self.dialog.findChild(QPushButton, f"{timeseries_table_widget_name}_copy") + self.timeseries_table_paste = self.dialog.findChild(QPushButton, f"{timeseries_table_widget_name}_paste") + for widget in [ + self.timeseries_table, + self.timeseries_table_add, + self.timeseries_table_delete, + self.timeseries_table_copy, + self.timeseries_table_paste, + ]: + self.custom_widgets[widget.objectName()] = widget + + def connect_custom_widgets(self): + """Connect other widgets.""" + super().connect_custom_widgets() + connect_signal(self.timeseries_table.cellChanged, self.save_timeseries_table_edits) + self.dialog.active_form_signals.add((self.timeseries_table.cellChanged, self.save_timeseries_table_edits)) + connect_signal(self.timeseries_table_add.clicked, self.add_table_row) + self.dialog.active_form_signals.add((self.timeseries_table_add.clicked, self.add_table_row)) + connect_signal(self.timeseries_table_delete.clicked, self.delete_table_rows) + self.dialog.active_form_signals.add((self.timeseries_table_delete.clicked, self.delete_table_rows)) + connect_signal(self.timeseries_table_paste.clicked, self.paste_table_rows) + self.dialog.active_form_signals.add((self.timeseries_table_paste.clicked, self.paste_table_rows)) + connect_signal(self.timeseries_table_copy.clicked, self.copy_table_rows) + self.dialog.active_form_signals.add((self.timeseries_table_copy.clicked, self.copy_table_rows)) + + def update_timeseries_table_header(self): + """Update timeseries table headers.""" + self.timeseries_table.setHorizontalHeaderLabels(self.table_header) + + def get_timeseries_table_values(self): + """Get timeseries table values.""" + num_of_rows = self.timeseries_table.rowCount() + num_of_cols = self.timeseries_table.columnCount() + timeseries_table_values = [] + for row_num in range(num_of_rows): + row_values = [] + for col_num in range(num_of_cols): + item = self.timeseries_table.item(row_num, col_num) + if item is not None: + item_text = item.text().strip() + else: + item_text = "" + row_values.append(item_text) + timeseries_table_values.append(row_values) + return timeseries_table_values + + def get_timeseries_table_text(self): + """Get timeseries table data as a string representation.""" + timeseries_table_values = self.get_timeseries_table_values() + timeseries_table_str = "\n".join(", ".join(row) for row in timeseries_table_values if all(row)) + return timeseries_table_str + + def save_timeseries_table_edits(self): + """ "Slot for handling table cells edits.""" + timeseries_table_str = self.get_timeseries_table_text() + if self.creation is True: + self.feature[self.timeseries_table_field_name] = timeseries_table_str + else: + timeseries_table_idx = self.layer.fields().lookupField(self.timeseries_table_field_name) + changes = {timeseries_table_idx: timeseries_table_str} + self.layer.changeAttributeValues(self.feature.id(), changes) + + def add_table_row(self): + """Slot for handling new row addition.""" + selected_rows = {idx.row() for idx in self.timeseries_table.selectedIndexes()} + if selected_rows: + last_row_number = max(selected_rows) + 1 + else: + last_row_number = self.timeseries_table.rowCount() + self.timeseries_table.insertRow(last_row_number) + + def delete_table_rows(self): + """Slot for handling deletion of the selected rows.""" + selected_rows = {idx.row() for idx in self.timeseries_table.selectedIndexes()} + for row_number in sorted(selected_rows, reverse=True): + self.timeseries_table.removeRow(row_number) + self.save_timeseries_table_edits() + + def paste_table_rows(self): + """Handling pasting new rows from the clipboard.""" + text = QApplication.clipboard().text() + rows = text.split("\n") + last_row_num = self.timeseries_table.rowCount() + disconnect_signal(self.timeseries_table.cellChanged, self.save_timeseries_table_edits) + for row in rows: + try: + height_str, width_str = row.replace(" ", "").split(",") + except ValueError: + continue + self.timeseries_table.insertRow(last_row_num) + self.timeseries_table.setItem(last_row_num, 0, QTableWidgetItem(height_str)) + self.timeseries_table.setItem(last_row_num, 1, QTableWidgetItem(width_str)) + last_row_num += 1 + connect_signal(self.timeseries_table.cellChanged, self.save_timeseries_table_edits) + self.save_timeseries_table_edits() + + def copy_table_rows(self): + """Slot for copying table values into the clipboard.""" + timeseries_table_values = self.get_timeseries_table_values() + clipboard_values = "\n".join([",".join(row_values) for row_values in timeseries_table_values]) + QApplication.clipboard().setText(clipboard_values) + + def clear_table_row_values(self): + """Slot for clearing table values.""" + num_of_rows = self.timeseries_table.rowCount() + num_of_cols = self.timeseries_table.columnCount() + disconnect_signal(self.timeseries_table.cellChanged, self.save_timeseries_table_edits) + for row_num in range(num_of_rows): + for col_num in range(num_of_cols): + self.timeseries_table.setItem(row_num, col_num, QTableWidgetItem("")) + connect_signal(self.timeseries_table.cellChanged, self.save_timeseries_table_edits) + self.save_timeseries_table_edits() + + def populate_timeseries_table_data(self): + """Populate timeseries tabular data in the table widget.""" + disconnect_signal(self.timeseries_table.cellChanged, self.save_timeseries_table_edits) + table = self.feature[self.timeseries_table_field_name] or "" + number_of_rows_main = len(table.split("\n")) + table_columns_count = len(self.table_header) + self.timeseries_table.clearContents() + self.timeseries_table.setRowCount(0) + self.timeseries_table.setColumnCount(table_columns_count) + self.update_timeseries_table_header() + for column_idx in range(table_columns_count): + self.timeseries_table.setItemDelegateForColumn(column_idx, NumericItemDelegate(self.timeseries_table)) + for row_num_main in range(number_of_rows_main): + self.timeseries_table.insertRow(row_num_main) + if self.feature is not None: + table = self.feature[self.timeseries_table_field_name] or "" + else: + table = "" + for row_number, row in enumerate(table.split("\n")): + row_values = [val for val in row.replace(" ", "").split(",") if val] + if len(row_values) != table_columns_count: + continue + for col_idx, row_value in enumerate(row_values): + self.timeseries_table.setItem(row_number, col_idx, QTableWidgetItem(row_value)) + connect_signal(self.timeseries_table.cellChanged, self.save_timeseries_table_edits) + + def populate_with_extra_widgets(self): + """Populate widgets for other layers attributes.""" + if self.creation is True: + self.fill_related_attributes() + self.populate_widgets() + self.populate_timeseries_table_data() + + class FormWithXSTable(BaseForm): """Base edit form for user layers with cross-section table reference.""" @@ -950,15 +1126,15 @@ class NodeToSurfaceMapForm(FormWithTags): def __init__(self, *args, **kwargs): super().__init__(*args, *kwargs) - self.surface_model = None - self.surface_id_field = None + self.dwf_model = None + self.dwf_id_field = None self.surface = None def fill_related_attributes(self): """Filling feature values based on related features attributes.""" super().fill_related_attributes() connection_node_handler = self.layer_manager.model_handlers[dm.ConnectionNode] - surface_handler = self.layer_manager.model_handlers[self.surface_model] + surface_handler = self.layer_manager.model_handlers[self.dwf_model] connection_node_layer = connection_node_handler.layer surface_layer = surface_handler.layer linestring = self.feature.geometry().asPolyline() @@ -970,7 +1146,7 @@ def fill_related_attributes(self): if self.surface is None: self.surface = find_point_polygons(start_point, surface_layer) if self.surface is not None: - self.feature[self.surface_id_field] = self.surface["id"] + self.feature[self.dwf_id_field] = self.surface["id"] def populate_with_extra_widgets(self): """Populate widgets for other layers attributes.""" @@ -981,11 +1157,11 @@ def populate_with_extra_widgets(self): def select_start_surface(self): """Selecting start surface""" - title = f"Select start {self.surface_model.__layername__}" - message = f"{self.surface_model.__layername__}s at location" + title = f"Select start {self.dwf_model.__layername__}" + message = f"{self.dwf_model.__layername__}s at location" linestring = self.feature.geometry().asPolyline() start_point, end_point = linestring[0], linestring[-1] - surface_handler = self.layer_manager.model_handlers[self.surface_model] + surface_handler = self.layer_manager.model_handlers[self.dwf_model] surface_layer = surface_handler.layer surface_feats = find_point_polygons(start_point, surface_layer, allow_multiple=True) surfaces_no = len(surface_feats) @@ -1267,26 +1443,35 @@ def select_start_pump(self): return pump_feat -class DryWeatherFlowMapForm(NodeToSurfaceMapForm, FormWithTags): - """Dry Weather Flow Map user layer edit form logic.""" +class SurfaceMapForm(NodeToSurfaceMapForm, FormWithTags): + """Surface Map user layer edit form logic.""" - MODEL = dm.DryWeatherFlowMap + MODEL = dm.SurfaceMap def __init__(self, *args, **kwargs): super().__init__(*args, *kwargs) - self.surface_model = dm.DryWeatherFlow - self.surface_id_field = "dry_weather_flow_id" + self.surface_model = dm.Surface + self.surface_id_field = "surface_id" -class SurfaceMapForm(NodeToSurfaceMapForm, FormWithTags): - """Surface Map user layer edit form logic.""" +class DryWeatherFlowForm(FormWithTags): + """Dry Weather Flow user layer edit form logic.""" - MODEL = dm.SurfaceMap + MODEL = dm.DryWeatherFlow def __init__(self, *args, **kwargs): super().__init__(*args, *kwargs) - self.surface_model = dm.Surface - self.surface_id_field = "surface_id" + + +class DryWeatherFlowMapForm(NodeToSurfaceMapForm, FormWithTags): + """Dry Weather Flow Map user layer edit form logic.""" + + MODEL = dm.DryWeatherFlowMap + + def __init__(self, *args, **kwargs): + super().__init__(*args, *kwargs) + self.dwf_model = dm.DryWeatherFlow + self.dwf_id_field = "dry_weather_flow_id" class ChannelForm(FormWithStartEndNode, FormWithXSTable, FormWithTags): @@ -1583,6 +1768,96 @@ def populate_with_extra_widgets(self): self.populate_widgets() +class BoundaryCondition1D(FormWithTags, FormWithNode, FormWithTimeseries): + """Boundary Condition 1D user layer edit form logic.""" + + MODEL = dm.BoundaryCondition1D + + def __init__(self, *args, **kwargs): + super().__init__(*args, *kwargs) + + def fill_related_attributes(self): + """Filling feature values based on related features attributes.""" + super().fill_related_attributes() + + def populate_with_extra_widgets(self): + """Populate widgets for other layers attributes.""" + if self.creation is True: + self.setup_connection_node_on_creation() + self.fill_related_attributes() + else: + self.setup_connection_node_on_edit() + # Populate widgets based on features attributes + self.populate_foreign_widgets() + self.populate_widgets() + self.populate_timeseries_table_data() + + +class BoundaryCondition2D(FormWithTags, FormWithTimeseries): + """Boundary Condition 2D user layer edit form logic.""" + + MODEL = dm.BoundaryCondition2D + + def __init__(self, *args, **kwargs): + super().__init__(*args, *kwargs) + + def fill_related_attributes(self): + """Filling feature values based on related features attributes.""" + super().fill_related_attributes() + + def populate_with_extra_widgets(self): + """Populate widgets for other layers attributes.""" + # Populate widgets based on features attributes + self.populate_foreign_widgets() + self.populate_widgets() + self.populate_timeseries_table_data() + + +class Lateral1D(FormWithTags, FormWithNode, FormWithTimeseries): + """Lateral 1D user layer edit form logic.""" + + MODEL = dm.Lateral1D + + def __init__(self, *args, **kwargs): + super().__init__(*args, *kwargs) + + def fill_related_attributes(self): + """Filling feature values based on related features attributes.""" + super().fill_related_attributes() + + def populate_with_extra_widgets(self): + """Populate widgets for other layers attributes.""" + if self.creation is True: + self.setup_connection_node_on_creation() + self.fill_related_attributes() + else: + self.setup_connection_node_on_edit() + # Populate widgets based on features attributes + self.populate_foreign_widgets() + self.populate_widgets() + self.populate_timeseries_table_data() + + +class Lateral2D(FormWithTags, FormWithTimeseries): + """Lateral 2D user layer edit form logic.""" + + MODEL = dm.Lateral2D + + def __init__(self, *args, **kwargs): + super().__init__(*args, *kwargs) + + def fill_related_attributes(self): + """Filling feature values based on related features attributes.""" + super().fill_related_attributes() + + def populate_with_extra_widgets(self): + """Populate widgets for other layers attributes.""" + # Populate widgets based on features attributes + self.populate_foreign_widgets() + self.populate_widgets() + self.populate_timeseries_table_data() + + ALL_FORMS = ( ConnectionNodeForm, PipeForm, @@ -1591,12 +1866,17 @@ def populate_with_extra_widgets(self): OrificeForm, PumpForm, PumpMapForm, + DryWeatherFlowForm, DryWeatherFlowMapForm, SurfaceMapForm, ChannelForm, CrossSectionLocationForm, PotentialBreachForm, ExchangeLineForm, + BoundaryCondition1D, + Lateral1D, + BoundaryCondition2D, + Lateral2D, ) MODEL_FORMS = MappingProxyType({form.MODEL: form for form in ALL_FORMS}) diff --git a/threedi_schematisation_editor/forms/ui/boundary_condition_1d.ui b/threedi_schematisation_editor/forms/ui/boundary_condition_1d.ui new file mode 100644 index 0000000..bc030b8 --- /dev/null +++ b/threedi_schematisation_editor/forms/ui/boundary_condition_1d.ui @@ -0,0 +1,597 @@ + + + Dialog + + + + 0 + 0 + 552 + 557 + + + + Dialog + + + + + + QFrame::NoFrame + + + true + + + + + 0 + 0 + 534 + 510 + + + + + + + + + + Qt::ImhNone + + + 0 + + + + 1D Boundary Condition + + + + + + + + + Qt::ImhNone + + + Timeseries + + + + + + + 0 + 125 + + + + + 8 + + + + Qt::ImhDigitsOnly + + + Qt::ScrollBarAsNeeded + + + QAbstractScrollArea::AdjustToContents + + + QAbstractItemView::SelectRows + + + 23 + + + + + + + Table + + + + + + + 0 + + + + + Delete selected rows + + + Delete + + + + + + + Copy + + + + + + + Paste rows from clipboard + + + Paste + + + + + + + Add new row + + + Add + + + + + + + + + + + + Qt::ImhNone + + + Time units + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + 0 + + + + + Interpolate + + + + + + + + 0 + 0 + + + + + + + Qt::ImhNone + + + false + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + 0 + + + + + Tags + + + + + + + true + + + + + + + + + true + + + + + + Connection node + + + + + + + + + Node storage area [m²] + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + 10 + + + + + + + + + + Node code + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + + + + Node initial water level [m MSL] + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + 10 + + + + + + + + 0 + 0 + + + + + + + Qt::ImhNone + + + QAbstractSpinBox::NoButtons + + + 3 + + + 999999999.000000000000000 + + + true + + + + + + + + 0 + 0 + + + + + + + Qt::ImhNone + + + QAbstractSpinBox::NoButtons + + + 3 + + + -1000.000000000000000 + + + 1000000.000000000000000 + + + true + + + true + + + true + + + + + + + + 0 + 0 + + + + + + + true + + + + + + + + 0 + 0 + + + + + + + true + + + QAbstractSpinBox::NoButtons + + + 999999999 + + + true + + + true + + + false + + + + + + + + + + Connection node ID + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + x + + + + + + + x + + + + + + + + + + true + + + + + + General + + + + + + + + + Display name + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + 10 + + + + + + + + 0 + 0 + + + + + + + true + + + + + + + + + + ID + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + + + + Code + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + + + + true + + + + + + + + + + true + + + QAbstractSpinBox::NoButtons + + + 99999999 + + + true + + + true + + + false + + + + + + + + + + Boundary type + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + 10 + + + + + + + + + + + + + + + + + + + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + scrollArea + tabWidget + id + code + display_name + timeseries_table + timeseries_table_delete + timeseries_table_copy + timeseries_table_paste + timeseries_table_add + + + + diff --git a/threedi_schematisation_editor/forms/ui/boundary_condition_2d.ui b/threedi_schematisation_editor/forms/ui/boundary_condition_2d.ui new file mode 100644 index 0000000..9329f8c --- /dev/null +++ b/threedi_schematisation_editor/forms/ui/boundary_condition_2d.ui @@ -0,0 +1,398 @@ + + + Dialog + + + + 0 + 0 + 552 + 557 + + + + Dialog + + + + + + QFrame::NoFrame + + + true + + + + + 0 + 0 + 534 + 510 + + + + + + + + + + Qt::ImhNone + + + 0 + + + + 2D Boundary Condition + + + + + + 0 + + + + + Tags + + + + + + + true + + + + + + + + + true + + + + + + General + + + + + + + + + Display name + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + 10 + + + + + + + + 0 + 0 + + + + + + + true + + + + + + + + + + ID + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + + + + Code + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + + + + true + + + + + + + + + + true + + + QAbstractSpinBox::NoButtons + + + 99999999 + + + true + + + true + + + false + + + + + + + + + + Boundary type + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + 10 + + + + + + + + + + + + + + + + + + + + Qt::ImhNone + + + Timeseries + + + + + + + 0 + 125 + + + + + 8 + + + + Qt::ImhDigitsOnly + + + Qt::ScrollBarAsNeeded + + + QAbstractScrollArea::AdjustToContents + + + QAbstractItemView::SelectRows + + + 23 + + + + + + + Table + + + + + + + 0 + + + + + Delete selected rows + + + Delete + + + + + + + Copy + + + + + + + Paste rows from clipboard + + + Paste + + + + + + + Add new row + + + Add + + + + + + + + + + + + Qt::ImhNone + + + Time units + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + 0 + + + + + Interpolate + + + + + + + + 0 + 0 + + + + + + + Qt::ImhNone + + + false + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + scrollArea + tabWidget + id + code + display_name + timeseries_table + timeseries_table_delete + timeseries_table_copy + timeseries_table_paste + timeseries_table_add + + + + diff --git a/threedi_schematisation_editor/forms/ui/lateral_1d.ui b/threedi_schematisation_editor/forms/ui/lateral_1d.ui new file mode 100644 index 0000000..801a529 --- /dev/null +++ b/threedi_schematisation_editor/forms/ui/lateral_1d.ui @@ -0,0 +1,633 @@ + + + Dialog + + + + 0 + 0 + 552 + 557 + + + + Dialog + + + + + + QFrame::NoFrame + + + true + + + + + 0 + 0 + 534 + 510 + + + + + + + + + + Qt::ImhNone + + + 0 + + + + 1D Lateral + + + + + + 0 + + + + + Tags + + + + + + + true + + + + + + + + + true + + + + + + General + + + + + + + + + true + + + QAbstractSpinBox::NoButtons + + + 99999999 + + + true + + + true + + + false + + + + + + + + + + Code + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + + + + true + + + + + + + + + + ID + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + + + + Display name + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + 10 + + + + + + + + 0 + 0 + + + + + + + true + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + true + + + + + + Connection node + + + + + + + 0 + 0 + + + + + + + Qt::ImhNone + + + QAbstractSpinBox::NoButtons + + + 3 + + + -1000.000000000000000 + + + 1000000.000000000000000 + + + true + + + true + + + true + + + + + + + + 0 + 0 + + + + + + + true + + + + + + + + + + Node initial water level [m MSL] + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + 10 + + + + + + + + + + Node code + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + + + + Node storage area [m²] + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + 10 + + + + + + + + 0 + 0 + + + + + + + true + + + QAbstractSpinBox::NoButtons + + + 999999999 + + + true + + + true + + + false + + + + + + + x + + + + + + + x + + + + + + + + 0 + 0 + + + + + + + Qt::ImhNone + + + QAbstractSpinBox::NoButtons + + + 3 + + + 999999999.000000000000000 + + + true + + + + + + + + + + Connection node ID + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + + + + + + + Qt::ImhNone + + + Timeseries + + + + + + 0 + + + + + Delete selected rows + + + Delete + + + + + + + Copy + + + + + + + Paste rows from clipboard + + + Paste + + + + + + + Add new row + + + Add + + + + + + + + + + 0 + 125 + + + + + 8 + + + + Qt::ImhDigitsOnly + + + Qt::ScrollBarAsNeeded + + + QAbstractScrollArea::AdjustToContents + + + QAbstractItemView::SelectRows + + + 23 + + + + + + + 0 + + + + + Interpolate + + + + + + + + 0 + 0 + + + + + + + Qt::ImhNone + + + false + + + + + + + + + Table + + + + + + + + + + Qt::ImhNone + + + Time units + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + Offset + + + + + + + 0 + + + + + + 0 + 0 + + + + QAbstractSpinBox::NoButtons + + + 999999999 + + + + + + + + + + Units + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + 10 + + + + + + + + 0 + 0 + + + + + + + + + + + + + + + + + + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + scrollArea + tabWidget + id + code + display_name + timeseries_table + timeseries_table_delete + timeseries_table_copy + timeseries_table_paste + timeseries_table_add + + + + diff --git a/threedi_schematisation_editor/forms/ui/lateral_2d.ui b/threedi_schematisation_editor/forms/ui/lateral_2d.ui new file mode 100644 index 0000000..f36bfcd --- /dev/null +++ b/threedi_schematisation_editor/forms/ui/lateral_2d.ui @@ -0,0 +1,434 @@ + + + Dialog + + + + 0 + 0 + 552 + 557 + + + + Dialog + + + + + + QFrame::NoFrame + + + true + + + + + 0 + 0 + 534 + 510 + + + + + + + + + + Qt::ImhNone + + + 0 + + + + 2D Lateral + + + + + + 0 + + + + + Tags + + + + + + + true + + + + + + + + + true + + + + + + General + + + + + + + + + true + + + QAbstractSpinBox::NoButtons + + + 99999999 + + + true + + + true + + + false + + + + + + + + + + Code + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + + + + true + + + + + + + + + + ID + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + + + + Display name + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + 10 + + + + + + + + 0 + 0 + + + + + + + true + + + + + + + + + + + + + Qt::ImhNone + + + Timeseries + + + + + + 0 + + + + + Delete selected rows + + + Delete + + + + + + + Copy + + + + + + + Paste rows from clipboard + + + Paste + + + + + + + Add new row + + + Add + + + + + + + + + + 0 + 125 + + + + + 8 + + + + Qt::ImhDigitsOnly + + + Qt::ScrollBarAsNeeded + + + QAbstractScrollArea::AdjustToContents + + + QAbstractItemView::SelectRows + + + 23 + + + + + + + 0 + + + + + Interpolate + + + + + + + + 0 + 0 + + + + + + + Qt::ImhNone + + + false + + + + + + + + + Table + + + + + + + Offset + + + + + + + + + + Qt::ImhNone + + + Time units + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + 0 + + + + + + 0 + 0 + + + + QAbstractSpinBox::NoButtons + + + 999999999 + + + + + + + + + + Units + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + 10 + + + + + + + + 0 + 0 + + + + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + scrollArea + tabWidget + id + code + display_name + timeseries_table + timeseries_table_delete + timeseries_table_copy + timeseries_table_paste + timeseries_table_add + + + + diff --git a/threedi_schematisation_editor/styles/vector/boundary_condition_1d/default.qml b/threedi_schematisation_editor/styles/vector/boundary_condition_1d/default.qml index a1ed643..6c340fa 100644 --- a/threedi_schematisation_editor/styles/vector/boundary_condition_1d/default.qml +++ b/threedi_schematisation_editor/styles/vector/boundary_condition_1d/default.qml @@ -1,38 +1,181 @@ - + 1 0 1 + 0 - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - - - + 0 0 1 - - - + + + + - - - - - - - - - - - - - - - - - - - - - - - + + + + + + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + - + + + + + + + + + + + + + + + + - - + + + + + + - - - - - + + + + + + + + + - - - - - + + + + + + + + + - + - @@ -216,56 +498,83 @@ - - - 0 + C:\Users/lukas/AppData/Roaming/QGIS/QGIS3\profiles\default/python/plugins\threedi_schematisation_editor\forms\ui\boundary_condition_1d.ui + open_edit_form + 2 - + 0 - tablayout + uifilelayout - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + - + + + + + + + + + + + + + + + + + ROWID - + 0 diff --git a/threedi_schematisation_editor/styles/vector/boundary_condition_2d/default.qml b/threedi_schematisation_editor/styles/vector/boundary_condition_2d/default.qml index 88de3cc..cbb135a 100644 --- a/threedi_schematisation_editor/styles/vector/boundary_condition_2d/default.qml +++ b/threedi_schematisation_editor/styles/vector/boundary_condition_2d/default.qml @@ -1,41 +1,189 @@ - + 1 0 1 + 0 - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - - - + 0 0 1 - - - + + + + - - - - - - - - - - - - - - - - - - - - - - - + + + + + + - + - + - + + + + + + + + + + + + + + + + + + + - - - + + + - + + + + + + + + + + + + + + - - + + + + + - - - - - + + + + + + + + - - - - - + + + + + + + + - + - @@ -219,54 +489,78 @@ - - - 0 + C:\Users/lukas/AppData/Roaming/QGIS/QGIS3\profiles\default/python/plugins\threedi_schematisation_editor\forms\ui\boundary_condition_2d.ui + open_edit_form + 2 - + 0 - tablayout + uifilelayout - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + - + + + + + + + + + + + + + + + "display_name" - + 1 diff --git a/threedi_schematisation_editor/styles/vector/lateral_1d/default.qml b/threedi_schematisation_editor/styles/vector/lateral_1d/default.qml index a33ebdb..c8d1867 100644 --- a/threedi_schematisation_editor/styles/vector/lateral_1d/default.qml +++ b/threedi_schematisation_editor/styles/vector/lateral_1d/default.qml @@ -1,38 +1,181 @@ - + 1 0 1 + 0 - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - - - + 0 0 1 - - - + + + + - - - - - - - - - - - - - - - - - - - - - - - + + + + + + - + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + - - - - + + + + + + + + + + - - - - + + + + + + + + + + - + - @@ -189,53 +496,81 @@ - . - - 0 + C:\Users/lukas/AppData/Roaming/QGIS/QGIS3\profiles\default/python/plugins\threedi_schematisation_editor\forms\ui\lateral_1d.ui + open_edit_form + 2 . - + 0 - tablayout + uifilelayout - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ROWID - + 0 diff --git a/threedi_schematisation_editor/styles/vector/lateral_2d/default.qml b/threedi_schematisation_editor/styles/vector/lateral_2d/default.qml index 7ad09f7..dcbd613 100644 --- a/threedi_schematisation_editor/styles/vector/lateral_2d/default.qml +++ b/threedi_schematisation_editor/styles/vector/lateral_2d/default.qml @@ -1,38 +1,181 @@ - + 1 0 1 + 0 - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - - - + 0 0 1 - - - + + + + - - - - - - - - - - - - - - - - - - - - - - - + + + + + + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + - - - - + + + + + + + + + + - - - - + + + + + + + + + + - + - @@ -192,51 +481,79 @@ - - - 0 + C:\Users/lukas/AppData/Roaming/QGIS/QGIS3\profiles\default/python/plugins\threedi_schematisation_editor\forms\ui\lateral_2d.ui + open_edit_form + 2 - + 0 - tablayout + uifilelayout - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + "id" - + 0 diff --git a/threedi_schematisation_editor/styles/vector/surface/default.qml b/threedi_schematisation_editor/styles/vector/surface/default.qml index 34706db..1ab56c0 100644 --- a/threedi_schematisation_editor/styles/vector/surface/default.qml +++ b/threedi_schematisation_editor/styles/vector/surface/default.qml @@ -1,178 +1,178 @@ - + 1 0 1 0 - + - + - + - + - + - + - + - + - + - + - + @@ -184,33 +184,33 @@ - + - + @@ -220,9 +220,9 @@ - @@ -230,54 +230,54 @@ 0 0 1 - - - - + + + + - + - + @@ -285,139 +285,150 @@ - + - + - + - + - + - - + + - + - + - + - - - + + + - - + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - + - @@ -449,88 +460,83 @@ def my_form_open(dialog, layer, feature): 0 tablayout - - + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + + + + + + - - - - - - - - - - - + - - - - - - - - - - - + + + + + + + + + + + - - - - - - - - - - - + + + + + + + + + + + - - - - - - + + + + + + diff --git a/threedi_schematisation_editor/user_layer_handlers.py b/threedi_schematisation_editor/user_layer_handlers.py index 251ae50..4f7153f 100644 --- a/threedi_schematisation_editor/user_layer_handlers.py +++ b/threedi_schematisation_editor/user_layer_handlers.py @@ -398,10 +398,20 @@ class ConnectionNodeHandler(UserLayerHandler): class BoundaryCondition1DHandler(UserLayerHandler): MODEL = dm.BoundaryCondition1D + RELATED_MODELS = MappingProxyType( + { + dm.ConnectionNode: 1, + } + ) class Lateral1DHandler(UserLayerHandler): MODEL = dm.Lateral1D + RELATED_MODELS = MappingProxyType( + { + dm.ConnectionNode: 1, + } + ) class PumpHandler(UserLayerHandler): @@ -944,6 +954,32 @@ class ExchangeLineHandler(UserLayerHandler): ) +class SurfaceHandler(UserLayerHandler): + MODEL = dm.Surface + + def connect_additional_signals(self): + """Connecting signals to action specific for the particular layers.""" + self.layer.geometryChanged.connect(self.update_surface_link) + + def disconnect_additional_signals(self): + """Disconnecting signals to action specific for the particular layers.""" + self.layer.geometryChanged.disconnect(self.update_surface_link) + + def update_surface_link(self, feat_id, geometry): + """Update geometry of the surface - node link.""" + surface_handler = self.layer_manager.model_handlers[dm.Surface] + surface_layer = surface_handler.layer + surface_link_handler = self.layer_manager.model_handlers[dm.SurfaceMap] + surface_link_layer = surface_link_handler.layer + surface_feat = surface_layer.getFeature(feat_id) + link_feat = surface_link_handler.get_feat_by_id(surface_feat["id"], "surface_id") + point = geometry.centroid().asPoint() + link_linestring = link_feat.geometry().asPolyline() + link_linestring[0] = point + link_new_geom = QgsGeometry.fromPolylineXY(link_linestring) + surface_link_layer.changeGeometry(link_feat.id(), link_new_geom) + + class DryWeatherFlowDistributionHandler(UserLayerHandler): MODEL = dm.DryWeatherFlowDistribution @@ -965,8 +1001,8 @@ def update_dwf_link(self, feat_id, geometry): dwf_layer = dwf_handler.layer dwf_link_handler = self.layer_manager.model_handlers[dm.DryWeatherFlowMap] dwf_link_layer = dwf_link_handler.layer - surface_feat = dwf_layer.getFeature(feat_id) - link_feat = dwf_link_handler.get_feat_by_id(surface_feat["id"], "dry_weather_flow_id") + dwf_feat = dwf_layer.getFeature(feat_id) + link_feat = dwf_link_handler.get_feat_by_id(dwf_feat["id"], "dry_weather_flow_id") point = geometry.centroid().asPoint() link_linestring = link_feat.geometry().asPolyline() link_linestring[0] = point @@ -974,32 +1010,6 @@ def update_dwf_link(self, feat_id, geometry): dwf_link_layer.changeGeometry(link_feat.id(), link_new_geom) -class SurfaceHandler(UserLayerHandler): - MODEL = dm.Surface - - def connect_additional_signals(self): - """Connecting signals to action specific for the particular layers.""" - self.layer.geometryChanged.connect(self.update_surface_link) - - def disconnect_additional_signals(self): - """Disconnecting signals to action specific for the particular layers.""" - self.layer.geometryChanged.disconnect(self.update_surface_link) - - def update_surface_link(self, feat_id, geometry): - """Update geometry of the surface - node link.""" - surface_handler = self.layer_manager.model_handlers[dm.Surface] - surface_layer = surface_handler.layer - surface_link_handler = self.layer_manager.model_handlers[dm.SurfaceMap] - surface_link_layer = surface_link_handler.layer - surface_feat = surface_layer.getFeature(feat_id) - link_feat = surface_link_handler.get_feat_by_id(surface_feat["id"], "surface_id") - point = geometry.centroid().asPoint() - link_linestring = link_feat.geometry().asPolyline() - link_linestring[0] = point - link_new_geom = QgsGeometry.fromPolylineXY(link_linestring) - surface_link_layer.changeGeometry(link_feat.id(), link_new_geom) - - class DryWeatherFlowMapHandler(UserLayerHandler): MODEL = dm.DryWeatherFlowMap DEFAULTS = MappingProxyType(