Merge branch 'main' into feat/translations

This commit is contained in:
Jann Stute
2024-12-23 19:26:03 +01:00
10 changed files with 184 additions and 238 deletions

1
.gitignore vendored
View File

@@ -262,6 +262,7 @@ compile_commands.json
tagstudio/tests/fixtures/library/*
tagstudio/tests/fixtures/json_library/.TagStudio/*.sqlite
TagStudio.ini
*.sqlite-journal
.envrc
.direnv

View File

@@ -86,13 +86,13 @@ Features are broken up into the following priority levels, with nested prioritie
- [ ] GPS Location [LOW]
- [ ] Custom field names [HIGH] [#18](https://github.com/TagStudioDev/TagStudio/issues/18)
- [ ] Search engine [HIGH] [#325](https://github.com/TagStudioDev/TagStudio/issues/325)
- [ ] Boolean operators [HIGH] [#225](https://github.com/TagStudioDev/TagStudio/issues/225), [#314](https://github.com/TagStudioDev/TagStudio/issues/314)
- [x] Boolean operators [HIGH] [#225](https://github.com/TagStudioDev/TagStudio/issues/225), [#314](https://github.com/TagStudioDev/TagStudio/issues/314)
- [ ] Tag objects + autocomplete [HIGH] [#476 (Autocomplete)](https://github.com/TagStudioDev/TagStudio/issues/476)
- [ ] Filename search [HIGH]
- [ ] Filetype search [HIGH]
- [ ] Search by extension (e.g. ".jpg", ".png") [HIGH]
- [ ] Optional consolidation of extension synonyms (i.e. ".jpg" can equal ".jpeg") [LOW]
- [ ] Search by media type (e.g. "image", "video", "document") [MEDIUM]
- [x] Filename search [HIGH]
- [x] Filetype search [HIGH]
- [x] Search by extension (e.g. ".jpg", ".png") [HIGH]
- [x] Optional consolidation of extension synonyms (i.e. ".jpg" can equal ".jpeg") [LOW]
- [x] Search by media type (e.g. "image", "video", "document") [MEDIUM]
- [ ] Field content search [HIGH] [#272](https://github.com/TagStudioDev/TagStudio/issues/272)
- [ ] HAS operator for composition tags [HIGH]
- [ ] OCR search [LOW]
@@ -172,12 +172,12 @@ These version milestones are rough estimations for when the previous core featur
- [ ] [Tag Categories](../library/tag_categories.md) [HIGH]
- [ ] Property available for tags that allow the tag and any inheriting from it to be displayed separately in the preview panel under a title [HIGH]
- [ ] Search engine [HIGH]
- [ ] Boolean operators [HIGH]
- [x] Boolean operators [HIGH]
- [ ] Tag objects + autocomplete [HIGH]
- [x] Filename search [HIGH]
- [x] Filetype search [HIGH]
- [x] Search by extension (e.g. ".jpg", ".png") [HIGH]
- [ ] Optional consolidation of extension synonyms (i.e. ".jpg" can equal ".jpeg") [LOW]
- [x] Optional consolidation of extension synonyms (i.e. ".jpg" can equal ".jpeg") [LOW]
- [x] Search by media type (e.g. "image", "video", "document") [MEDIUM]
- [ ] Field content search [HIGH]
- [ ] Sortable results [HIGH]

View File

@@ -163,7 +163,6 @@
"tag.parent_tags": "Parent Tags",
"tag.search_for_tag": "Search for Tag",
"tag.shorthand": "Shorthand",
"tag.remove_alias": "Remove selected alias",
"tag.tag_name_required": "Tag Name (Required)",
"view.size.0": "Mini",
"view.size.1": "Small",
@@ -181,5 +180,5 @@
"drop_import.progress.label.plural": "Importing New Files...\n{count} Files Imported.{suffix}",
"drop_import.progress.window_title": "Import Files",
"drop_import.duplicates_choice.singular": "The following file has a filename that already exists in the library.",
"drop_import.duplicates_choice.plural": "The following {count} files have filenames that already exist in the library.",
"drop_import.duplicates_choice.plural": "The following {count} files have filenames that already exist in the library."
}

View File

@@ -619,6 +619,7 @@ class Library:
)
session.expunge_all()
return res
def get_all_child_tag_ids(self, tag_id: int) -> list[int]:
@@ -901,9 +902,9 @@ class Library:
def add_tag(
self,
tag: Tag,
subtag_ids: set[int] | None = None,
alias_names: set[str] | None = None,
alias_ids: set[int] | None = None,
subtag_ids: list[int] | set[int] | None = None,
alias_names: list[str] | set[str] | None = None,
alias_ids: list[int] | set[int] | None = None,
) -> Tag | None:
with Session(self.engine, expire_on_commit=False) as session:
try:
@@ -1077,9 +1078,9 @@ class Library:
def update_tag(
self,
tag: Tag,
subtag_ids: set[int] | None = None,
alias_names: set[str] | None = None,
alias_ids: set[int] | None = None,
subtag_ids: list[int] | set[int] | None = None,
alias_names: list[str] | set[str] | None = None,
alias_ids: list[int] | set[int] | None = None,
) -> None:
"""Edit a Tag in the Library."""
self.add_tag(tag, subtag_ids, alias_names, alias_ids)

View File

@@ -3,7 +3,7 @@ from typing import TYPE_CHECKING
from sqlalchemy import and_, distinct, func, or_, select
from sqlalchemy.orm import Session
from sqlalchemy.sql.expression import BinaryExpression, ColumnExpressionArgument
from src.core.media_types import MediaCategories
from src.core.media_types import FILETYPE_EQUIVALENTS, MediaCategories
from src.core.query_lang import BaseVisitor
from src.core.query_lang.ast import AST, ANDList, Constraint, ConstraintType, Not, ORList, Property
@@ -17,6 +17,13 @@ else:
Library = None # don't import .library because of circular imports
def get_filetype_equivalency_list(item: str) -> list[str] | set[str]:
for s in FILETYPE_EQUIVALENTS:
if item in s:
return s
return [item]
class SQLBoolExpressionBuilder(BaseVisitor[ColumnExpressionArgument]):
def __init__(self, lib: Library) -> None:
super().__init__()
@@ -73,7 +80,9 @@ class SQLBoolExpressionBuilder(BaseVisitor[ColumnExpressionArgument]):
break
return Entry.suffix.in_(map(lambda x: x.replace(".", ""), extensions))
elif node.type == ConstraintType.FileType:
return Entry.suffix.ilike(node.value)
return or_(
*[Entry.suffix.ilike(ft) for ft in get_filetype_equivalency_list(node.value)]
)
elif node.type == ConstraintType.Special: # noqa: SIM102 unnecessary once there is a second special constraint
if node.value.lower() == "untagged":
return ~Entry.id.in_(

View File

@@ -10,6 +10,8 @@ from pathlib import Path
logging.basicConfig(format="%(message)s", level=logging.INFO)
FILETYPE_EQUIVALENTS = [set(["jpg", "jpeg"])]
class MediaType(str, Enum):
"""Names of media types."""

View File

@@ -3,45 +3,60 @@
# Created for TagStudio: https://github.com/CyanVoxel/TagStudio
import math
import sys
from typing import cast
import structlog
from PySide6 import QtCore
from PySide6.QtCore import Qt, Signal
from PySide6.QtGui import (
QAction,
)
from PySide6.QtWidgets import (
QApplication,
QComboBox,
QFrame,
QLabel,
QLineEdit,
QPushButton,
QScrollArea,
QTableWidget,
QVBoxLayout,
QWidget,
)
from src.core.library import Library, Tag
from src.core.library.alchemy.enums import TagColor
from src.core.palette import ColorType, UiColor, get_tag_color, get_ui_color
from src.qt.flowlayout import FlowLayout
from src.qt.modals.tag_search import TagSearchPanel
from src.qt.widgets.panel import PanelModal, PanelWidget
from src.qt.widgets.tag import TagAliasWidget, TagWidget
from src.qt.widgets.tag import TagWidget
from ..translations import Translations
logger = structlog.get_logger(__name__)
class CustomTableItem(QLineEdit):
def __init__(self, text, on_return, on_backspace, parent=None):
super().__init__(parent)
self.setText(text)
self.on_return = on_return
self.on_backspace = on_backspace
def set_id(self, id):
self.id = id
def keyPressEvent(self, event): # noqa: N802
if event.key() == Qt.Key.Key_Return or event.key() == Qt.Key.Key_Enter:
self.on_return()
elif event.key() == Qt.Key.Key_Backspace and self.text().strip() == "":
self.on_backspace()
else:
super().keyPressEvent(event)
class BuildTagPanel(PanelWidget):
on_edit = Signal(Tag)
def __init__(self, library: Library, tag: Tag | None = None):
super().__init__()
self.lib = library
# self.callback = callback
# self.tag_id = tag_id
self.setMinimumSize(300, 400)
self.root_layout = QVBoxLayout(self)
@@ -90,43 +105,16 @@ class BuildTagPanel(PanelWidget):
Translations.translate_qobject(self.aliases_title, "tag.aliases")
self.aliases_layout.addWidget(self.aliases_title)
self.aliases_flow_widget = QWidget()
self.aliases_flow_layout = FlowLayout(self.aliases_flow_widget)
self.aliases_flow_layout.setContentsMargins(0, 0, 0, 0)
self.aliases_flow_layout.enable_grid_optimizations(value=False)
self.aliases_table = QTableWidget(0, 2)
self.aliases_table.horizontalHeader().setVisible(False)
self.aliases_table.verticalHeader().setVisible(False)
self.aliases_table.horizontalHeader().setStretchLastSection(True)
self.aliases_table.setColumnWidth(0, 35)
self.alias_add_button = QPushButton()
self.alias_add_button.setMinimumSize(23, 23)
self.alias_add_button.setMaximumSize(23, 23)
self.alias_add_button.setText("+")
self.alias_add_button.setToolTip("CTRL + A")
self.alias_add_button.setShortcut(
QtCore.QKeyCombination(
QtCore.Qt.KeyboardModifier(QtCore.Qt.KeyboardModifier.ControlModifier),
QtCore.Qt.Key.Key_A,
)
)
self.alias_add_button.setStyleSheet(
f"QPushButton{{"
f"background: #1e1e1e;"
f"color: #FFFFFF;"
f"font-weight: bold;"
f"border-color: #333333;"
f"border-radius: 6px;"
f"border-style:solid;"
f"border-width:{math.ceil(self.devicePixelRatio())}px;"
f"padding-bottom: 5px;"
f"font-size: 20px;"
f"}}"
f"QPushButton::hover"
f"{{"
f"border-color: #CCCCCC;"
f"background: #555555;"
f"}}"
)
self.alias_add_button.clicked.connect(lambda: self.add_alias_callback())
self.aliases_flow_layout.addWidget(self.alias_add_button)
self.alias_add_button.clicked.connect(self.add_alias_callback)
# Subtags ------------------------------------------------------------
@@ -141,42 +129,25 @@ class BuildTagPanel(PanelWidget):
Translations.translate_qobject(self.subtags_title, "tag.parent_tags")
self.subtags_layout.addWidget(self.subtags_title)
self.subtag_flow_widget = QWidget()
self.subtag_flow_layout = FlowLayout(self.subtag_flow_widget)
self.subtag_flow_layout.setContentsMargins(0, 0, 0, 0)
self.subtag_flow_layout.enable_grid_optimizations(value=False)
self.scroll_contents = QWidget()
self.subtags_scroll_layout = QVBoxLayout(self.scroll_contents)
self.subtags_scroll_layout.setContentsMargins(6, 0, 6, 0)
self.subtags_scroll_layout.setAlignment(Qt.AlignmentFlag.AlignTop)
self.scroll_area = QScrollArea()
# self.scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOn)
self.scroll_area.setWidgetResizable(True)
self.scroll_area.setFrameShadow(QFrame.Shadow.Plain)
self.scroll_area.setFrameShape(QFrame.Shape.NoFrame)
self.scroll_area.setWidget(self.scroll_contents)
# self.scroll_area.setMinimumHeight(60)
self.subtags_layout.addWidget(self.scroll_area)
self.subtags_add_button = QPushButton()
self.subtags_add_button.setCursor(Qt.CursorShape.PointingHandCursor)
self.subtags_add_button.setText("+")
self.subtags_add_button.setToolTip("CTRL + P")
self.subtags_add_button.setMinimumSize(23, 23)
self.subtags_add_button.setMaximumSize(23, 23)
self.subtags_add_button.setShortcut(
QtCore.QKeyCombination(
QtCore.Qt.KeyboardModifier(QtCore.Qt.KeyboardModifier.ControlModifier),
QtCore.Qt.Key.Key_P,
)
)
self.subtags_add_button.setStyleSheet(
f"QPushButton{{"
f"background: #1e1e1e;"
f"color: #FFFFFF;"
f"font-weight: bold;"
f"border-color: #333333;"
f"border-radius: 6px;"
f"border-style:solid;"
f"border-width:{math.ceil(self.devicePixelRatio())}px;"
f"padding-bottom: 5px;"
f"font-size: 20px;"
f"}}"
f"QPushButton::hover"
f"{{"
f"border-color: #CCCCCC;"
f"background: #555555;"
f"}}"
)
self.subtag_flow_layout.addWidget(self.subtags_add_button)
self.subtags_layout.addWidget(self.subtags_add_button)
exclude_ids: list[int] = list()
if tag is not None:
@@ -188,11 +159,6 @@ class BuildTagPanel(PanelWidget):
Translations.translate_with_setter(self.add_tag_modal.setTitle, "tag.parent_tags.add")
Translations.translate_with_setter(self.add_tag_modal.setWindowTitle, "tag.parent_tags.add")
self.subtags_add_button.clicked.connect(self.add_tag_modal.show)
# self.subtags_layout.addWidget(self.subtags_add_button)
# self.subtags_field = TagBoxWidget()
# self.subtags_field.setMinimumHeight(60)
# self.subtags_layout.addWidget(self.subtags_field)
# Shorthand ------------------------------------------------------------
self.color_widget = QWidget()
@@ -224,66 +190,59 @@ class BuildTagPanel(PanelWidget):
)
)
self.color_layout.addWidget(self.color_field)
remove_selected_alias_action = QAction(self)
Translations.translate_qobject(remove_selected_alias_action, "tag.remove_alias")
remove_selected_alias_action.triggered.connect(self.remove_selected_alias)
remove_selected_alias_action.setShortcut(
QtCore.QKeyCombination(
QtCore.Qt.KeyboardModifier(QtCore.Qt.KeyboardModifier.ControlModifier),
QtCore.Qt.Key.Key_D,
)
)
self.addAction(remove_selected_alias_action)
# Add Widgets to Layout ================================================
self.root_layout.addWidget(self.name_widget)
self.root_layout.addWidget(self.shorthand_widget)
self.root_layout.addWidget(self.aliases_widget)
self.root_layout.addWidget(self.aliases_flow_widget)
self.root_layout.addWidget(self.aliases_table)
self.root_layout.addWidget(self.alias_add_button)
self.root_layout.addWidget(self.subtags_widget)
self.root_layout.addWidget(self.subtag_flow_widget)
self.root_layout.addWidget(self.color_widget)
# self.parent().done.connect(self.update_tag)
self.subtag_ids: set[int] = set()
self.alias_ids: set[int] = set()
self.alias_names: set[str] = set()
self.new_alias_names: dict = dict()
self.subtag_ids: list[int] = []
self.alias_ids: list[int] = []
self.alias_names: list[str] = []
self.new_alias_names: dict = {}
self.new_item_id = sys.maxsize
self.set_tag(tag or Tag(name=Translations["tag.new"]))
if tag is None:
self.name_field.selectAll()
def keyPressEvent(self, event): # noqa: N802
if event.key() == Qt.Key_Return or event.key() == Qt.Key_Enter: # type: ignore
focused_widget = QApplication.focusWidget()
if isinstance(focused_widget.parent(), TagAliasWidget):
self.add_alias_callback()
def remove_selected_alias(self):
count = self.aliases_flow_layout.count()
if count <= 0:
return
def backspace(self):
focused_widget = QApplication.focusWidget()
row = self.aliases_table.rowCount()
if focused_widget is None:
if isinstance(focused_widget, CustomTableItem) is False:
return
remove_row = 0
for i in range(0, row):
item = self.aliases_table.cellWidget(i, 1)
if (
isinstance(item, CustomTableItem)
and cast(CustomTableItem, item).id == cast(CustomTableItem, focused_widget).id
):
cast(QPushButton, self.aliases_table.cellWidget(i, 0)).click()
remove_row = i
break
if self.aliases_table.rowCount() <= 0:
return
if isinstance(focused_widget.parent(), TagAliasWidget):
cast(TagAliasWidget, focused_widget.parent()).on_remove.emit()
if remove_row == 0:
remove_row = 1
count = self.aliases_flow_layout.count()
if count > 1:
cast(
TagAliasWidget, self.aliases_flow_layout.itemAt(count - 2).widget()
).text_field.setFocus()
else:
self.alias_add_button.setFocus()
self.aliases_table.cellWidget(remove_row - 1, 1).setFocus()
def enter(self):
focused_widget = QApplication.focusWidget()
if isinstance(focused_widget, CustomTableItem):
self.add_alias_callback()
def add_subtag_callback(self, tag_id: int):
logger.info("add_subtag_callback", tag_id=tag_id)
self.subtag_ids.add(tag_id)
self.subtag_ids.append(tag_id)
self.set_subtags()
def remove_subtag_callback(self, tag_id: int):
@@ -293,76 +252,71 @@ class BuildTagPanel(PanelWidget):
def add_alias_callback(self):
logger.info("add_alias_callback")
# bug passing in the text for a here means when the text changes
# the remove callback uses what a whas initialy assigned
new_field = TagAliasWidget()
id = new_field.__hash__()
new_field.id = id
new_field.on_remove.connect(lambda a="": self.remove_alias_callback(a, id))
new_field.setMaximumHeight(25)
new_field.setMinimumHeight(25)
id = self.new_item_id
self.alias_ids.add(id)
self.alias_ids.append(id)
self.new_alias_names[id] = ""
self.aliases_flow_layout.addWidget(new_field)
new_field.text_field.setFocus()
self.aliases_flow_layout.addWidget(self.alias_add_button)
self.new_item_id -= 1
self._set_aliases()
row = self.aliases_table.rowCount() - 1
item = self.aliases_table.cellWidget(row, 1)
item.setFocus()
def remove_alias_callback(self, alias_name: str, alias_id: int | None = None):
logger.info("remove_alias_callback")
self.alias_ids.remove(alias_id)
self._set_aliases()
def set_subtags(self):
while self.subtag_flow_layout.itemAt(1):
self.subtag_flow_layout.takeAt(0).widget().deleteLater()
while self.subtags_scroll_layout.itemAt(0):
self.subtags_scroll_layout.takeAt(0).widget().deleteLater()
c = QWidget()
layout = QVBoxLayout(c)
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(3)
for tag_id in self.subtag_ids:
tag = self.lib.get_tag(tag_id)
tw = TagWidget(tag, has_edit=False, has_remove=True)
tw.on_remove.connect(lambda t=tag_id: self.remove_subtag_callback(t))
self.subtag_flow_layout.addWidget(tw)
self.subtag_flow_layout.addWidget(self.subtags_add_button)
layout.addWidget(tw)
self.subtags_scroll_layout.addWidget(c)
def add_aliases(self):
fields: set[TagAliasWidget] = set()
for i in range(0, self.aliases_flow_layout.count() - 1):
widget = self.aliases_flow_layout.itemAt(i).widget()
names: set[str] = set()
for i in range(0, self.aliases_table.rowCount()):
widget = self.aliases_table.cellWidget(i, 1)
if not isinstance(widget, TagAliasWidget):
return
names.add(cast(CustomTableItem, widget).text())
field: TagAliasWidget = cast(TagAliasWidget, widget)
fields.add(field)
remove: set[str] = set(self.alias_names) - names
remove: set[str] = self.alias_names - set([a.text_field.text() for a in fields])
self.alias_names = list(set(self.alias_names) - remove)
self.alias_names = self.alias_names - remove
for field in fields:
for name in names:
# add new aliases
if field.text_field.text() != "":
self.alias_names.add(field.text_field.text())
if name.strip() != "" and name not in set(self.alias_names):
self.alias_names.append(name)
elif name.strip() == "" and name in set(self.alias_names):
self.alias_names.remove(name)
def _update_new_alias_name_dict(self):
for i in range(0, self.aliases_flow_layout.count() - 1):
widget = self.aliases_flow_layout.itemAt(i).widget()
if not isinstance(widget, TagAliasWidget):
return
field: TagAliasWidget = cast(TagAliasWidget, widget)
text_field_text = field.text_field.text()
self.new_alias_names[field.id] = text_field_text
row = self.aliases_table.rowCount()
logger.info(row)
for i in range(0, self.aliases_table.rowCount()):
widget = self.aliases_table.cellWidget(i, 1)
self.new_alias_names[widget.id] = widget.text() # type: ignore
def _set_aliases(self):
self._update_new_alias_name_dict()
while self.aliases_flow_layout.itemAt(1):
self.aliases_flow_layout.takeAt(0).widget().deleteLater()
while self.aliases_table.rowCount() > 0:
self.aliases_table.removeRow(0)
self.alias_names.clear()
@@ -371,43 +325,45 @@ class BuildTagPanel(PanelWidget):
alias_name = alias.name if alias else self.new_alias_names[alias_id]
new_field = TagAliasWidget(
alias_id,
alias_name,
lambda a=alias_name, id=alias_id: self.remove_alias_callback(a, id),
)
new_field.setMaximumHeight(25)
new_field.setMinimumHeight(25)
self.aliases_flow_layout.addWidget(new_field)
self.alias_names.add(alias_name)
# handel when an alias name changes
if alias_id in self.new_alias_names:
alias_name = self.new_alias_names[alias_id]
self.aliases_flow_layout.addWidget(self.alias_add_button)
self.alias_names.append(alias_name)
remove_btn = QPushButton("-")
remove_btn.clicked.connect(
lambda a=alias_name, id=alias_id: self.remove_alias_callback(a, id)
)
row = self.aliases_table.rowCount()
new_item = CustomTableItem(alias_name, self.enter, self.backspace)
new_item.set_id(alias_id)
new_item.editingFinished.connect(lambda item=new_item: self._alias_name_change(item))
self.aliases_table.insertRow(row)
self.aliases_table.setCellWidget(row, 1, new_item)
self.aliases_table.setCellWidget(row, 0, remove_btn)
def _alias_name_change(self, item: CustomTableItem):
self.new_alias_names[item.id] = item.text()
def set_tag(self, tag: Tag):
self.tag = tag
self.tag = tag
logger.info("setting tag", tag=tag)
self.name_field.setText(tag.name)
self.shorthand_field.setText(tag.shorthand or "")
for alias_id in tag.alias_ids:
self.alias_ids.add(alias_id)
self.alias_ids.append(alias_id)
self._set_aliases()
for subtag in tag.subtag_ids:
self.subtag_ids.add(subtag)
for alias_id in tag.alias_ids:
self.alias_ids.add(alias_id)
self._set_aliases()
for subtag in tag.subtag_ids:
self.subtag_ids.add(subtag)
self.subtag_ids.append(subtag)
self.set_subtags()

View File

@@ -167,7 +167,9 @@ class TagDatabasePanel(PanelWidget):
self.edit_modal.show()
def edit_tag_callback(self, btp: BuildTagPanel):
self.lib.update_tag(btp.build_tag(), btp.subtag_ids, btp.alias_names, btp.alias_ids)
self.lib.update_tag(
btp.build_tag(), set(btp.subtag_ids), set(btp.alias_names), set(btp.alias_ids)
)
self.update_tags(self.search_field.text())
def showEvent(self, event: QShowEvent) -> None: # noqa N802

View File

@@ -673,7 +673,10 @@ class QtDriver(DriverMixin, QObject):
self.modal.saved.connect(
lambda: (
self.lib.add_tag(
panel.build_tag(), panel.subtag_ids, panel.alias_names, panel.alias_ids
panel.build_tag(),
set(panel.subtag_ids),
set(panel.alias_names),
set(panel.alias_ids),
),
self.modal.hide(),
)

View File

@@ -1,9 +1,5 @@
from typing import cast
from PySide6.QtWidgets import QApplication, QMainWindow
from src.core.library.alchemy.models import Tag
from src.qt.modals.build_tag import BuildTagPanel
from src.qt.widgets.tag import TagAliasWidget
def test_build_tag_panel_add_sub_tag_callback(library, generate_tag):
@@ -43,29 +39,6 @@ import os
os.environ["QT_QPA_PLATFORM"] = "offscreen"
def test_build_tag_panel_remove_selected_alias(library, generate_tag):
app = QApplication.instance() or QApplication([])
window = QMainWindow()
parent_tag = library.add_tag(generate_tag("xxx", id=123))
panel = BuildTagPanel(library, parent_tag)
panel.setParent(window)
panel.add_alias_callback()
window.show()
assert panel.aliases_flow_layout.count() == 2
alias_widget = panel.aliases_flow_layout.itemAt(0).widget()
alias_widget.text_field.setFocus()
app.processEvents()
panel.remove_selected_alias()
assert panel.aliases_flow_layout.count() == 1
def test_build_tag_panel_add_alias_callback(library, generate_tag):
tag = library.add_tag(generate_tag("xxx", id=123))
assert tag
@@ -74,7 +47,7 @@ def test_build_tag_panel_add_alias_callback(library, generate_tag):
panel.add_alias_callback()
assert panel.aliases_flow_layout.count() == 2
assert panel.aliases_table.rowCount() == 1
def test_build_tag_panel_remove_alias_callback(library, generate_tag):
@@ -112,7 +85,7 @@ def test_build_tag_panel_set_subtags(library, generate_tag):
panel: BuildTagPanel = BuildTagPanel(library, child)
assert len(panel.subtag_ids) == 1
assert panel.subtag_flow_layout.count() == 2
assert panel.subtags_scroll_layout.count() == 1
def test_build_tag_panel_add_aliases(library, generate_tag):
@@ -128,19 +101,19 @@ def test_build_tag_panel_add_aliases(library, generate_tag):
panel: BuildTagPanel = BuildTagPanel(library, tag)
widget = panel.aliases_flow_layout.itemAt(0).widget()
widget = panel.aliases_table.cellWidget(0, 1)
alias_names: set[str] = set()
alias_names.add(cast(TagAliasWidget, widget).text_field.text())
alias_names.add(widget.text())
widget = panel.aliases_flow_layout.itemAt(1).widget()
alias_names.add(cast(TagAliasWidget, widget).text_field.text())
widget = panel.aliases_table.cellWidget(1, 1)
alias_names.add(widget.text())
assert "alias" in alias_names
assert "alias_2" in alias_names
old_text = cast(TagAliasWidget, widget).text_field.text()
cast(TagAliasWidget, widget).text_field.setText("alias_update")
old_text = widget.text()
widget.setText("alias_update")
panel.add_aliases()
@@ -161,7 +134,7 @@ def test_build_tag_panel_set_aliases(library, generate_tag):
panel: BuildTagPanel = BuildTagPanel(library, tag)
assert panel.aliases_flow_layout.count() == 2
assert panel.aliases_table.rowCount() == 1
assert len(panel.alias_names) == 1
assert len(panel.alias_ids) == 1