Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

78 lue lisätiedonlajit QGISin tasolta kaavamääräyksen widgetissä #80

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion arho_feature_template/core/plan_regulation_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ class PlanRegulationDefinition:
"""Associates a PlanRegulationConfig with an optional default value and additional data."""

regulation_config: PlanRegulationConfig
default_value: str | Number | None
default_value: str | Number | list[int] | None
additional_information: list[
dict[str, str | Number | None]
] # NOTE: Correct typing for additional information values?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def add_selected_plan_regulation(self, item: QTreeWidgetItem, column: int):
self.add_plan_regulation(config)

def add_plan_regulation(self, config: PlanRegulationConfig):
widget = PlanRegulationWidget(config=config, parent=self.plan_regulations_scroll_area_contents)
widget = PlanRegulationWidget.from_config(config=config, parent=self.plan_regulations_scroll_area_contents)
widget.delete_signal.connect(self.delete_plan_regulation)
index = self.plan_regulations_layout.count() - 1
self.plan_regulations_layout.insertWidget(index, widget)
Expand Down
10 changes: 4 additions & 6 deletions arho_feature_template/gui/plan_regulation_group_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
if TYPE_CHECKING:
from qgis.PyQt.QtWidgets import QFrame, QLineEdit, QPushButton

from arho_feature_template.core.plan_regulation_config import PlanRegulationConfig
from arho_feature_template.core.plan_regulation_config import PlanRegulationDefinition
from arho_feature_template.core.plan_regulation_group_config import PlanRegulationGroupDefinition

ui_path = resources.files(__package__) / "plan_regulation_group_widget.ui"
Expand Down Expand Up @@ -41,16 +41,14 @@ def __init__(self, group_definition: PlanRegulationGroupDefinition):
self.heading.setText(self.group_definition.name)
self.init_buttons()
for plan_regulation_definition in self.group_definition.plan_regulations:
config = plan_regulation_definition.regulation_config
widget = self.add_plan_regulation_widget(config)
widget.populate_from_definition(plan_regulation_definition)
_ = self.add_plan_regulation_widget(plan_regulation_definition)

def init_buttons(self):
self.del_btn.setIcon(QgsApplication.getThemeIcon("mActionDeleteSelected.svg"))
self.del_btn.clicked.connect(lambda: self.delete_signal.emit(self))

def add_plan_regulation_widget(self, config: PlanRegulationConfig) -> PlanRegulationWidget:
widget = PlanRegulationWidget(config=config, parent=self.frame)
def add_plan_regulation_widget(self, definition: PlanRegulationDefinition) -> PlanRegulationWidget:
widget = PlanRegulationWidget.from_definition(definition=definition, parent=self.frame)
widget.delete_signal.connect(self.delete_plan_regulation_widget)
self.frame.layout().addWidget(widget)
return widget
Expand Down
180 changes: 111 additions & 69 deletions arho_feature_template/gui/plan_regulation_widget.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

from importlib import resources
from numbers import Number
from typing import TYPE_CHECKING, cast

from qgis.core import QgsApplication
Expand Down Expand Up @@ -46,14 +47,15 @@
class PlanRegulationWidget(QWidget, FormClass): # type: ignore
"""A widget representation of a plan regulation."""

language = "fin"

delete_signal = pyqtSignal(QWidget)

def __init__(self, config: PlanRegulationConfig, parent=None):
super().__init__(parent)
self.setupUi(self)

# TYPES
# self.spacer_layout: QBoxLayout
self.plan_regulation_name: QLineEdit
self.form_layout: QFormLayout

Expand All @@ -72,103 +74,123 @@ def __init__(self, config: PlanRegulationConfig, parent=None):
self.widgets: list[tuple[QLabel, QWidget]] = []
self.plan_regulation_name.setText(config.name)
self.plan_regulation_name.setReadOnly(True)
self.init_value_fields()
self.init_buttons()
self.del_btn.setIcon(QgsApplication.getThemeIcon("mActionDeleteSelected.svg"))
self.del_btn.clicked.connect(lambda: self.delete_signal.emit(self))
self.expand_hide_btn.clicked.connect(self._on_expand_hide_btn_clicked)
self.init_additional_information_btn()
self.init_other_information_btn()

@classmethod
def from_config(cls, config: PlanRegulationConfig, parent=None) -> PlanRegulationWidget:
instance = cls(config, parent)
instance.init_fields()
return instance

@classmethod
def from_definition(cls, definition: PlanRegulationDefinition, parent=None) -> PlanRegulationWidget:
instance = cls(definition.regulation_config, parent)
instance.init_fields_from_definition(definition)
return instance

def init_fields_from_definition(self, definition: PlanRegulationDefinition):
# Value input
value_type = self.config.value_type
if value_type:
self._add_value_input(value_type, self.config.unit, definition.default_value)

def populate_from_definition(self, definition: PlanRegulationDefinition):
# Additional information
for info in definition.additional_information:
info_type: str = cast("str", info["type"])
self.add_additional_info(info_type)
if info_type == "kayttotarkoituskohdistus":
info_value_widget = QLineEdit()
label = QLabel(get_additional_information_name(info_type))
value = info.get("value")
if value:
info_value_widget.setText(value)
self.form_layout.addRow(label, info_value_widget)
self.add_additional_info(info_type, info.get("value"))

# TODO: Other saved information from PlanRegulationDefinition

def init_value_fields(self):
def init_fields(self):
value_type = self.config.value_type
if value_type:
self._add_value_input(value_type, self.config.unit)

def _add_value_input(self, value_type: ValueType, unit: Unit | None):
@staticmethod
def _check_number_or_none(value: str | Number | None, error_msg: str):
if not isinstance(value, Number) and value is not None:
raise ValueError(error_msg)

def _add_value_input(
self, value_type: ValueType, unit: Unit | None, default_value: str | Number | list[int] | None = None
):
base_error_msg = f"Invalid type for default value {type(default_value)}."
if value_type in [ValueType.DECIMAL, ValueType.POSITIVE_DECIMAL]:
self.add_decimal_input(value_type, unit)
if not isinstance(default_value, Number) and default_value is not None:
raise ValueError(base_error_msg)
self.add_decimal_input(value_type, unit, default_value)
elif value_type == ValueType.POSITIVE_INTEGER:
self.add_positive_integer_input(unit)
if not isinstance(default_value, int) and default_value is not None:
raise ValueError(base_error_msg)
self.add_positive_integer_input(unit, default_value)
elif value_type == ValueType.POSITIVE_INTEGER_RANGE:
self.add_positive_integer_range_input(unit)
if not isinstance(default_value, list) and default_value is not None:
raise ValueError(base_error_msg)
if isinstance(default_value, list) and len(default_value) != 2: # noqa: PLR2004
error_msg = f"Invalid number of values in default value {type(default_value)}."
raise ValueError(error_msg)
self.add_positive_integer_range_input(unit, default_value)
elif value_type == ValueType.VERSIONED_TEXT:
self.add_versioned_text_input()
if not isinstance(default_value, str) and default_value is not None:
raise ValueError(base_error_msg)
self.add_versioned_text_input(default_value)
else:
msg = f"Invalid input value type for plan regulation: {value_type}"
raise ValueError(msg)

def init_buttons(self):
# DEL
self.del_btn.setIcon(QgsApplication.getThemeIcon("mActionDeleteSelected.svg"))
self.del_btn.clicked.connect(lambda: self.delete_signal.emit(self))

# ADDITIONAL INFORMATION
type_menu = QMenu("Tyyppi", self)
type_menu_items = [
"Pääkäyttötarkoitus",
"Osa-alue",
"Poisluettava käyttötarkoitus",
"Väliaikainen määräys",
"Vaihtoehtoinen",
"Ohjeellinen sijainti",
"Yhteystarve",
]

for item in type_menu_items:
action = type_menu.addAction(item)
action.triggered.connect(lambda _, item=item: self.add_additional_info(item))

signifigance_menu = QMenu("Merkittävyys", self)
signifigance_menu_items = [
"Kansainvälinen",
"Valtakunnallinen",
"Maakunnallinen",
"Seudullinen",
"Alueellinen",
"Paikallinen",
]

for item in signifigance_menu_items:
action = signifigance_menu.addAction(item)
action.triggered.connect(lambda _, item=item: self.add_additional_info(item))

type_main_menu = QMenu(self)
type_main_menu.addMenu(type_menu)
type_main_menu.addMenu(signifigance_menu)
self.add_additional_information_btn.setMenu(type_main_menu)
def init_additional_information_btn(self):
informations_dict: dict[str, QMenu] = {}
add_later: dict[str, list[str]] = {}

def _add_action(informations_dict: dict[str, QMenu], parent_id: str, info_type: str):
action = informations_dict[parent_id].addAction(info_type)
action.triggered.connect(lambda _: self.add_additional_info(info_type))

# Iterate code layer and build menus
for feature in get_layer_by_name("Lisätiedonlaji").getFeatures():
if feature["level"] == 1:
menu = QMenu(feature["name"][self.language], self)
informations_dict[feature["id"]] = menu
else:
parent_id = feature["parent_id"]
info_type = feature["name"][self.language]
if parent_id in informations_dict:
_add_action(informations_dict, parent_id, info_type)
else:
if parent_id not in add_later:
add_later[parent_id] = []
add_later[parent_id].append(info_type)
for parent_id, additional_information_types in add_later.items():
for info_type in additional_information_types:
_add_action(informations_dict, parent_id, info_type)

# Create main menu for btn and add submenus
additional_information_type_menu = QMenu(self)
for menu in informations_dict.values():
additional_information_type_menu.addMenu(menu)
self.add_additional_information_btn.setMenu(additional_information_type_menu)
self.add_additional_information_btn.setIcon(QgsApplication.getThemeIcon("mActionPropertiesWidget.svg"))

# OTHER INFO / ADD FIELD
def init_other_information_btn(self):
add_field_menu = QMenu(self)
add_field_menu.addAction("Määräysnumero").triggered.connect(self.add_regulation_number)
add_field_menu.addAction("Liiteasiakirja").triggered.connect(self.add_file)
add_field_menu.addAction("Aihetunniste").triggered.connect(self.add_topic_tag)

theme_menu = QMenu("Kaavoitusteema", self)
language = "fin"
for feature in get_layer_by_name("Kaavoitusteemat").getFeatures():
name = feature["name"][language]
name = feature["name"][self.language]
action = theme_menu.addAction(name)
action.triggered.connect(lambda _, name=name: self.add_theme(name))
add_field_menu.addMenu(theme_menu)

self.add_field_btn.setMenu(add_field_menu)
self.add_field_btn.setIcon(QgsApplication.getThemeIcon("mActionAdd.svg"))

# EXPAND BTN
self.expand_hide_btn.clicked.connect(self._on_expand_hide_btn_clicked)

def _on_expand_hide_btn_clicked(self):
if self.expanded:
for label, value_widget in self.widgets:
Expand All @@ -192,7 +214,7 @@ def _add_widgets_to_form(self, label: QLabel, widget: QWidget):
if not self.expanded:
self._on_expand_hide_btn_clicked()

def add_decimal_input(self, value_type: ValueType, unit: Unit | None):
def add_decimal_input(self, value_type: ValueType, unit: Unit | None, default_value: Number | None = None):
value_widget = QgsDoubleSpinBox()
label = QLabel("Arvo")
if value_type == ValueType.POSITIVE_DECIMAL:
Expand All @@ -204,19 +226,23 @@ def add_decimal_input(self, value_type: ValueType, unit: Unit | None):
value_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
if unit:
value_widget.setSuffix(f" {unit.value}")
if default_value:
value_widget.setValue(default_value)
self._add_widgets_to_form(label, value_widget)

def add_positive_integer_input(self, unit: Unit | None):
def add_positive_integer_input(self, unit: Unit | None, default_value: int | None = None):
value_widget = QgsSpinBox()
value_widget.setMinimum(0)
value_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
if unit:
value_widget.setSuffix(f" {unit.value}")
label = QLabel("Arvo")
label.setToolTip("Tyyppi: kokonaisluku (positiivinen)")
if default_value:
value_widget.setValue(default_value)
self._add_widgets_to_form(label, value_widget)

def add_positive_integer_range_input(self, unit: Unit | None):
def add_positive_integer_range_input(self, unit: Unit | None, default_values: list[int] | None = None):
min_widget = QgsSpinBox()
min_widget.setMinimum(0)
min_label = QLabel("Arvo minimi")
Expand All @@ -229,21 +255,37 @@ def add_positive_integer_range_input(self, unit: Unit | None):
if unit:
min_widget.setSuffix(f" {unit.value}")
max_widget.setSuffix(f" {unit.value}")
if default_values:
min_widget.setValue(default_values[0])
max_widget.setValue(default_values[1])
self._add_widgets_to_form(min_label, min_widget)
self._add_widgets_to_form(max_label, max_widget)

def add_versioned_text_input(self):
def add_versioned_text_input(self, default_value: str | None = None):
text_widget = QTextEdit()
label = QLabel("Arvo")
label.setToolTip("Tyyppi: kieliversioitu teksti")
if default_value:
text_widget.setText(default_value)
self._add_widgets_to_form(label, text_widget)

def add_additional_info(self, info_type: str):
info_type_line = QLineEdit(get_additional_information_name(info_type))
def add_additional_info(self, info_type: str, default_value: str | Number | None = None):
# NOTE: Now info type is the name / readable version when this is triggered by user
# Might need to refactor this later..
name = get_additional_information_name(info_type)
info_type_line = QLineEdit(name)
info_type_line.setReadOnly(True)
label = QLabel("Lisätiedonlaji")
self._add_widgets_to_form(label, info_type_line)

if name == "Käyttötarkoituskohdistus":
info_value_widget = QLineEdit()
info_value_label = QLabel(name)
self._add_widgets_to_form(info_value_label, info_value_widget)

if default_value:
info_value_widget.setText(str(default_value))

def add_regulation_number(self):
if not self.regulation_number_added:
number_widget = QgsSpinBox()
Expand Down
Loading