mirror of
https://github.com/TagStudioDev/TagStudio.git
synced 2026-06-25 08:31:49 +00:00
fix: fix various issues with adding templates, reduce reused code
This commit is contained in:
@@ -1314,22 +1314,23 @@ class Library:
|
||||
|
||||
return direct_tags, descendant_tags
|
||||
|
||||
def add_field_template(self, field_template: BaseFieldTemplate) -> bool:
|
||||
def add_field_template(self, field_template: BaseFieldTemplate) -> BaseFieldTemplate | None:
|
||||
"""Add a new field template to the library."""
|
||||
if not (isinstance(field_template, (TextFieldTemplate, DatetimeFieldTemplate))):
|
||||
logger.error("[Library] BaseFieldTemplate attempted to be added to the library.")
|
||||
return False
|
||||
return None
|
||||
|
||||
with Session(self.engine) as session:
|
||||
try:
|
||||
session.add(field_template)
|
||||
session.flush()
|
||||
make_transient(field_template)
|
||||
session.commit()
|
||||
return field_template
|
||||
except IntegrityError as e:
|
||||
logger.error(e)
|
||||
session.rollback()
|
||||
return False
|
||||
|
||||
return True
|
||||
return None
|
||||
|
||||
def update_field_template(self, old_field_type: str, field_template: BaseFieldTemplate) -> bool:
|
||||
"""Update a field template in the library.
|
||||
|
||||
@@ -24,7 +24,7 @@ class EditFieldTemplateModal(EditFieldTemplateModalView):
|
||||
|
||||
def __init__(self, field_template: BaseFieldTemplate | None = None) -> None:
|
||||
super().__init__()
|
||||
self.__field_id: int = field_template.id if field_template else -1
|
||||
self.__field_id: int | None = field_template.id if field_template else None
|
||||
self.__field_name: str = ""
|
||||
self.__field_type: str | None = field_template.class_name if field_template else None
|
||||
self.__text_field_is_multiline: bool = False
|
||||
@@ -47,7 +47,7 @@ class EditFieldTemplateModal(EditFieldTemplateModalView):
|
||||
# Indicates a new template, set default values
|
||||
if field_template is None:
|
||||
self.__field_name = Translations["field_template.new"]
|
||||
self.__field_type = None
|
||||
self.__field_type = list(EditFieldTemplateModal.field_type_map.keys())[0] # First index
|
||||
return
|
||||
# Populate common values for any field type
|
||||
else:
|
||||
|
||||
@@ -69,9 +69,32 @@ class FieldTemplateSearchPanel(SearchPanel[BaseFieldTemplate]):
|
||||
return len(self.__lib.field_templates)
|
||||
|
||||
@override
|
||||
def on_item_create(self) -> None:
|
||||
# TODO: Allow creation of field templates
|
||||
pass
|
||||
def on_item_create(self, add_to_entry: bool = False) -> None:
|
||||
"""Opens panel to create a new field template and optionally add it to an entry.
|
||||
|
||||
Populates name field using current search query.
|
||||
|
||||
Args:
|
||||
add_to_entry (bool): Should this item be added to currently selected entries?
|
||||
"""
|
||||
query: str = self.get_search_query()
|
||||
logger.info("[FieldTemplateSearch] Create and Add Field Template", name=query)
|
||||
|
||||
panel: EditFieldTemplateModal = EditFieldTemplateModal()
|
||||
modal: PanelModal = PanelModal(
|
||||
panel,
|
||||
Translations["field_template.new"],
|
||||
Translations["field_template.add"]
|
||||
if add_to_entry
|
||||
else Translations["field_template.new"],
|
||||
has_save=True,
|
||||
)
|
||||
|
||||
if query.strip():
|
||||
panel.name_field.setText(query)
|
||||
|
||||
modal.saved.connect(lambda: self.create_item(panel, choose_item=add_to_entry))
|
||||
modal.show()
|
||||
|
||||
@override
|
||||
def on_item_edit(self, item: BaseFieldTemplate) -> None:
|
||||
@@ -89,6 +112,8 @@ class FieldTemplateSearchPanel(SearchPanel[BaseFieldTemplate]):
|
||||
|
||||
@override
|
||||
def _on_item_remove(self, item: BaseFieldTemplate) -> None:
|
||||
if self.is_chooser:
|
||||
return
|
||||
|
||||
message_box = QMessageBox(
|
||||
QMessageBox.Icon.Question,
|
||||
@@ -105,29 +130,6 @@ class FieldTemplateSearchPanel(SearchPanel[BaseFieldTemplate]):
|
||||
self.__lib.remove_field_template(item)
|
||||
self.update_items(self.get_search_query())
|
||||
|
||||
@override
|
||||
def on_item_create_and_add(self) -> None:
|
||||
"""Opens "Create Field Template" panel to create a new field template.
|
||||
|
||||
Populates name field using current search query.
|
||||
"""
|
||||
query: str = self.get_search_query()
|
||||
logger.info("[FieldTemplateSearch] Create and Add Field Template", name=query)
|
||||
|
||||
panel: EditFieldTemplateModal = EditFieldTemplateModal()
|
||||
modal: PanelModal = PanelModal(
|
||||
panel,
|
||||
Translations["field_template.new"],
|
||||
Translations["field.add"],
|
||||
has_save=True,
|
||||
)
|
||||
|
||||
if query.strip():
|
||||
panel.name_field.setText(query)
|
||||
|
||||
modal.saved.connect(lambda: self.create_item(panel, choose_item=True))
|
||||
modal.show()
|
||||
|
||||
@override
|
||||
def _on_item_chosen(self, item: BaseFieldTemplate) -> None:
|
||||
self.field_template_chosen.emit(item)
|
||||
@@ -146,6 +148,8 @@ class FieldTemplateSearchPanel(SearchPanel[BaseFieldTemplate]):
|
||||
if item is None:
|
||||
return
|
||||
|
||||
field_template_widget.has_remove = not self.is_chooser
|
||||
|
||||
# Disconnect previous callbacks
|
||||
with catch_warnings(record=True):
|
||||
field_template_widget.on_edit.disconnect()
|
||||
|
||||
@@ -16,6 +16,7 @@ class FieldTemplateWidget(FieldTemplateWidgetView):
|
||||
super().__init__()
|
||||
|
||||
self.__field_template: BaseFieldTemplate | None = None
|
||||
self.has_remove: bool = False
|
||||
|
||||
# Add actions
|
||||
edit_action = QAction(self)
|
||||
@@ -36,12 +37,14 @@ class FieldTemplateWidget(FieldTemplateWidgetView):
|
||||
|
||||
@override
|
||||
def enterEvent(self, event: QEnterEvent) -> None:
|
||||
self._delete_button.setHidden(False)
|
||||
if self.has_remove:
|
||||
self._delete_button.setHidden(False)
|
||||
self.update()
|
||||
return super().enterEvent(event)
|
||||
|
||||
@override
|
||||
def leaveEvent(self, event: QEvent) -> None:
|
||||
self._delete_button.setHidden(True)
|
||||
if self.has_remove:
|
||||
self._delete_button.setHidden(True)
|
||||
self.update()
|
||||
return super().leaveEvent(event)
|
||||
|
||||
@@ -42,11 +42,11 @@ class PreviewPanel(PreviewPanelView):
|
||||
self.__add_tag_modal.tsp.item_chosen.connect(self._add_tag_to_selected)
|
||||
|
||||
def _add_field_to_selected(self, template: BaseFieldTemplate) -> None:
|
||||
self._fields.add_field_to_selected(template)
|
||||
self._containers.add_field_to_selected(template)
|
||||
if len(self._selected) == 1:
|
||||
self._fields.update_from_entry(self._selected[0])
|
||||
self._containers.update_from_entry(self._selected[0])
|
||||
|
||||
def _add_tag_to_selected(self, tag_id: int) -> None:
|
||||
self._fields.add_tags_to_selected(tag_id)
|
||||
self._containers.add_tags_to_selected(tag_id)
|
||||
if len(self._selected) == 1:
|
||||
self._fields.update_from_entry(self._selected[0])
|
||||
self._containers.update_from_entry(self._selected[0])
|
||||
|
||||
@@ -136,14 +136,14 @@ class SearchPanel[T](PanelWidget):
|
||||
|
||||
# Create and add item if no search results
|
||||
if len(self._search_results) <= 0:
|
||||
self.on_item_create_and_add()
|
||||
self.on_item_create(add_to_entry=True)
|
||||
elif self.is_chooser:
|
||||
self._on_item_chosen(self._search_results[0])
|
||||
|
||||
self.clear_search_query()
|
||||
self.update_items()
|
||||
|
||||
def on_item_create(self) -> None:
|
||||
def on_item_create(self, add_to_entry: bool = False) -> None: # pyright: ignore[reportUnusedParameter]
|
||||
raise NotImplementedError()
|
||||
|
||||
def on_item_edit(self, item: T) -> None: # pyright: ignore[reportUnusedParameter]
|
||||
@@ -152,9 +152,6 @@ class SearchPanel[T](PanelWidget):
|
||||
def _on_item_remove(self, item: T) -> None: # pyright: ignore[reportUnusedParameter]
|
||||
raise NotImplementedError()
|
||||
|
||||
def on_item_create_and_add(self) -> None:
|
||||
raise NotImplementedError()
|
||||
|
||||
def _on_item_chosen(self, item: T) -> None: # pyright: ignore[reportUnusedParameter]
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
@@ -76,7 +76,14 @@ class TagSearchPanel(SearchPanel[Tag]):
|
||||
return len(self.__lib.tags)
|
||||
|
||||
@override
|
||||
def on_item_create(self) -> None:
|
||||
def on_item_create(self, add_to_entry: bool = False) -> None:
|
||||
"""Opens panel to create a new tag and optionally add it to an entry.
|
||||
|
||||
Populates name field using current search query.
|
||||
|
||||
Args:
|
||||
add_to_entry (bool): Should this item be added to currently selected entries?
|
||||
"""
|
||||
# TODO: Move this to a top-level import
|
||||
from tagstudio.qt.mixed.build_tag import BuildTagPanel # here due to circular imports
|
||||
|
||||
@@ -86,13 +93,14 @@ class TagSearchPanel(SearchPanel[Tag]):
|
||||
modal: PanelModal = PanelModal(
|
||||
panel,
|
||||
Translations["tag.new"],
|
||||
Translations["tag.add"] if add_to_entry else Translations["tag.new"],
|
||||
has_save=True,
|
||||
)
|
||||
|
||||
if query.strip():
|
||||
panel.name_field.setText(query)
|
||||
|
||||
modal.saved.connect(lambda: self.create_item(panel))
|
||||
modal.saved.connect(lambda: self.create_item(panel, choose_item=add_to_entry))
|
||||
modal.show()
|
||||
|
||||
@override
|
||||
@@ -134,33 +142,6 @@ class TagSearchPanel(SearchPanel[Tag]):
|
||||
self.__lib.remove_tag(item.id)
|
||||
self.update_items(self.get_search_query())
|
||||
|
||||
@override
|
||||
def on_item_create_and_add(self) -> None:
|
||||
"""Opens "Create Tag" panel to create and add a new tag.
|
||||
|
||||
Populates name field using current search query.
|
||||
"""
|
||||
# TODO: Move this to a top-level import
|
||||
from tagstudio.qt.mixed.build_tag import BuildTagPanel # here due to circular imports
|
||||
|
||||
query: str = self.get_search_query()
|
||||
|
||||
logger.info("Create and Add Tag", name=query)
|
||||
|
||||
panel: BuildTagPanel = BuildTagPanel(self.__lib)
|
||||
modal: PanelModal = PanelModal(
|
||||
panel,
|
||||
Translations["tag.new"],
|
||||
Translations["tag.add"],
|
||||
has_save=True,
|
||||
)
|
||||
|
||||
if query.strip():
|
||||
panel.name_field.setText(query)
|
||||
|
||||
modal.saved.connect(lambda: self.create_item(panel, choose_item=True))
|
||||
modal.show()
|
||||
|
||||
@override
|
||||
def _on_item_chosen(self, item: Tag) -> None:
|
||||
self.item_chosen.emit(item.id)
|
||||
|
||||
@@ -233,19 +233,19 @@ class FieldContainers(QWidget):
|
||||
)
|
||||
self.lib.add_field_to_entries(entry_id, field_template.to_field())
|
||||
|
||||
def add_tags_to_selected(self, tags: int | list[int]) -> None:
|
||||
def add_tags_to_selected(self, tag_ids: int | list[int]) -> None:
|
||||
"""Add list of tags to one or more selected items.
|
||||
|
||||
Uses the current driver selection, NOT the field containers cache.
|
||||
"""
|
||||
if isinstance(tags, int):
|
||||
tags = [tags]
|
||||
if isinstance(tag_ids, int):
|
||||
tag_ids = [tag_ids]
|
||||
logger.info(
|
||||
"[FieldContainers][add_tags_to_selected]",
|
||||
selected=self.driver.selected,
|
||||
tags=tags,
|
||||
tag_ids=tag_ids,
|
||||
)
|
||||
self.driver.add_tags_to_selected_callback(tags)
|
||||
self.driver.add_tags_to_selected_callback(tag_ids)
|
||||
|
||||
def write_container(self, index: int, field: BaseField, is_mixed: bool = False) -> None:
|
||||
"""Update/Create data for a FieldContainer.
|
||||
|
||||
@@ -67,7 +67,7 @@ class PreviewPanelView(QWidget):
|
||||
|
||||
self.__thumb = PreviewThumb(self.lib, driver)
|
||||
self.__file_attrs = FileAttributes(self.lib, driver)
|
||||
self._fields = FieldContainers(
|
||||
self._containers = FieldContainers(
|
||||
self.lib, driver
|
||||
) # TODO: this should be name mangled, but is still needed on the controller side atm
|
||||
|
||||
@@ -107,7 +107,7 @@ class PreviewPanelView(QWidget):
|
||||
|
||||
preview_layout.addWidget(self.__thumb)
|
||||
info_layout.addWidget(self.__file_attrs)
|
||||
info_layout.addWidget(self._fields)
|
||||
info_layout.addWidget(self._containers)
|
||||
|
||||
splitter.addWidget(preview_section)
|
||||
splitter.addWidget(info_section)
|
||||
@@ -148,7 +148,7 @@ class PreviewPanelView(QWidget):
|
||||
self.__thumb.hide_preview()
|
||||
self.__file_attrs.update_stats()
|
||||
self.__file_attrs.update_date_label()
|
||||
self._fields.hide_containers()
|
||||
self._containers.hide_containers()
|
||||
|
||||
self.add_buttons_enabled = False
|
||||
|
||||
@@ -163,7 +163,7 @@ class PreviewPanelView(QWidget):
|
||||
stats: FileAttributeData = self.__thumb.display_file(filepath)
|
||||
self.__file_attrs.update_stats(filepath, stats)
|
||||
self.__file_attrs.update_date_label(filepath)
|
||||
self._fields.update_from_entry(entry_id)
|
||||
self._containers.update_from_entry(entry_id)
|
||||
|
||||
self._set_selection_callback()
|
||||
|
||||
@@ -175,7 +175,7 @@ class PreviewPanelView(QWidget):
|
||||
self.__thumb.hide_preview() # TODO: Render mixed selection
|
||||
self.__file_attrs.update_multi_selection(len(selected))
|
||||
self.__file_attrs.update_date_label()
|
||||
self._fields.hide_containers() # TODO: Allow for mixed editing
|
||||
self._containers.hide_containers() # TODO: Allow for mixed editing
|
||||
|
||||
self._set_selection_callback()
|
||||
|
||||
@@ -205,7 +205,7 @@ class PreviewPanelView(QWidget):
|
||||
@property
|
||||
def field_containers_widget(self) -> FieldContainers: # needed for the tests
|
||||
"""Getter for the field containers widget."""
|
||||
return self._fields
|
||||
return self._containers
|
||||
|
||||
@property
|
||||
def preview_thumb(self) -> PreviewThumb:
|
||||
|
||||
@@ -139,7 +139,9 @@ class SearchPanelView(PanelWidget):
|
||||
)
|
||||
|
||||
self.create_button.clicked.connect(controller.on_item_create)
|
||||
self.create_and_add_button.clicked.connect(controller.on_item_create_and_add)
|
||||
self.create_and_add_button.clicked.connect(
|
||||
lambda: controller.on_item_create(add_to_entry=True)
|
||||
)
|
||||
|
||||
def set_limit_items(self, limit_items: list[tuple[str, int]]) -> None:
|
||||
# Remove existing limit items
|
||||
|
||||
Reference in New Issue
Block a user