Skip to content

Commit d6756f4

Browse files
committed
add plan propositions
- add plan proposition model - add widget for plan proposition - handle saving and loading plan propositions for plan regulation groups - add possibility to define plan propositions for custom plan regulation groups - add PlanThemeLayer code layer model - remove spacer and horizontal line layout items from plan regulation widget
1 parent 3992fcb commit d6756f4

11 files changed

+472
-84
lines changed

arho_feature_template/core/models.py

+12
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,16 @@ class Regulation:
265265
# value_number_pair: tuple[Number, Number] | None
266266

267267

268+
@dataclass
269+
class Proposition:
270+
name: str
271+
value: str
272+
theme_id: str | None = None
273+
proposition_number: int | None = None
274+
regulation_group_id_: int | None = None
275+
id_: int | None = None
276+
277+
268278
@dataclass
269279
class RegulationGroup:
270280
type_code_id: str | None
@@ -273,6 +283,7 @@ class RegulationGroup:
273283
color_code: str | None
274284
group_number: int | None = None
275285
regulations: list[Regulation] = field(default_factory=list)
286+
propositions: list[Proposition] = field(default_factory=list)
276287
id_: int | None = None
277288

278289
@classmethod
@@ -308,6 +319,7 @@ def from_config_data(cls, data: dict) -> RegulationGroup:
308319
color_code=data.get("color_code"),
309320
group_number=data.get("group_number"),
310321
regulations=regulations,
322+
propositions=[],
311323
id_=None,
312324
)
313325

arho_feature_template/core/plan_manager.py

+22-1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
OtherPointLayer,
3434
PlanFeatureLayer,
3535
PlanLayer,
36+
PlanPropositionLayer,
3637
PlanRegulationLayer,
3738
RegulationGroupAssociationLayer,
3839
RegulationGroupLayer,
@@ -51,7 +52,7 @@
5152
if TYPE_CHECKING:
5253
from qgis.core import QgsFeature
5354

54-
from arho_feature_template.core.models import Regulation, RegulationGroup
55+
from arho_feature_template.core.models import Proposition, Regulation, RegulationGroup
5556

5657
logger = logging.getLogger(__name__)
5758

@@ -485,6 +486,12 @@ def save_regulation_group(regulation_group: RegulationGroup, plan_id: str | None
485486
regulation.regulation_group_id_ = feature["id"] # Updating regulation group ID
486487
save_regulation(regulation)
487488

489+
# Handle propositions
490+
if regulation_group.propositions:
491+
for proposition in regulation_group.propositions:
492+
proposition.regulation_group_id_ = feature["id"] # Updating regulation group ID
493+
save_proposition(proposition)
494+
488495
return feature
489496

490497

@@ -513,3 +520,17 @@ def save_regulation(regulation: Regulation) -> QgsFeature:
513520
)
514521

515522
return feature
523+
524+
525+
def save_proposition(proposition: Proposition) -> QgsFeature:
526+
feature = PlanPropositionLayer.feature_from_model(proposition)
527+
layer = PlanPropositionLayer.get_from_project()
528+
529+
_save_feature(
530+
feature=feature,
531+
layer=layer,
532+
id_=proposition.id_,
533+
edit_text="Kaavasuosituksen lisäys" if proposition.id_ is None else "Kaavasuosituksen muokkaus",
534+
)
535+
536+
return feature
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
from __future__ import annotations
2+
3+
from importlib import resources
4+
from typing import TYPE_CHECKING
5+
6+
from qgis.core import QgsApplication
7+
from qgis.PyQt import uic
8+
from qgis.PyQt.QtCore import Qt, pyqtSignal
9+
from qgis.PyQt.QtWidgets import QFormLayout, QLabel, QMenu, QTextEdit, QToolButton, QWidget
10+
11+
from arho_feature_template.core.models import Proposition
12+
from arho_feature_template.gui.components.code_combobox import CodeComboBox
13+
from arho_feature_template.gui.components.plan_regulation_input_widgets import IntegerInputWidget
14+
from arho_feature_template.project.layers.code_layers import PlanThemeLayer
15+
16+
if TYPE_CHECKING:
17+
from qgis.PyQt.QtWidgets import QLineEdit, QPushButton
18+
19+
ui_path = resources.files(__package__) / "plan_proposition_widget.ui"
20+
FormClass, _ = uic.loadUiType(ui_path)
21+
22+
# TO BE REPLACED
23+
LANGUAGE = "fin"
24+
25+
26+
class PropositionWidget(QWidget, FormClass): # type: ignore
27+
"""A widget representation of a plan proposition."""
28+
29+
delete_signal = pyqtSignal(QWidget)
30+
31+
def __init__(self, proposition: Proposition, parent=None):
32+
super().__init__(parent)
33+
self.setupUi(self)
34+
35+
# TYPES
36+
self.name: QLineEdit
37+
self.value_label: QLabel
38+
self.text_input: QTextEdit
39+
self.add_field_btn: QPushButton
40+
self.del_btn: QPushButton
41+
self.form_layout: QFormLayout
42+
self.expand_hide_btn: QToolButton
43+
44+
# INIT
45+
self.proposition = proposition
46+
self.proposition_number_widget: IntegerInputWidget | None = None
47+
self.theme_widget: CodeComboBox | None = None
48+
49+
# List of widgets for hiding / showing
50+
self.widgets: list[tuple[QLabel, QWidget]] = [(self.value_label, self.text_input)]
51+
52+
add_field_menu = QMenu(self)
53+
add_field_menu.addAction("Suositusnumero").triggered.connect(self._add_proposition_number)
54+
add_field_menu.addAction("Kaavoitusteema").triggered.connect(self._add_theme)
55+
self.add_field_btn.setMenu(add_field_menu)
56+
self.add_field_btn.setIcon(QgsApplication.getThemeIcon("mActionAdd.svg"))
57+
58+
self.del_btn.setIcon(QgsApplication.getThemeIcon("mActionDeleteSelected.svg"))
59+
self.del_btn.clicked.connect(lambda: self.delete_signal.emit(self))
60+
61+
self.expanded = True
62+
self.expand_hide_btn.clicked.connect(self._on_expand_hide_btn_clicked)
63+
64+
self.name.setText(proposition.name)
65+
self.text_input.setText(proposition.value)
66+
self._add_theme(proposition.theme_id)
67+
self._add_proposition_number(proposition.proposition_number)
68+
69+
def _add_widgets(self, label: QLabel, widget: QWidget):
70+
self.form_layout.addRow(label, widget)
71+
self.widgets.append((label, widget))
72+
if not self.expanded:
73+
self._on_expand_hide_btn_clicked()
74+
75+
def _add_proposition_number(self, default_value: int | None = None):
76+
if not self.proposition_number_widget:
77+
self.proposition_number_widget = IntegerInputWidget(default_value, None, True)
78+
self._add_widgets(QLabel("Suositusnumero"), self.proposition_number_widget)
79+
80+
def _add_theme(self, default_value: str | None = None):
81+
if not self.theme_widget:
82+
self.theme_widget = CodeComboBox()
83+
self.theme_widget.populate_from_code_layer(PlanThemeLayer)
84+
if default_value:
85+
self.theme_widget.set_value(default_value)
86+
self._add_widgets(QLabel("Kaavoitusteema"), self.theme_widget)
87+
88+
def _on_expand_hide_btn_clicked(self):
89+
if self.expanded:
90+
for label, value_widget in self.widgets:
91+
self.form_layout.removeWidget(label)
92+
label.hide()
93+
self.form_layout.removeWidget(value_widget)
94+
value_widget.hide()
95+
self.expand_hide_btn.setArrowType(Qt.ArrowType.DownArrow)
96+
self.expanded = False
97+
else:
98+
for label, value_widget in self.widgets:
99+
self.form_layout.addRow(label, value_widget)
100+
label.show()
101+
value_widget.show()
102+
self.expand_hide_btn.setArrowType(Qt.ArrowType.UpArrow)
103+
self.expanded = True
104+
105+
def into_model(self) -> Proposition:
106+
return Proposition(
107+
name=self.name.text(),
108+
value=self.text_input.toPlainText(),
109+
theme_id=self.theme_widget.value() if self.theme_widget else None,
110+
proposition_number=self.proposition_number_widget.get_value() if self.proposition_number_widget else None,
111+
id_=self.proposition.id_,
112+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<ui version="4.0">
3+
<class>Form</class>
4+
<widget class="QWidget" name="Form">
5+
<property name="geometry">
6+
<rect>
7+
<x>0</x>
8+
<y>0</y>
9+
<width>441</width>
10+
<height>207</height>
11+
</rect>
12+
</property>
13+
<property name="sizePolicy">
14+
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
15+
<horstretch>0</horstretch>
16+
<verstretch>0</verstretch>
17+
</sizepolicy>
18+
</property>
19+
<property name="baseSize">
20+
<size>
21+
<width>0</width>
22+
<height>160</height>
23+
</size>
24+
</property>
25+
<property name="windowTitle">
26+
<string>Form</string>
27+
</property>
28+
<property name="styleSheet">
29+
<string notr="true"/>
30+
</property>
31+
<layout class="QVBoxLayout" name="verticalLayout">
32+
<item>
33+
<layout class="QHBoxLayout" name="horizontalLayout">
34+
<item>
35+
<widget class="QLabel" name="label_2">
36+
<property name="font">
37+
<font>
38+
<pointsize>11</pointsize>
39+
<weight>75</weight>
40+
<bold>true</bold>
41+
</font>
42+
</property>
43+
<property name="text">
44+
<string>Kaavasuositus</string>
45+
</property>
46+
</widget>
47+
</item>
48+
<item>
49+
<spacer name="horizontalSpacer">
50+
<property name="orientation">
51+
<enum>Qt::Horizontal</enum>
52+
</property>
53+
<property name="sizeHint" stdset="0">
54+
<size>
55+
<width>40</width>
56+
<height>20</height>
57+
</size>
58+
</property>
59+
</spacer>
60+
</item>
61+
<item>
62+
<widget class="QPushButton" name="add_field_btn">
63+
<property name="toolTip">
64+
<string>Muu tieto</string>
65+
</property>
66+
<property name="text">
67+
<string/>
68+
</property>
69+
</widget>
70+
</item>
71+
<item>
72+
<widget class="QPushButton" name="del_btn">
73+
<property name="sizePolicy">
74+
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
75+
<horstretch>0</horstretch>
76+
<verstretch>0</verstretch>
77+
</sizepolicy>
78+
</property>
79+
<property name="maximumSize">
80+
<size>
81+
<width>30</width>
82+
<height>16777215</height>
83+
</size>
84+
</property>
85+
<property name="text">
86+
<string/>
87+
</property>
88+
</widget>
89+
</item>
90+
</layout>
91+
</item>
92+
<item>
93+
<layout class="QFormLayout" name="form_layout">
94+
<item row="0" column="0">
95+
<widget class="QLabel" name="name_label">
96+
<property name="text">
97+
<string>Otsikko</string>
98+
</property>
99+
</widget>
100+
</item>
101+
<item row="0" column="1">
102+
<layout class="QHBoxLayout" name="horizontalLayout_2">
103+
<item>
104+
<widget class="QLineEdit" name="name">
105+
<property name="clearButtonEnabled">
106+
<bool>false</bool>
107+
</property>
108+
</widget>
109+
</item>
110+
<item>
111+
<widget class="QToolButton" name="expand_hide_btn">
112+
<property name="text">
113+
<string>Laajenna</string>
114+
</property>
115+
<property name="autoRaise">
116+
<bool>false</bool>
117+
</property>
118+
<property name="arrowType">
119+
<enum>Qt::UpArrow</enum>
120+
</property>
121+
</widget>
122+
</item>
123+
</layout>
124+
</item>
125+
<item row="1" column="0">
126+
<widget class="QLabel" name="value_label">
127+
<property name="text">
128+
<string>Sisältö</string>
129+
</property>
130+
</widget>
131+
</item>
132+
<item row="1" column="1">
133+
<widget class="QTextEdit" name="text_input">
134+
<property name="sizePolicy">
135+
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
136+
<horstretch>0</horstretch>
137+
<verstretch>0</verstretch>
138+
</sizepolicy>
139+
</property>
140+
<property name="baseSize">
141+
<size>
142+
<width>600</width>
143+
<height>120</height>
144+
</size>
145+
</property>
146+
</widget>
147+
</item>
148+
</layout>
149+
</item>
150+
</layout>
151+
</widget>
152+
<resources/>
153+
<connections/>
154+
</ui>

arho_feature_template/gui/components/plan_regulation_group_widget.py

+17-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
from qgis.PyQt.QtCore import pyqtSignal
99
from qgis.PyQt.QtWidgets import QWidget
1010

11-
from arho_feature_template.core.models import Regulation, RegulationGroup
11+
from arho_feature_template.core.models import Proposition, Regulation, RegulationGroup
12+
from arho_feature_template.gui.components.plan_proposition_widget import PropositionWidget
1213
from arho_feature_template.gui.components.plan_regulation_widget import RegulationWidget
1314
from arho_feature_template.project.layers.code_layers import PlanRegulationGroupTypeLayer
1415

@@ -41,6 +42,9 @@ def __init__(self, regulation_group_data: RegulationGroup, layer_name: str):
4142
self.regulation_widgets: list[RegulationWidget] = [
4243
self.add_regulation_widget(regulation) for regulation in self.regulation_group_data.regulations
4344
]
45+
self.proposition_widgets: list[PropositionWidget] = [
46+
self.add_proposition_widget(proposition) for proposition in self.regulation_group_data.propositions
47+
]
4448

4549
regulation_group_data.type_code_id = PlanRegulationGroupTypeLayer.get_id_by_feature_layer_name(layer_name)
4650
self.name.setText(self.regulation_group_data.name if self.regulation_group_data.name else "")
@@ -59,12 +63,24 @@ def delete_regulation_widget(self, regulation_widget: RegulationWidget):
5963
self.regulation_widgets.remove(regulation_widget)
6064
regulation_widget.deleteLater()
6165

66+
def add_proposition_widget(self, proposition: Proposition) -> PropositionWidget:
67+
widget = PropositionWidget(proposition=proposition, parent=self.frame)
68+
widget.delete_signal.connect(self.delete_proposition_widget)
69+
self.frame.layout().addWidget(widget)
70+
return widget
71+
72+
def delete_proposition_widget(self, proposition_widget: RegulationWidget):
73+
self.frame.layout().removeWidget(proposition_widget)
74+
self.proposition_widgets.remove(proposition_widget)
75+
proposition_widget.deleteLater()
76+
6277
def into_model(self) -> RegulationGroup:
6378
return RegulationGroup(
6479
type_code_id=self.regulation_group_data.type_code_id,
6580
name=self.name.text(),
6681
short_name=None if not self.short_name.text() else self.short_name.text(),
6782
color_code=self.regulation_group_data.color_code,
6883
regulations=[widget.into_model() for widget in self.regulation_widgets],
84+
propositions=[widget.into_model() for widget in self.proposition_widgets],
6985
id_=self.regulation_group_data.id_,
7086
)

0 commit comments

Comments
 (0)