Skip to content

Commit 1c7aae4

Browse files
committed
Improved lifecycle UI
1 parent 941cd27 commit 1c7aae4

File tree

5 files changed

+161
-159
lines changed

5 files changed

+161
-159
lines changed

arho_feature_template/core/models.py

+2
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ def from_config_file(cls, config_fp: Path) -> FeatureTemplateLibrary:
6363
name=feature_data.get("name"),
6464
description=feature_data.get("description"),
6565
regulation_groups=[], # feature_data.get("regulation_groups"), # Handle regulation group creaton
66+
lifecycles=[],
6667
plan_id=None,
6768
id_=None,
6869
)
@@ -348,6 +349,7 @@ class PlanFeature:
348349
name: str | None = None
349350
description: str | None = None
350351
regulation_groups: list[RegulationGroup] = field(default_factory=list)
352+
lifecycles: list[LifeCycle] = field(default_factory=list)
351353
plan_id: int | None = None
352354
id_: int | None = None
353355

arho_feature_template/core/plan_manager.py

+3-6
Original file line numberDiff line numberDiff line change
@@ -177,14 +177,12 @@ def edit_plan(self):
177177
active_plan_id = QgsExpressionContextUtils.projectScope(QgsProject.instance()).variable("active_plan_id")
178178
feature = PlanLayer.get_feature_by_id(active_plan_id, no_geometries=False)
179179
if feature is None:
180-
iface.messageBar().pushWarning("", "No active/open plan found!")
181180
return
182181
plan_model = PlanLayer.model_from_feature(feature)
183182

184183
attribute_form = PlanAttributeForm(plan_model, self.get_regulation_group_libraries())
185184
if attribute_form.exec_():
186185
feature = save_plan(attribute_form.model)
187-
# lifecycle_features = save_lifecycle(attribute_form.lifecycle_model)
188186

189187
def add_new_plan_feature(self):
190188
if not handle_unsaved_changes():
@@ -244,16 +242,16 @@ def _plan_feature_geom_digitized(self, feature: QgsFeature):
244242
)
245243
if attribute_form.exec_():
246244
save_plan_feature(attribute_form.model)
247-
# save_lifecycle(attribute_form.lifecycle_model)
248245

249246
def edit_plan_feature(self, feature: QgsFeature, layer_name: str):
250247
layer_class = FEATURE_LAYER_NAME_TO_CLASS_MAP[layer_name]
251248
plan_feature = layer_class.model_from_feature(feature)
252249

253-
# Geom editing handled with basic QGIS vertex editing?
254250
title = plan_feature.name if plan_feature.name else layer_name
255251
attribute_form = PlanFeatureForm(
256-
plan_feature, title, [*self.get_regulation_group_libraries(), regulation_group_library_from_active_plan()]
252+
plan_feature,
253+
title,
254+
[*self.get_regulation_group_libraries(), regulation_group_library_from_active_plan()],
257255
)
258256
if attribute_form.exec_():
259257
save_plan_feature(attribute_form.model)
@@ -565,7 +563,6 @@ def save_regulation(regulation: Regulation) -> QgsFeature:
565563

566564

567565
def save_lifecycle(lifecycle: LifeCycle) -> QgsFeature:
568-
"""Save a list of LifeCycle objects to the layer."""
569566
feature = LifeCycleLayer.feature_from_model(lifecycle)
570567
layer = LifeCycleLayer.get_from_project()
571568

arho_feature_template/gui/dialogs/plan_attribute_form.py

+123-85
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,22 @@
33
from importlib import resources
44
from typing import TYPE_CHECKING
55

6+
from qgis.gui import QgsDateTimeEdit
67
from qgis.PyQt import uic
7-
from qgis.PyQt.QtCore import QDate, Qt
8-
from qgis.PyQt.QtGui import QStandardItem, QStandardItemModel
8+
from qgis.PyQt.QtCore import Qt
9+
from qgis.PyQt.QtGui import QStandardItem
910
from qgis.PyQt.QtWidgets import (
1011
QComboBox,
1112
QDateEdit,
1213
QDialog,
1314
QDialogButtonBox,
15+
QHeaderView,
1416
QLineEdit,
1517
QPushButton,
1618
QSizePolicy,
1719
QSpacerItem,
18-
QStyledItemDelegate,
1920
QTableWidget,
21+
QTableWidgetItem,
2022
QTextEdit,
2123
QTreeWidgetItem,
2224
QWidget,
@@ -41,50 +43,6 @@
4143
FormClass, _ = uic.loadUiType(ui_path)
4244

4345

44-
class LifecycleTableModel(QStandardItemModel):
45-
def __init__(self, status_options, parent=None):
46-
super().__init__(parent)
47-
self.status_options = status_options
48-
49-
def flags(self, index):
50-
if index.column() == 0: # "Elinkaaren tila" - editable combo box
51-
return Qt.ItemIsSelectable | Qt.ItemIsEnabled
52-
53-
if index.column() in (1, 2): # Dates
54-
return Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsEditable
55-
return super().flags(index)
56-
57-
58-
class LifecycleDelegate(QStyledItemDelegate):
59-
def create_editor(self, parent, option, index):
60-
if index.column() == 0: # Status column
61-
lifecycle_combo_box = CodeComboBox(parent)
62-
lifecycle_combo_box.populate_from_code_layer(LifeCycleStatusLayer)
63-
return lifecycle_combo_box
64-
if index.column() in (1, 2): # Dates columns
65-
date_edit = QDateEdit(parent)
66-
date_edit.setDisplayFormat("yyyy-MM-dd")
67-
date_edit.setCalendarPopup(True)
68-
return date_edit
69-
return super().createEditor(parent, option, index)
70-
71-
def set_editor_data(self, editor, index):
72-
if isinstance(editor, CodeComboBox) and index.column() == 0:
73-
value = index.data(Qt.EditRole)
74-
if value is not None:
75-
editor.set_value(value)
76-
elif isinstance(editor, QDateEdit) and index.column() in (1, 2):
77-
value = index.data(Qt.EditRole)
78-
if value:
79-
editor.setDate(QDate.fromString(value, "yyyy-MM-dd"))
80-
81-
def set_model_data(self, editor, model, index):
82-
if isinstance(editor, CodeComboBox) and index.column() == 0:
83-
model.setData(index, editor.value(), Qt.EditRole)
84-
if isinstance(editor, QDateEdit) and index.column() in (1, 2):
85-
model.setData(index, editor.date().toString("yyyy-MM-dd"), Qt.EditRole)
86-
87-
8846
class PlanAttributeForm(QDialog, FormClass): # type: ignore
8947
permanent_identifier_line_edit: QLineEdit
9048
name_line_edit: QLineEdit
@@ -102,16 +60,22 @@ class PlanAttributeForm(QDialog, FormClass): # type: ignore
10260

10361
lifecycle_table: QTableWidget
10462
add_lifecycle: QPushButton
105-
delete_lifecycle: QPushButton
63+
# delete_lifecycle: QPushButton
10664

10765
button_box: QDialogButtonBox
10866

109-
def __init__(self, plan: Plan, regulation_group_libraries: list[RegulationGroupLibrary], parent=None):
67+
def __init__(
68+
self,
69+
plan: Plan,
70+
regulation_group_libraries: list[RegulationGroupLibrary],
71+
parent=None,
72+
):
11073
super().__init__(parent)
11174

11275
self.setupUi(self)
11376

11477
self.plan = plan
78+
self.lifecycle_models = plan.lifecycles
11579

11680
self.plan_type_combo_box.populate_from_code_layer(PlanTypeLayer)
11781
self.lifecycle_status_combo_box.populate_from_code_layer(LifeCycleStatusLayer)
@@ -151,12 +115,22 @@ def __init__(self, plan: Plan, regulation_group_libraries: list[RegulationGroupL
151115
self.add_plan_regulation_group(regulation_group)
152116

153117
# Lifecycle table setup
154-
self.lifecycle_table.setColumnCount(3) # Three columns: Status, Start Date, End Date
155-
self.lifecycle_table.setHorizontalHeaderLabels(["Elinkaaren tila", "Alkupvm", "Loppupvm"])
156-
self.lifecycle_table.setRowCount(0) # No rows initially
118+
self.lifecycle_table.setColumnCount(4)
119+
self.lifecycle_table.setHorizontalHeaderLabels(["Elinkaaren tila", "Alkupvm", "Loppupvm", "id"])
120+
# self.lifecycle_table.setColumnHidden(3, True)
121+
self.lifecycle_table.setRowCount(0)
122+
123+
header = self.lifecycle_table.horizontalHeader()
124+
header.setSectionResizeMode(0, QHeaderView.Stretch)
125+
header.setSectionResizeMode(1, QHeaderView.ResizeToContents)
126+
header.setSectionResizeMode(2, QHeaderView.ResizeToContents)
127+
128+
self.populate_lifecycle_table()
129+
# for lifecycle in plan.lifecycles:
130+
# self.add_lifecycle_row(lifecycle)
157131

158132
self.add_lifecycle_button.clicked.connect(self.add_lifecycle_row)
159-
self.delete_lifecycle_button.clicked.connect(self.delete_lifecycle_row)
133+
# self.delete_lifecycle_button.clicked.connect(self.delete_lifecycle_row)
160134

161135
self.button_box.button(QDialogButtonBox.Ok).setEnabled(False)
162136
self.button_box.accepted.connect(self._on_ok_clicked)
@@ -166,7 +140,6 @@ def __init__(self, plan: Plan, regulation_group_libraries: list[RegulationGroupL
166140
def _check_required_fields(self) -> None:
167141
ok_button = self.button_box.button(QDialogButtonBox.Ok)
168142

169-
# Check if all required fields are filled and lifecycle table has at least one valid row
170143
has_valid_lifecycle_row = False
171144
for row in range(self.lifecycle_table.rowCount()):
172145
status_item = self.lifecycle_table.cellWidget(row, 0)
@@ -176,7 +149,7 @@ def _check_required_fields(self) -> None:
176149
if (
177150
status_item
178151
and status_item.value() is not None
179-
and start_date_item
152+
# and start_date_item
180153
and start_date_item.date()
181154
and (end_date_item and end_date_item.date() or True)
182155
):
@@ -235,56 +208,107 @@ def init_plan_regulation_group_library(self, library: RegulationGroupLibrary):
235208

236209
# ---
237210

211+
def populate_lifecycle_table(self):
212+
# kutsu add_lifecycle_row
213+
self.lifecycle_table.setRowCount(len(self.lifecycle_models))
214+
215+
for row, lifecycle in enumerate(self.lifecycle_models):
216+
# Populate the status combobox
217+
status_combobox = CodeComboBox(self)
218+
status_combobox.populate_from_code_layer(LifeCycleStatusLayer)
219+
status_combobox.set_value(lifecycle.status_id)
220+
self.lifecycle_table.setCellWidget(row, 0, status_combobox)
221+
222+
# Populate the start date edit field
223+
start_date_edit = QDateEdit(self)
224+
start_date_edit.setDisplayFormat("yyyy-MM-dd")
225+
start_date_edit.setCalendarPopup(True)
226+
start_date_edit.setDate(lifecycle.starting_at.date())
227+
self.lifecycle_table.setCellWidget(row, 1, start_date_edit)
228+
229+
# Populate the end date edit field
230+
end_date_edit = QDateEdit(self)
231+
end_date_edit.setDisplayFormat("yyyy-MM-dd")
232+
end_date_edit.setCalendarPopup(True)
233+
if lifecycle.ending_at:
234+
end_date_edit.setDate(lifecycle.ending_at.date())
235+
self.lifecycle_table.setCellWidget(row, 2, end_date_edit)
236+
237+
# Populate the ID column
238+
id_item = QTableWidgetItem(str(lifecycle.id_) if lifecycle.id_ else "")
239+
id_item.setData(Qt.UserRole, lifecycle.id_)
240+
self.lifecycle_table.setItem(row, 3, id_item)
241+
238242
def add_lifecycle_row(self):
239243
row_position = self.lifecycle_table.rowCount()
240244
self.lifecycle_table.insertRow(row_position)
241245

242-
status = CodeComboBox(self)
246+
status = CodeComboBox()
243247
status.populate_from_code_layer(LifeCycleStatusLayer)
244248
self.lifecycle_table.setCellWidget(row_position, 0, status)
245249

246-
start_date_edit = QDateEdit(self)
250+
start_date_edit = QgsDateTimeEdit()
247251
start_date_edit.setDisplayFormat("yyyy-MM-dd")
248252
start_date_edit.setCalendarPopup(True)
249253
self.lifecycle_table.setCellWidget(row_position, 1, start_date_edit)
250254

251-
end_date_edit = QDateEdit(self)
255+
end_date_edit = QgsDateTimeEdit()
252256
end_date_edit.setDisplayFormat("yyyy-MM-dd")
253257
end_date_edit.setCalendarPopup(True)
258+
end_date_edit.clear()
254259
self.lifecycle_table.setCellWidget(row_position, 2, end_date_edit)
255260

256-
self.lifecycle_table.resizeRowsToContents()
257-
self.lifecycle_table.resizeColumnsToContents()
258-
259-
def delete_lifecycle_row(self):
260-
selected_rows = self.lifecycle_table.selectionModel().selectedRows()
261+
""" def add_lifecycle_row(self, lifecycle: LifeCycle | None):
262+
# Add a new row at the end of the table
263+
row_position = self.lifecycle_table.rowCount()
264+
self.lifecycle_table.insertRow(row_position)
261265
262-
if selected_rows:
263-
row_position = selected_rows[0].row()
264-
self.lifecycle_table.removeRow(row_position)
265-
self._check_required_fields()
266+
# Status combobox
267+
status_combobox = CodeComboBox()
268+
status_combobox.populate_from_code_layer(LifeCycleStatusLayer)
269+
self.lifecycle_table.setCellWidget(row_position, 0, status_combobox)
266270
267-
def save_lifecycle(self):
268-
for row in range(self.lifecycle_table.rowCount()):
269-
status = self.lifecycle_table.cellWidget(row, 0)
270-
start_date_item = self.lifecycle_table.cellWidget(row, 1)
271-
end_date_item = self.lifecycle_table.cellWidget(row, 2)
271+
# Start date field
272+
start_date_edit = QgsDateTimeEdit()
273+
start_date_edit.setDisplayFormat("yyyy-MM-dd")
274+
start_date_edit.setCalendarPopup(True)
275+
self.lifecycle_table.setCellWidget(row_position, 1, start_date_edit)
272276
273-
if status and start_date_item:
274-
status = status.value() if status.value() is not None else ""
275-
start_date = start_date_item.date().toString("yyyy-MM-dd") if start_date_item.date() else ""
276-
end_date = end_date_item.date().toString("yyyy-MM-dd") if end_date_item.date() else None
277+
# End date field
278+
end_date_edit = QgsDateTimeEdit()
279+
end_date_edit.setDisplayFormat("yyyy-MM-dd")
280+
end_date_edit.setCalendarPopup(True)
281+
self.lifecycle_table.setCellWidget(row_position, 2, end_date_edit)
277282
278-
lifecycle_model_item = QStandardItem(status)
279-
lifecycle_model_item.setData(status.value(), Qt.UserRole + 1)
280-
start_date_model_item = QStandardItem(start_date)
281-
end_date_model_item = QStandardItem(end_date if end_date else "")
283+
# ID field
284+
id_item = QTableWidgetItem()
285+
# If lifecycle exists and has an ID, set it
286+
if lifecycle and lifecycle.id_:
287+
id_item.setData(Qt.UserRole, lifecycle.id_)
288+
id_item.setText(str(lifecycle.id_))
289+
self.lifecycle_table.setItem(row_position, 3, id_item)
290+
291+
# If lifecycle is provided, populate the fields
292+
if lifecycle:
293+
# For existing lifecycle, populate its data
294+
status_combobox.set_value(lifecycle.status_id)
295+
if lifecycle.starting_at:
296+
start_date_edit.setDate(lifecycle.starting_at.date())
297+
if lifecycle.ending_at:
298+
end_date_edit.setDate(lifecycle.ending_at.date())
299+
else:
300+
# For a new lifecycle, leave the fields empty
301+
status_combobox.set_value(None) # Do not set a default value
302+
start_date_edit.clear() # No date set initially
303+
end_date_edit.clear() # No date set initially """
282304

283-
self.lifecycle_table.model().appendRow(
284-
[lifecycle_model_item, start_date_model_item, end_date_model_item]
285-
)
305+
# def delete_lifecycle_row(self):
306+
# selected_rows = self.lifecycle_table.selectionModel().selectedRows()
286307

287-
self._check_required_fields()
308+
# if selected_rows:
309+
# row_position = selected_rows[0].row()
310+
# self.lifecycle_table.removeRow(row_position)
311+
# self._check_required_fields()
288312

289313
def into_lifecycle_model(self) -> list[LifeCycle]:
290314
lifecycles = []
@@ -294,13 +318,27 @@ def into_lifecycle_model(self) -> list[LifeCycle]:
294318
status_item = self.lifecycle_table.cellWidget(row, 0)
295319
start_date_item = self.lifecycle_table.cellWidget(row, 1)
296320
end_date_item = self.lifecycle_table.cellWidget(row, 2)
321+
id_item = self.lifecycle_table.item(row, 3)
297322

323+
# Check if status_item and start_date_item exist, then create the LifeCycle
298324
if status_item and start_date_item:
299325
status_id = status_item.value()
300326
start_date = start_date_item.date().toString("yyyy-MM-dd") if start_date_item.date() else ""
301327
end_date = end_date_item.date().toString("yyyy-MM-dd") if end_date_item.date() else None
302328

303-
lifecycles.append(LifeCycle(status_id=status_id, starting_at=start_date, ending_at=end_date))
329+
lifecycle_id = None
330+
if id_item and id_item.text().strip(): # Check if the ID in the 4th column is not empty
331+
lifecycle_id = id_item.data(Qt.UserRole)
332+
333+
# Create the LifeCycle model and add it to the list
334+
lifecycles.append(
335+
LifeCycle(
336+
status_id=status_id,
337+
starting_at=start_date,
338+
ending_at=end_date,
339+
id_=lifecycle_id, # Set the ID only if not None
340+
)
341+
)
304342

305343
return lifecycles
306344

0 commit comments

Comments
 (0)