"""
Provide entity management widgets for the Graphical User Interface.
"""
from __future__ import annotations
from typing import Callable, cast, Iterator, TYPE_CHECKING
from PyQt6.QtWidgets import (
QFormLayout,
QLabel,
QComboBox,
QLineEdit,
QWidget,
QPushButton,
QVBoxLayout,
QHBoxLayout,
)
from typing_extensions import override
from betty.gui.locale import LocalizedObject
from betty.model import UserFacingEntity, Entity
from betty.project import EntityReference, EntityReferenceSequence
if TYPE_CHECKING:
from betty.locale import Localizable
from betty.app import App
[docs]
class EntityReferenceCollector(LocalizedObject, QWidget):
"""
A form widget that allows users to configure an entity reference.
"""
[docs]
def __init__(
self,
app: App,
entity_reference: EntityReference[UserFacingEntity & Entity],
label_builder: Callable[[], str] | None = None,
caption_builder: Callable[[], str] | None = None,
):
super().__init__(app)
self._entity_reference = entity_reference
self._label_builder = label_builder
self._caption_builder = caption_builder
self._layout = QFormLayout()
self.setLayout(self._layout)
if self._entity_reference.entity_type_is_constrained:
self._entity_type_label = QLabel()
else:
def _update_entity_type() -> None:
self._entity_reference.entity_type = self._entity_type.currentData()
self._entity_type = QComboBox()
self._entity_type.currentIndexChanged.connect(_update_entity_type)
entity_types = enumerate(
sorted(
cast(
Iterator[type["UserFacingEntity & Entity"]],
filter(
lambda entity_type: issubclass(
entity_type, UserFacingEntity
),
self._app.entity_types,
),
),
key=lambda entity_type: entity_type.entity_type_label().localize(
self._app.localizer
),
)
)
for i, entity_type in entity_types:
self._entity_type.addItem(
entity_type.entity_type_label().localize(self._app.localizer),
entity_type,
)
if entity_type == self._entity_reference.entity_type:
self._entity_type.setCurrentIndex(i)
self._entity_type_label = QLabel()
self._layout.addRow(self._entity_type_label, self._entity_type)
def _update_entity_id() -> None:
self._entity_reference.entity_id = self._entity_id.text()
self._entity_id = QLineEdit()
self._entity_id.textChanged.connect(_update_entity_id)
self._entity_id_label = QLabel()
self._layout.addRow(self._entity_id_label, self._entity_id)
self._set_translatables()
@override
def _set_translatables(self) -> None:
super()._set_translatables()
if self._entity_reference.entity_type:
self._entity_id_label.setText(
self._app.localizer._("{entity_type_label} ID").format(
entity_type_label=self._entity_reference.entity_type.entity_type_label().localize(
self._app.localizer
),
)
)
else:
self._entity_id_label.setText(self._app.localizer._("Entity ID"))
[docs]
class EntityReferenceSequenceCollector(LocalizedObject, QWidget):
"""
A form widget that allows users to configure a sequence of entity references.
"""
[docs]
def __init__(
self,
app: App,
entity_references: EntityReferenceSequence[UserFacingEntity & Entity],
label_text: Localizable | None = None,
caption_text: Localizable | None = None,
):
super().__init__(app)
self._entity_references = entity_references
self._label_text = label_text
self._caption_text = caption_text
self._entity_reference_collectors: list[EntityReferenceCollector] = [
EntityReferenceCollector(self._app, entity_reference)
for entity_reference in entity_references
]
self._layout = QVBoxLayout()
self.setLayout(self._layout)
if label_text:
self._label = QLabel()
self._layout.addWidget(self._label)
self._entity_reference_collectors_widget = QWidget()
self._entity_reference_collectors_layout = QVBoxLayout()
self._entity_reference_collectors_widget.setLayout(
self._entity_reference_collectors_layout
)
self._layout.addWidget(self._entity_reference_collectors_widget)
self._entity_reference_collection_widgets: list[QWidget] = []
self._entity_reference_remove_buttons: list[QPushButton] = []
if caption_text:
self._caption = QLabel()
self._layout.addWidget(self._caption)
self._add_entity_reference_button = QPushButton()
self._add_entity_reference_button.released.connect(self._add_entity_reference)
self._layout.addWidget(self._add_entity_reference_button)
self._build_entity_references_collection()
self._set_translatables()
def _add_entity_reference(self) -> None:
entity_reference = EntityReference["UserFacingEntity & Entity"]()
self._entity_references.append(entity_reference)
self._build_entity_reference_collection(
len(self._entity_references) - 1, entity_reference
)
self._set_translatables()
def _remove_entity_reference(self, i: int) -> None:
del self._entity_references[i]
del self._entity_reference_collectors[i]
del self._entity_reference_remove_buttons[i]
self._entity_reference_collectors_layout.removeWidget(
self._entity_reference_collection_widgets[i]
)
del self._entity_reference_collection_widgets[i]
def _build_entity_references_collection(self) -> None:
for i, entity_reference in enumerate(self._entity_references):
self._build_entity_reference_collection(i, entity_reference)
def _build_entity_reference_collection(
self,
i: int,
entity_reference: EntityReference[UserFacingEntity & Entity],
) -> None:
widget = QWidget()
layout = QHBoxLayout()
widget.setLayout(layout)
self._entity_reference_collection_widgets.insert(i, widget)
self._entity_reference_collectors_layout.insertWidget(i, widget)
entity_reference_collector = EntityReferenceCollector(
self._app, entity_reference
)
self._entity_reference_collectors.append(entity_reference_collector)
layout.addWidget(entity_reference_collector)
entity_reference_remove_button = QPushButton()
self._entity_reference_remove_buttons.insert(i, entity_reference_remove_button)
entity_reference_remove_button.released.connect(
lambda: self._remove_entity_reference(i)
)
layout.addWidget(entity_reference_remove_button)
@override
def _set_translatables(self) -> None:
super()._set_translatables()
if self._label_text:
self._label.setText(self._label_text.localize(self._app.localizer))
if self._caption_text:
self._caption.setText(self._caption_text.localize(self._app.localizer))
self._add_entity_reference_button.setText(
self._app.localizer._("Add an entity")
)
for entity_reference_remove_button in self._entity_reference_remove_buttons:
entity_reference_remove_button.setText(self._app.localizer._("Remove"))