diff --git a/arho_feature_template/gui/components/plan_regulation_group_widget.py b/arho_feature_template/gui/components/plan_regulation_group_widget.py index 880f8b9..cf4883c 100644 --- a/arho_feature_template/gui/components/plan_regulation_group_widget.py +++ b/arho_feature_template/gui/components/plan_regulation_group_widget.py @@ -1,22 +1,23 @@ from __future__ import annotations from importlib import resources -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, cast from qgis.core import QgsApplication from qgis.PyQt import uic from qgis.PyQt.QtCore import pyqtSignal -from qgis.PyQt.QtGui import QIcon -from qgis.PyQt.QtWidgets import QWidget +from qgis.PyQt.QtGui import QIcon, QPixmap +from qgis.PyQt.QtWidgets import QHBoxLayout, QLabel, QSizePolicy, QWidget -from arho_feature_template.core.models import Proposition, Regulation, RegulationGroup +from arho_feature_template.core.models import PlanFeature, Proposition, Regulation, RegulationGroup from arho_feature_template.gui.components.plan_proposition_widget import PropositionWidget from arho_feature_template.gui.components.plan_regulation_widget import RegulationWidget from arho_feature_template.project.layers.code_layers import PlanRegulationGroupTypeLayer +from arho_feature_template.project.layers.plan_layers import RegulationGroupAssociationLayer from arho_feature_template.qgis_plugin_tools.tools.resources import resources_path if TYPE_CHECKING: - from qgis.PyQt.QtWidgets import QFormLayout, QFrame, QLabel, QLineEdit, QPushButton + from qgis.PyQt.QtWidgets import QFormLayout, QFrame, QLineEdit, QPushButton ui_path = resources.files(__package__) / "plan_regulation_group_widget.ui" FormClass, _ = uic.loadUiType(ui_path) @@ -28,7 +29,7 @@ class RegulationGroupWidget(QWidget, FormClass): # type: ignore open_as_form_signal = pyqtSignal(QWidget) delete_signal = pyqtSignal(QWidget) - def __init__(self, regulation_group: RegulationGroup, layer_name: str): + def __init__(self, regulation_group: RegulationGroup, plan_feature: PlanFeature): super().__init__() self.setupUi(self) @@ -42,11 +43,16 @@ def __init__(self, regulation_group: RegulationGroup, layer_name: str): self.regulation_group_details_layout: QFormLayout # INIT + self.frame.setObjectName("frame") # Set unique name to avoid style cascading self.regulation_widgets: list[RegulationWidget] = [] self.proposition_widgets: list[PropositionWidget] = [] + self.link_label_icon: QLabel | None = None + self.link_label_text: QLabel | None = None - regulation_group.type_code_id = PlanRegulationGroupTypeLayer.get_id_by_feature_layer_name(layer_name) + self.plan_feature = plan_feature + self.layer_name = cast(str, plan_feature.layer_name) self.from_model(regulation_group) + self.regulation_group.type_code_id = PlanRegulationGroupTypeLayer.get_id_by_feature_layer_name(self.layer_name) self.edit_btn.setIcon(QIcon(resources_path("icons", "settings.svg"))) self.edit_btn.clicked.connect(lambda: self.open_as_form_signal.emit(self)) @@ -69,6 +75,19 @@ def from_model(self, regulation_group: RegulationGroup): for proposition in regulation_group.propositions: self.add_proposition_widget(proposition) + # Remove existing indicators if reinitializing + self.unset_existing_regulation_group_style() + + if regulation_group.id_: + other_linked_features_count = len( + RegulationGroupAssociationLayer.get_associations_for_regulation_group_exclude_feature( + cast(str, regulation_group.id_), cast(str, self.plan_feature.id_), self.layer_name + ) + ) + if other_linked_features_count > 0: + # Set indicators that regulation group exists in the plan already and is assigned for other features + self.set_existing_regulation_group_style(other_linked_features_count) + def add_regulation_widget(self, regulation: Regulation) -> RegulationWidget: widget = RegulationWidget(regulation=regulation, parent=self.frame) widget.delete_signal.connect(self.delete_regulation_widget) @@ -93,6 +112,44 @@ def delete_proposition_widget(self, proposition_widget: RegulationWidget): self.proposition_widgets.remove(proposition_widget) proposition_widget.deleteLater() + def set_existing_regulation_group_style(self, other_linked_features_count: int): + tooltip = ( + "Kaavamääräysryhmä on tallennettu kaavaan. Ryhmän tietojen muokkaaminen vaikuttaa muihin " + "kaavakohteisiin, joille ryhmä on lisätty." + ) + layout = QHBoxLayout() + + self.link_label_icon = QLabel() + self.link_label_icon.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Fixed) + self.link_label_icon.setPixmap(QPixmap(resources_path("icons", "linked_img_small.png"))) + self.link_label_icon.setToolTip(tooltip) + layout.addWidget(self.link_label_icon) + + self.link_label_text = QLabel() + self.link_label_text.setObjectName("text_label") # Set unique name to avoid style cascading + self.link_label_text.setText( + f"Kaavamääräysryhmä on käytössä myös {other_linked_features_count} toisella kaavakohteella" + ) + self.link_label_text.setWordWrap(True) + self.link_label_text.setStyleSheet("#text_label { color: #4b8db2; }") + self.link_label_text.setToolTip(tooltip) + layout.addWidget(self.link_label_text) + + self.frame.layout().insertLayout(1, layout) + + self.setStyleSheet("#frame { border: 2px solid #4b8db2; }") + + def unset_existing_regulation_group_style(self): + if self.link_label_icon: + self.link_label_icon.deleteLater() + self.link_label_icon = None + + if self.link_label_text: + self.link_label_text.deleteLater() + self.link_label_text = None + + self.setStyleSheet("") + def into_model(self) -> RegulationGroup: return RegulationGroup( type_code_id=self.regulation_group.type_code_id, diff --git a/arho_feature_template/gui/components/plan_regulation_group_widget.ui b/arho_feature_template/gui/components/plan_regulation_group_widget.ui index ab36df9..bda291f 100644 --- a/arho_feature_template/gui/components/plan_regulation_group_widget.ui +++ b/arho_feature_template/gui/components/plan_regulation_group_widget.ui @@ -6,8 +6,8 @@ <rect> <x>0</x> <y>0</y> - <width>420</width> - <height>129</height> + <width>467</width> + <height>130</height> </rect> </property> <property name="sizePolicy"> diff --git a/arho_feature_template/gui/dialogs/plan_feature_form.py b/arho_feature_template/gui/dialogs/plan_feature_form.py index bf59490..d945fd8 100644 --- a/arho_feature_template/gui/dialogs/plan_feature_form.py +++ b/arho_feature_template/gui/dialogs/plan_feature_form.py @@ -1,7 +1,7 @@ from __future__ import annotations from importlib import resources -from typing import TYPE_CHECKING, cast +from typing import TYPE_CHECKING from qgis.PyQt import uic from qgis.PyQt.QtCore import Qt @@ -78,7 +78,6 @@ def __init__( # Initialize attributes from template self.plan_feature = plan_feature - self.layer_name = plan_feature.layer_name # Should always have a layer name if plan_feature.name: self.feature_name.setText(plan_feature.name) @@ -105,7 +104,7 @@ def add_selected_plan_regulation_group(self, item: QTreeWidgetItem, column: int) self.add_plan_regulation_group(regulation_group) def add_plan_regulation_group(self, definition: RegulationGroup): - regulation_group_widget = RegulationGroupWidget(definition, cast(str, self.layer_name)) + regulation_group_widget = RegulationGroupWidget(definition, self.plan_feature) regulation_group_widget.delete_signal.connect(self.remove_plan_regulation_group) regulation_group_widget.open_as_form_signal.connect(self.open_plan_regulation_group_form) self._remove_spacer() @@ -141,7 +140,7 @@ def into_model(self) -> PlanFeature: type_of_underground_id=self.feature_type_of_underground.value(), description=self.feature_description.toPlainText(), geom=self.plan_feature.geom, - layer_name=self.layer_name, + layer_name=self.plan_feature.layer_name, regulation_groups=[reg_group_widget.into_model() for reg_group_widget in self.regulation_group_widgets], id_=self.plan_feature.id_, ) diff --git a/arho_feature_template/gui/dialogs/plan_regulation_group_form.py b/arho_feature_template/gui/dialogs/plan_regulation_group_form.py index cf43229..5fcd32b 100644 --- a/arho_feature_template/gui/dialogs/plan_regulation_group_form.py +++ b/arho_feature_template/gui/dialogs/plan_regulation_group_form.py @@ -6,7 +6,17 @@ from qgis.core import QgsApplication from qgis.PyQt import uic from qgis.PyQt.QtCore import Qt -from qgis.PyQt.QtWidgets import QDialog, QDialogButtonBox, QTextBrowser, QTreeWidgetItem, QVBoxLayout +from qgis.PyQt.QtGui import QPixmap +from qgis.PyQt.QtWidgets import ( + QDialog, + QDialogButtonBox, + QHBoxLayout, + QLabel, + QSizePolicy, + QTextBrowser, + QTreeWidgetItem, + QVBoxLayout, +) from arho_feature_template.core.models import ( Proposition, @@ -19,6 +29,8 @@ from arho_feature_template.gui.components.plan_regulation_widget import RegulationWidget from arho_feature_template.gui.components.tree_with_search_widget import TreeWithSearchWidget from arho_feature_template.project.layers.code_layers import PlanRegulationGroupTypeLayer +from arho_feature_template.project.layers.plan_layers import RegulationGroupAssociationLayer +from arho_feature_template.qgis_plugin_tools.tools.resources import resources_path if TYPE_CHECKING: from qgis.gui import QgsSpinBox @@ -49,6 +61,8 @@ def __init__(self, regulation_group: RegulationGroup): self.regulations_layout: QBoxLayout self.regulation_info: QTextBrowser + self.regulation_group_info_tab: QWidget + self.propositions_layout: QVBoxLayout self.propositions_scroll_contents: QWidget self.add_proposition_btn: QPushButton @@ -90,6 +104,36 @@ def __init__(self, regulation_group: RegulationGroup): for proposition in self.regulation_group.propositions: self.add_proposition(proposition) + if self.regulation_group.id_: + feat_count = len( + list( + RegulationGroupAssociationLayer.get_associations_for_regulation_group( + str(self.regulation_group.id_) + ) + ) + ) + tooltip = ( + "Kaavamääräysryhmä on tallennettu kaavaan. Ryhmän tietojen muokkaaminen vaikuttaa " + "kaavakohteisiin, joille ryhmä on lisätty." + ) + layout = QHBoxLayout() + + self.link_label_icon = QLabel() + self.link_label_icon.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Fixed) + self.link_label_icon.setPixmap(QPixmap(resources_path("icons", "linked_img_small.png"))) + self.link_label_icon.setToolTip(tooltip) + layout.addWidget(self.link_label_icon) + + self.link_label_text = QLabel() + self.link_label_text.setObjectName("text_label") # Set unique name to avoid style cascading + self.link_label_text.setText(f"Kaavamääräysryhmä on käytössä yhteensä {feat_count} kaavakohteella") + self.link_label_text.setWordWrap(True) + self.link_label_text.setStyleSheet("#text_label { color: #4b8db2; }") + self.link_label_text.setToolTip(tooltip) + layout.addWidget(self.link_label_text) + + self.regulation_group_info_tab.layout().insertLayout(1, layout) + def _initalize_regulation_from_config(self, config: RegulationConfig, parent: QTreeWidgetItem | None = None): item = self.regulations_selection_widget.add_item_to_tree(config.name, config, parent) diff --git a/arho_feature_template/gui/dialogs/plan_regulation_group_form.ui b/arho_feature_template/gui/dialogs/plan_regulation_group_form.ui index 973d0e4..fddd83f 100644 --- a/arho_feature_template/gui/dialogs/plan_regulation_group_form.ui +++ b/arho_feature_template/gui/dialogs/plan_regulation_group_form.ui @@ -25,83 +25,81 @@ </attribute> <layout class="QVBoxLayout" name="verticalLayout_3"> <item> - <widget class="QScrollArea" name="scrollArea"> - <property name="widgetResizable"> - <bool>true</bool> + <layout class="QFormLayout" name="formLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>Otsikko</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLineEdit" name="name"/> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Lyhyt nimi</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLineEdit" name="short_name"/> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Ryhmänumero</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QgsSpinBox" name="group_number"> + <property name="specialValueText"> + <string>NULL</string> + </property> + <property name="maximum"> + <number>999</number> + </property> + <property name="clearValue"> + <bool>false</bool> + </property> + </widget> + </item> + <item row="3" column="0"> + <widget class="QLabel" name="label_5"> + <property name="text"> + <string>Värikoodi</string> + </property> + </widget> + </item> + <item row="3" column="1"> + <widget class="QLineEdit" name="color_code"/> + </item> + <item row="4" column="0"> + <widget class="QLabel" name="label_7"> + <property name="text"> + <string>Tyyppi</string> + </property> + </widget> + </item> + <item row="4" column="1"> + <widget class="CodeComboBox" name="type_of_regulation_group"/> + </item> + </layout> + </item> + <item> + <spacer name="verticalSpacer_3"> + <property name="orientation"> + <enum>Qt::Vertical</enum> </property> - <widget class="QWidget" name="scrollAreaWidgetContents"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>851</width> - <height>577</height> - </rect> - </property> - <layout class="QFormLayout" name="formLayout"> - <item row="0" column="0"> - <widget class="QLabel" name="label_2"> - <property name="text"> - <string>Otsikko</string> - </property> - </widget> - </item> - <item row="0" column="1"> - <widget class="QLineEdit" name="name"/> - </item> - <item row="1" column="0"> - <widget class="QLabel" name="label_3"> - <property name="text"> - <string>Lyhyt nimi</string> - </property> - </widget> - </item> - <item row="1" column="1"> - <widget class="QLineEdit" name="short_name"/> - </item> - <item row="2" column="0"> - <widget class="QLabel" name="label"> - <property name="text"> - <string>Ryhmänumero</string> - </property> - </widget> - </item> - <item row="3" column="0"> - <widget class="QLabel" name="label_5"> - <property name="text"> - <string>Värikoodi</string> - </property> - </widget> - </item> - <item row="3" column="1"> - <widget class="QLineEdit" name="color_code"/> - </item> - <item row="4" column="0"> - <widget class="QLabel" name="label_7"> - <property name="text"> - <string>Tyyppi</string> - </property> - </widget> - </item> - <item row="4" column="1"> - <widget class="CodeComboBox" name="type_of_regulation_group"/> - </item> - <item row="2" column="1"> - <widget class="QgsSpinBox" name="group_number"> - <property name="specialValueText"> - <string>NULL</string> - </property> - <property name="maximum"> - <number>999</number> - </property> - <property name="clearValue"> - <bool>false</bool> - </property> - </widget> - </item> - </layout> - </widget> - </widget> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> </item> </layout> </widget> @@ -156,8 +154,8 @@ p, li { white-space: pre-wrap; } <rect> <x>0</x> <y>0</y> - <width>587</width> - <height>577</height> + <width>38</width> + <height>18</height> </rect> </property> <layout class="QVBoxLayout" name="regulations_layout"> @@ -195,8 +193,8 @@ p, li { white-space: pre-wrap; } <rect> <x>0</x> <y>0</y> - <width>851</width> - <height>577</height> + <width>120</width> + <height>49</height> </rect> </property> <layout class="QVBoxLayout" name="propositions_layout"> diff --git a/arho_feature_template/project/layers/plan_layers.py b/arho_feature_template/project/layers/plan_layers.py index f8a08db..3657541 100644 --- a/arho_feature_template/project/layers/plan_layers.py +++ b/arho_feature_template/project/layers/plan_layers.py @@ -286,6 +286,17 @@ def get_associations_for_feature(cls, feature_id: str, layer_name: str) -> Gener def get_associations_for_regulation_group(cls, group_id: str) -> Generator[QgsFeature]: return cls.get_features_by_attribute_value("plan_regulation_group_id", group_id) + @classmethod + def get_associations_for_regulation_group_exclude_feature( + cls, group_id: str, excluded_feature_id: str, excluded_feature_layer_name: str + ) -> list[QgsFeature]: + attribute = RegulationGroupAssociationLayer.layer_name_to_attribute_map.get(excluded_feature_layer_name) + return [ + association + for association in cls.get_associations_for_regulation_group(group_id) + if association[attribute] != excluded_feature_id + ] + @classmethod def get_group_ids_for_feature(cls, feature_id: str, layer_name: str) -> Generator[str]: attribute = cls.layer_name_to_attribute_map.get(layer_name) @@ -326,7 +337,7 @@ def feature_from_model(cls, model: Regulation) -> QgsFeature: feature["unit"] = model.config.unit feature["text_value"] = {LANGUAGE: model.value if isinstance(model.value, str) else ""} feature["numeric_value"] = model.value if isinstance(model.value, Number) else NULL - feature["name"] = {LANGUAGE: model.topic_tag if model.topic_tag else ""} + # feature["name"] = {LANGUAGE: model.topic_tag if model.topic_tag else ""} feature["type_of_verbal_plan_regulation_id"] = model.verbal_regulation_type_id feature["id"] = model.id_ if model.id_ else feature["id"] # feature["plan_theme_id"] diff --git a/arho_feature_template/resources/icons/linked_img.png b/arho_feature_template/resources/icons/linked_img.png new file mode 100644 index 0000000..1080f71 Binary files /dev/null and b/arho_feature_template/resources/icons/linked_img.png differ diff --git a/arho_feature_template/resources/icons/linked_img_small.png b/arho_feature_template/resources/icons/linked_img_small.png new file mode 100644 index 0000000..dced74f Binary files /dev/null and b/arho_feature_template/resources/icons/linked_img_small.png differ