Skip to content

Commit fac589b

Browse files
committed
refactor: TagDatabasePanel now inherits from TagSearchPanel for code deduplication
1 parent 042f539 commit fac589b

File tree

2 files changed

+102
-160
lines changed

2 files changed

+102
-160
lines changed

tagstudio/src/qt/modals/tag_database.py

Lines changed: 7 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -3,72 +3,32 @@
33
# Created for TagStudio: https://github.com/CyanVoxel/TagStudio
44

55
import structlog
6-
from PySide6.QtCore import QSize, Qt, Signal
7-
from PySide6.QtGui import QShowEvent
86
from PySide6.QtWidgets import (
9-
QFrame,
10-
QHBoxLayout,
11-
QLineEdit,
127
QMessageBox,
138
QPushButton,
14-
QScrollArea,
15-
QVBoxLayout,
16-
QWidget,
179
)
1810
from src.core.constants import RESERVED_TAG_END, RESERVED_TAG_START
1911
from src.core.library import Library, Tag
2012
from src.qt.modals.build_tag import BuildTagPanel
13+
from src.qt.modals.tag_search import TagSearchPanel
2114
from src.qt.translations import Translations
22-
from src.qt.widgets.panel import PanelModal, PanelWidget
23-
from src.qt.widgets.tag import TagWidget
15+
from src.qt.widgets.panel import PanelModal
2416

2517
logger = structlog.get_logger(__name__)
2618

27-
# TODO: This class shares the majority of its code with tag_search.py.
28-
# It should either be made DRY, or be replaced with the intended and more robust
29-
# Tag Management tab/pane outlined on the Feature Roadmap.
19+
# TODO: Once this class is removed, the `is_tag_chooser` option of `TagSearchPanel`
20+
# will most likely be enabled in every case
21+
# and the possibilty of disabling it can therefore be removed
3022

3123

32-
class TagDatabasePanel(PanelWidget):
33-
tag_chosen = Signal(int)
34-
24+
class TagDatabasePanel(TagSearchPanel):
3525
def __init__(self, library: Library):
36-
super().__init__()
37-
self.lib: Library = library
38-
self.is_initialized: bool = False
39-
self.first_tag_id = -1
40-
41-
self.setMinimumSize(300, 400)
42-
self.root_layout = QVBoxLayout(self)
43-
self.root_layout.setContentsMargins(6, 0, 6, 0)
44-
45-
self.search_field = QLineEdit()
46-
self.search_field.setObjectName("searchField")
47-
self.search_field.setMinimumSize(QSize(0, 32))
48-
Translations.translate_with_setter(self.search_field.setPlaceholderText, "home.search_tags")
49-
self.search_field.textEdited.connect(lambda: self.update_tags(self.search_field.text()))
50-
self.search_field.returnPressed.connect(
51-
lambda checked=False: self.on_return(self.search_field.text())
52-
)
53-
54-
self.scroll_contents = QWidget()
55-
self.scroll_layout = QVBoxLayout(self.scroll_contents)
56-
self.scroll_layout.setContentsMargins(6, 0, 6, 0)
57-
self.scroll_layout.setAlignment(Qt.AlignmentFlag.AlignTop)
58-
59-
self.scroll_area = QScrollArea()
60-
self.scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOn)
61-
self.scroll_area.setWidgetResizable(True)
62-
self.scroll_area.setFrameShadow(QFrame.Shadow.Plain)
63-
self.scroll_area.setFrameShape(QFrame.Shape.NoFrame)
64-
self.scroll_area.setWidget(self.scroll_contents)
26+
super().__init__(library, is_tag_chooser=False)
6527

6628
self.create_tag_button = QPushButton()
6729
Translations.translate_qobject(self.create_tag_button, "tag.create")
6830
self.create_tag_button.clicked.connect(lambda: self.build_tag(self.search_field.text()))
6931

70-
self.root_layout.addWidget(self.search_field)
71-
self.root_layout.addWidget(self.scroll_area)
7232
self.root_layout.addWidget(self.create_tag_button)
7333
self.update_tags()
7434

@@ -97,41 +57,6 @@ def build_tag(self, name: str):
9757
)
9858
self.modal.show()
9959

100-
def on_return(self, text: str):
101-
if text and self.first_tag_id >= 0:
102-
# callback(self.first_tag_id)
103-
self.search_field.setText("")
104-
self.update_tags()
105-
else:
106-
self.search_field.setFocus()
107-
self.parentWidget().hide()
108-
109-
def update_tags(self, query: str | None = None):
110-
# TODO: Look at recycling rather than deleting and re-initializing
111-
logger.info("[Tag Manager Modal] Updating Tags")
112-
while self.scroll_layout.itemAt(0):
113-
self.scroll_layout.takeAt(0).widget().deleteLater()
114-
115-
tags_results = self.lib.search_tags(name=query)
116-
117-
for tag in tags_results:
118-
container = QWidget()
119-
row = QHBoxLayout(container)
120-
row.setContentsMargins(0, 0, 0, 0)
121-
row.setSpacing(3)
122-
123-
if tag.id in range(RESERVED_TAG_START, RESERVED_TAG_END):
124-
tag_widget = TagWidget(tag, has_edit=True, has_remove=False)
125-
else:
126-
tag_widget = TagWidget(tag, has_edit=True, has_remove=True)
127-
128-
tag_widget.on_edit.connect(lambda checked=False, t=tag: self.edit_tag(t))
129-
tag_widget.on_remove.connect(lambda t=tag: self.remove_tag(t))
130-
row.addWidget(tag_widget)
131-
self.scroll_layout.addWidget(container)
132-
133-
self.search_field.setFocus()
134-
13560
def remove_tag(self, tag: Tag):
13661
if tag.id in range(RESERVED_TAG_START, RESERVED_TAG_END):
13762
return
@@ -149,29 +74,3 @@ def remove_tag(self, tag: Tag):
14974

15075
self.lib.remove_tag(tag)
15176
self.update_tags()
152-
153-
def edit_tag(self, tag: Tag):
154-
build_tag_panel = BuildTagPanel(self.lib, tag=tag)
155-
156-
self.edit_modal = PanelModal(
157-
build_tag_panel,
158-
tag.name,
159-
done_callback=(self.update_tags(self.search_field.text())),
160-
has_save=True,
161-
)
162-
Translations.translate_with_setter(self.edit_modal.setWindowTitle, "tag.edit")
163-
# TODO Check Warning: Expected type 'BuildTagPanel', got 'PanelWidget' instead
164-
self.edit_modal.saved.connect(lambda: self.edit_tag_callback(build_tag_panel))
165-
self.edit_modal.show()
166-
167-
def edit_tag_callback(self, btp: BuildTagPanel):
168-
self.lib.update_tag(
169-
btp.build_tag(), set(btp.parent_ids), set(btp.alias_names), set(btp.alias_ids)
170-
)
171-
self.update_tags(self.search_field.text())
172-
173-
def showEvent(self, event: QShowEvent) -> None: # noqa N802
174-
if not self.is_initialized:
175-
self.update_tags()
176-
self.is_initialized = True
177-
return super().showEvent(event)

tagstudio/src/qt/modals/tag_search.py

Lines changed: 95 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -17,24 +17,31 @@
1717
QVBoxLayout,
1818
QWidget,
1919
)
20-
from src.core.library import Library
20+
from src.core.constants import RESERVED_TAG_END, RESERVED_TAG_START
21+
from src.core.library import Library, Tag
2122
from src.core.palette import ColorType, get_tag_color
2223
from src.qt.translations import Translations
23-
from src.qt.widgets.panel import PanelWidget
24+
from src.qt.widgets.panel import PanelModal, PanelWidget
2425
from src.qt.widgets.tag import TagWidget
2526

2627
logger = structlog.get_logger(__name__)
2728

2829

2930
class TagSearchPanel(PanelWidget):
3031
tag_chosen = Signal(int)
31-
32-
def __init__(self, library: Library, exclude: list[int] | None = None):
32+
lib: Library
33+
is_initialized: bool = False
34+
first_tag_id: int = None
35+
is_tag_chooser: bool
36+
37+
def __init__(
38+
self, library: Library, exclude: list[int] | None = None, is_tag_chooser: bool = True
39+
):
3340
super().__init__()
3441
self.lib = library
3542
self.exclude = exclude
36-
self.is_initialized: bool = False
37-
self.first_tag_id: int = None
43+
self.is_tag_chooser = is_tag_chooser
44+
3845
self.setMinimumSize(300, 400)
3946
self.root_layout = QVBoxLayout(self)
4047
self.root_layout.setContentsMargins(6, 0, 6, 0)
@@ -44,9 +51,7 @@ def __init__(self, library: Library, exclude: list[int] | None = None):
4451
self.search_field.setMinimumSize(QSize(0, 32))
4552
Translations.translate_with_setter(self.search_field.setPlaceholderText, "home.search_tags")
4653
self.search_field.textEdited.connect(lambda: self.update_tags(self.search_field.text()))
47-
self.search_field.returnPressed.connect(
48-
lambda checked=False: self.on_return(self.search_field.text())
49-
)
54+
self.search_field.returnPressed.connect(lambda: self.on_return(self.search_field.text()))
5055

5156
self.scroll_contents = QWidget()
5257
self.scroll_layout = QVBoxLayout(self.scroll_contents)
@@ -63,17 +68,10 @@ def __init__(self, library: Library, exclude: list[int] | None = None):
6368
self.root_layout.addWidget(self.search_field)
6469
self.root_layout.addWidget(self.scroll_area)
6570

66-
def on_return(self, text: str):
67-
if text and self.first_tag_id is not None:
68-
self.tag_chosen.emit(self.first_tag_id)
69-
self.search_field.setText("")
70-
self.update_tags()
71-
else:
72-
self.search_field.setFocus()
73-
self.parentWidget().hide()
74-
7571
def update_tags(self, query: str | None = None):
76-
logger.info("[Tag Search Modal] Updating Tags")
72+
logger.info("[Tag Search Super Class] Updating Tags")
73+
74+
# TODO: Look at recycling rather than deleting and re-initializing
7775
while self.scroll_layout.count():
7876
self.scroll_layout.takeAt(0).widget().deleteLater()
7977

@@ -87,45 +85,90 @@ def update_tags(self, query: str | None = None):
8785
if self.exclude is not None and tag.id in self.exclude:
8886
continue
8987

90-
c = QWidget()
91-
layout = QHBoxLayout(c)
92-
layout.setContentsMargins(0, 0, 0, 0)
93-
layout.setSpacing(3)
94-
tw = TagWidget(tag, has_edit=False, has_remove=False)
95-
ab = QPushButton()
96-
ab.setMinimumSize(23, 23)
97-
ab.setMaximumSize(23, 23)
98-
ab.setText("+")
99-
ab.setStyleSheet(
100-
f"QPushButton{{"
101-
f"background: {get_tag_color(ColorType.PRIMARY, tag.color)};"
102-
f"color: {get_tag_color(ColorType.TEXT, tag.color)};"
103-
f"font-weight: 600;"
104-
f"border-color:{get_tag_color(ColorType.BORDER, tag.color)};"
105-
f"border-radius: 6px;"
106-
f"border-style:solid;"
107-
f"border-width: {math.ceil(self.devicePixelRatio())}px;"
108-
f"padding-bottom: 5px;"
109-
f"font-size: 20px;"
110-
f"}}"
111-
f"QPushButton::hover"
112-
f"{{"
113-
f"border-color:{get_tag_color(ColorType.LIGHT_ACCENT, tag.color)};"
114-
f"color: {get_tag_color(ColorType.DARK_ACCENT, tag.color)};"
115-
f"background: {get_tag_color(ColorType.LIGHT_ACCENT, tag.color)};"
116-
f"}}"
117-
)
118-
119-
ab.clicked.connect(lambda checked=False, x=tag.id: self.tag_chosen.emit(x))
88+
container = QWidget()
89+
row = QHBoxLayout(container)
90+
row.setContentsMargins(0, 0, 0, 0)
91+
row.setSpacing(3)
12092

121-
layout.addWidget(tw)
122-
layout.addWidget(ab)
123-
self.scroll_layout.addWidget(c)
93+
tag_widget = TagWidget(
94+
tag,
95+
has_edit=True,
96+
has_remove=(not self.is_tag_chooser)
97+
and (tag.id not in range(RESERVED_TAG_START, RESERVED_TAG_END)),
98+
)
99+
tag_widget.on_edit.connect(lambda t=tag: self.edit_tag(t))
100+
tag_widget.on_remove.connect(lambda t=tag: self.remove_tag(t))
101+
row.addWidget(tag_widget)
102+
103+
if self.is_tag_chooser:
104+
add_button = QPushButton()
105+
add_button.setMinimumSize(23, 23)
106+
add_button.setMaximumSize(23, 23)
107+
add_button.setText("+")
108+
add_button.setStyleSheet(
109+
f"QPushButton{{"
110+
f"background: {get_tag_color(ColorType.PRIMARY, tag.color)};"
111+
f"color: {get_tag_color(ColorType.TEXT, tag.color)};"
112+
f"font-weight: 600;"
113+
f"border-color:{get_tag_color(ColorType.BORDER, tag.color)};"
114+
f"border-radius: 6px;"
115+
f"border-style:solid;"
116+
f"border-width: {math.ceil(self.devicePixelRatio())}px;"
117+
f"padding-bottom: 5px;"
118+
f"font-size: 20px;"
119+
f"}}"
120+
f"QPushButton::hover"
121+
f"{{"
122+
f"border-color:{get_tag_color(ColorType.LIGHT_ACCENT, tag.color)};"
123+
f"color: {get_tag_color(ColorType.DARK_ACCENT, tag.color)};"
124+
f"background: {get_tag_color(ColorType.LIGHT_ACCENT, tag.color)};"
125+
f"}}"
126+
)
127+
add_button.clicked.connect(lambda x=tag.id: self.tag_chosen.emit(x))
128+
row.addWidget(add_button)
129+
130+
self.scroll_layout.addWidget(container)
124131

125132
self.search_field.setFocus()
126133

134+
def on_return(self, text: str):
135+
if text and self.first_tag_id is not None:
136+
if self.is_tag_chooser:
137+
self.tag_chosen.emit(self.first_tag_id)
138+
self.search_field.setText("")
139+
self.update_tags()
140+
else:
141+
self.search_field.setFocus()
142+
self.parentWidget().hide()
143+
127144
def showEvent(self, event: QShowEvent) -> None: # noqa N802
128145
if not self.is_initialized:
129146
self.update_tags()
130147
self.is_initialized = True
131148
return super().showEvent(event)
149+
150+
def remove_tag(self, tag: Tag):
151+
raise NotImplementedError
152+
153+
def edit_tag(self, tag: Tag):
154+
# only import here because of circular imports
155+
from src.qt.modals.build_tag import BuildTagPanel
156+
157+
def edit_tag_callback(btp: BuildTagPanel):
158+
self.lib.update_tag(
159+
btp.build_tag(), set(btp.parent_ids), set(btp.alias_names), set(btp.alias_ids)
160+
)
161+
self.update_tags(self.search_field.text())
162+
163+
build_tag_panel = BuildTagPanel(self.lib, tag=tag)
164+
165+
self.edit_modal = PanelModal(
166+
build_tag_panel,
167+
tag.name,
168+
done_callback=(self.update_tags(self.search_field.text())),
169+
has_save=True,
170+
)
171+
Translations.translate_with_setter(self.edit_modal.setWindowTitle, "tag.edit")
172+
173+
self.edit_modal.saved.connect(lambda: edit_tag_callback(build_tag_panel))
174+
self.edit_modal.show()

0 commit comments

Comments
 (0)