mirror of
https://github.com/TagStudioDev/TagStudio.git
synced 2026-01-28 22:01:24 +00:00
feat(tags): add tag merging (#322)
* added merge tag context menu option, created modal, and created merge_tag logic * forgot to close out the modal * added ability to re-assign current tag * ruff/mypy checks
This commit is contained in:
@@ -1895,6 +1895,31 @@ class Library:
|
||||
for t in self.tags:
|
||||
self._map_tag_strings_to_tag_id(t)
|
||||
|
||||
def merge_tag(self, source_tag: Tag, target_tag: Tag) -> None:
|
||||
source_tag_id: int = source_tag.id
|
||||
target_tag_id: int = target_tag.id
|
||||
|
||||
if source_tag.name not in target_tag.aliases:
|
||||
target_tag.aliases.append(source_tag.name)
|
||||
|
||||
for alias in source_tag.aliases:
|
||||
if alias not in target_tag.aliases:
|
||||
target_tag.aliases.append(alias)
|
||||
|
||||
for subtag_id in source_tag.subtag_ids:
|
||||
if subtag_id not in target_tag.subtag_ids:
|
||||
target_tag.subtag_ids.append(subtag_id)
|
||||
|
||||
for entry in self.entries:
|
||||
for field in entry.fields:
|
||||
if self.get_field_attr(field, "type") == "tag_box":
|
||||
if source_tag_id in self.get_field_attr(field, "content"):
|
||||
self.get_field_attr(field, "content").remove(source_tag_id)
|
||||
if target_tag_id not in self.get_field_attr(field, "content"):
|
||||
self.get_field_attr(field, "content").append(target_tag_id)
|
||||
|
||||
self.remove_tag(source_tag_id)
|
||||
|
||||
def get_tag_ref_count(self, tag_id: int) -> tuple[int, int]:
|
||||
"""Returns an int tuple (entry_ref_count, subtag_ref_count) of Tag reference counts."""
|
||||
entry_ref_count: int = 0
|
||||
|
||||
63
tagstudio/src/qt/modals/merge_tag.py
Normal file
63
tagstudio/src/qt/modals/merge_tag.py
Normal file
@@ -0,0 +1,63 @@
|
||||
from PySide6.QtWidgets import QDialog, QVBoxLayout, QLabel, QComboBox, QPushButton
|
||||
from PySide6.QtCore import Qt
|
||||
import logging
|
||||
|
||||
|
||||
class MergeTagModal(QDialog):
|
||||
def __init__(self, library, current_tag):
|
||||
super().__init__()
|
||||
self.lib = library
|
||||
self.current_tag = current_tag
|
||||
self.init_ui()
|
||||
|
||||
def init_ui(self):
|
||||
layout = QVBoxLayout()
|
||||
|
||||
layout.addWidget(QLabel("Selected tag:"))
|
||||
self.selected_tag_dropdown = QComboBox(self)
|
||||
for tag in self.lib.tags:
|
||||
self.selected_tag_dropdown.addItem(tag.display_name(self.lib), tag)
|
||||
self.selected_tag_dropdown.setCurrentIndex(self.find_current_tag_index())
|
||||
self.selected_tag_dropdown.currentIndexChanged.connect(self.update_current_tag)
|
||||
layout.addWidget(self.selected_tag_dropdown)
|
||||
|
||||
arrow_label = QLabel("↓")
|
||||
arrow_label.setAlignment(Qt.AlignCenter)
|
||||
layout.addWidget(arrow_label)
|
||||
|
||||
layout.addWidget(QLabel("Select tag to merge with:"))
|
||||
self.tag2_dropdown = QComboBox(self)
|
||||
self.update_tag2_dropdown()
|
||||
layout.addWidget(self.tag2_dropdown)
|
||||
|
||||
self.merge_button = QPushButton("Merge", self)
|
||||
layout.addWidget(self.merge_button)
|
||||
|
||||
self.merge_button.clicked.connect(self.merge_tags)
|
||||
|
||||
self.setLayout(layout)
|
||||
|
||||
def find_current_tag_index(self):
|
||||
for index in range(self.selected_tag_dropdown.count()):
|
||||
if self.selected_tag_dropdown.itemData(index) == self.current_tag:
|
||||
return index
|
||||
return 0
|
||||
|
||||
def update_current_tag(self):
|
||||
self.current_tag = self.selected_tag_dropdown.currentData()
|
||||
self.update_tag2_dropdown()
|
||||
|
||||
def update_tag2_dropdown(self):
|
||||
self.tag2_dropdown.clear()
|
||||
for tag in self.lib.tags:
|
||||
if tag.id != self.current_tag.id:
|
||||
self.tag2_dropdown.addItem(tag.display_name(self.lib), tag)
|
||||
|
||||
def merge_tags(self):
|
||||
target_tag = self.tag2_dropdown.currentData()
|
||||
if target_tag and self.current_tag != target_tag:
|
||||
self.lib.merge_tag(self.current_tag, target_tag)
|
||||
self.accept()
|
||||
else:
|
||||
logging.error("MergeTagModal: Invalid tag selection.")
|
||||
self.reject()
|
||||
@@ -15,6 +15,7 @@ from PySide6.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QPushButton
|
||||
|
||||
from src.core.library import Library, Tag
|
||||
from src.core.palette import ColorType, get_tag_color
|
||||
from src.qt.modals.merge_tag import MergeTagModal
|
||||
|
||||
|
||||
ERROR = f"[ERROR]"
|
||||
@@ -77,6 +78,10 @@ class TagWidget(QWidget):
|
||||
add_to_search_action = QAction("Add to Search", self)
|
||||
self.bg_button.addAction(add_to_search_action)
|
||||
|
||||
merge_tag_action = QAction("Merge Tag", self)
|
||||
merge_tag_action.triggered.connect(self.show_merge_tag_modal)
|
||||
self.bg_button.addAction(merge_tag_action)
|
||||
|
||||
self.inner_layout = QHBoxLayout()
|
||||
self.inner_layout.setObjectName("innerLayout")
|
||||
self.inner_layout.setContentsMargins(2, 2, 2, 2)
|
||||
@@ -234,6 +239,9 @@ class TagWidget(QWidget):
|
||||
# pass
|
||||
# # self.bg.clicked.connect(lambda checked=False, filepath=filepath: open_file(filepath))
|
||||
# # self.bg.clicked.connect(function)
|
||||
def show_merge_tag_modal(self):
|
||||
modal = MergeTagModal(self.lib, self.tag)
|
||||
modal.exec_()
|
||||
|
||||
def enterEvent(self, event: QEnterEvent) -> None:
|
||||
if self.has_remove:
|
||||
|
||||
Reference in New Issue
Block a user