mirror of
https://github.com/fergalmoran/picard.git
synced 2025-12-28 12:17:37 +00:00
Merge pull request #1379 from phw/PICARD-1677-ignore-tags
PICARD-1677: Tags can be ignored for metadata comparisson
This commit is contained in:
@@ -545,15 +545,17 @@ class File(QtCore.QObject, Item):
|
||||
names = set(new_metadata.keys())
|
||||
names.update(self.orig_metadata.keys())
|
||||
clear_existing_tags = config.setting["clear_existing_tags"]
|
||||
ignored_tags = config.setting["compare_ignore_tags"]
|
||||
for name in names:
|
||||
if not name.startswith('~') and self.supports_tag(name):
|
||||
if (not name.startswith('~') and self.supports_tag(name)
|
||||
and name not in ignored_tags):
|
||||
new_values = new_metadata.getall(name)
|
||||
if not (new_values or clear_existing_tags
|
||||
or name in new_metadata.deleted_tags):
|
||||
continue
|
||||
orig_values = self.orig_metadata.getall(name)
|
||||
if orig_values != new_values:
|
||||
self.similarity = self.orig_metadata.compare(new_metadata)
|
||||
self.similarity = self.orig_metadata.compare(new_metadata, ignored_tags)
|
||||
if self.state == File.NORMAL:
|
||||
self.state = File.CHANGED
|
||||
break
|
||||
|
||||
@@ -102,14 +102,18 @@ class Metadata(MutableMapping):
|
||||
return (1.0 - min(abs(a - b),
|
||||
LENGTH_SCORE_THRES_MS) / float(LENGTH_SCORE_THRES_MS))
|
||||
|
||||
def compare(self, other):
|
||||
def compare(self, other, ignored=None):
|
||||
parts = []
|
||||
if ignored is None:
|
||||
ignored = []
|
||||
|
||||
if self.length and other.length:
|
||||
if self.length and other.length and '~length' not in ignored:
|
||||
score = self.length_score(self.length, other.length)
|
||||
parts.append((score, 8))
|
||||
|
||||
for name, weight in self.__weights:
|
||||
if name in ignored:
|
||||
continue
|
||||
a = self[name]
|
||||
b = other[name]
|
||||
if a and b:
|
||||
|
||||
@@ -43,6 +43,7 @@ class AdvancedOptionsPage(OptionsPage):
|
||||
config.BoolOption("setting", "completeness_ignore_pregap", False),
|
||||
config.BoolOption("setting", "completeness_ignore_data", False),
|
||||
config.BoolOption("setting", "completeness_ignore_silence", False),
|
||||
config.ListOption("setting", "compare_ignore_tags", []),
|
||||
]
|
||||
|
||||
def __init__(self, parent=None):
|
||||
@@ -60,6 +61,7 @@ class AdvancedOptionsPage(OptionsPage):
|
||||
self.ui.completeness_ignore_pregap.setChecked(config.setting["completeness_ignore_pregap"])
|
||||
self.ui.completeness_ignore_data.setChecked(config.setting["completeness_ignore_data"])
|
||||
self.ui.completeness_ignore_silence.setChecked(config.setting["completeness_ignore_silence"])
|
||||
self.ui.compare_ignore_tags.update(config.setting["compare_ignore_tags"])
|
||||
|
||||
def save(self):
|
||||
config.setting["ignore_regex"] = self.ui.ignore_regex.text()
|
||||
@@ -70,6 +72,13 @@ class AdvancedOptionsPage(OptionsPage):
|
||||
config.setting["completeness_ignore_pregap"] = self.ui.completeness_ignore_pregap.isChecked()
|
||||
config.setting["completeness_ignore_data"] = self.ui.completeness_ignore_data.isChecked()
|
||||
config.setting["completeness_ignore_silence"] = self.ui.completeness_ignore_silence.isChecked()
|
||||
tags = list(self.ui.compare_ignore_tags.tags)
|
||||
if tags != config.setting["compare_ignore_tags"]:
|
||||
config.setting["compare_ignore_tags"] = tags
|
||||
|
||||
def restore_defaults(self):
|
||||
self.ui.compare_ignore_tags.clear()
|
||||
super().restore_defaults()
|
||||
|
||||
|
||||
register_options_page(AdvancedOptionsPage)
|
||||
|
||||
@@ -51,9 +51,6 @@ class InterfaceTopTagsOptionsPage(OptionsPage):
|
||||
super().__init__(parent)
|
||||
self.ui = Ui_InterfaceTopTagsOptionsPage()
|
||||
self.ui.setupUi(self)
|
||||
selection = self.ui.top_tags_list.selectionModel()
|
||||
selection.selectionChanged.connect(self.on_selection_changed)
|
||||
self.on_selection_changed([], [])
|
||||
|
||||
def load(self):
|
||||
tags = config.setting["metadatabox_top_tags"]
|
||||
@@ -69,11 +66,5 @@ class InterfaceTopTagsOptionsPage(OptionsPage):
|
||||
self.ui.top_tags_list.clear()
|
||||
super().restore_defaults()
|
||||
|
||||
def on_selection_changed(self, selected, deselected):
|
||||
buttons_enabled = len(self.ui.top_tags_list.selectedIndexes()) > 0
|
||||
self.ui.tags_remove_btn.setEnabled(buttons_enabled)
|
||||
self.ui.tags_move_up_btn.setEnabled(buttons_enabled)
|
||||
self.ui.tags_move_down_btn.setEnabled(buttons_enabled)
|
||||
|
||||
|
||||
register_options_page(InterfaceTopTagsOptionsPage)
|
||||
|
||||
@@ -86,8 +86,17 @@ class Ui_AdvancedOptionsPage(object):
|
||||
self.completeness_ignore_silence.setObjectName("completeness_ignore_silence")
|
||||
self.verticalLayout_2.addWidget(self.completeness_ignore_silence)
|
||||
self.vboxlayout.addWidget(self.groupBox_completeness)
|
||||
spacerItem = QtWidgets.QSpacerItem(181, 21, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
|
||||
self.vboxlayout.addItem(spacerItem)
|
||||
self.compare_ignore_tags_label = QtWidgets.QLabel(AdvancedOptionsPage)
|
||||
self.compare_ignore_tags_label.setObjectName("compare_ignore_tags_label")
|
||||
self.vboxlayout.addWidget(self.compare_ignore_tags_label)
|
||||
self.compare_ignore_tags = TagListEditor(AdvancedOptionsPage)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Expanding)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.compare_ignore_tags.sizePolicy().hasHeightForWidth())
|
||||
self.compare_ignore_tags.setSizePolicy(sizePolicy)
|
||||
self.compare_ignore_tags.setObjectName("compare_ignore_tags")
|
||||
self.vboxlayout.addWidget(self.compare_ignore_tags)
|
||||
|
||||
self.retranslateUi(AdvancedOptionsPage)
|
||||
QtCore.QMetaObject.connectSlotsByName(AdvancedOptionsPage)
|
||||
@@ -111,3 +120,5 @@ class Ui_AdvancedOptionsPage(object):
|
||||
self.completeness_ignore_pregap.setText(_("Pregap tracks"))
|
||||
self.completeness_ignore_data.setText(_("Data tracks"))
|
||||
self.completeness_ignore_silence.setText(_("Silent tracks"))
|
||||
self.compare_ignore_tags_label.setText(_("Tags to ignore for comparisson:"))
|
||||
from picard.ui.widgets.taglisteditor import TagListEditor
|
||||
|
||||
@@ -10,7 +10,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
class Ui_InterfaceTopTagsOptionsPage(object):
|
||||
def setupUi(self, InterfaceTopTagsOptionsPage):
|
||||
InterfaceTopTagsOptionsPage.setObjectName("InterfaceTopTagsOptionsPage")
|
||||
InterfaceTopTagsOptionsPage.resize(893, 698)
|
||||
InterfaceTopTagsOptionsPage.resize(418, 310)
|
||||
self.vboxlayout = QtWidgets.QVBoxLayout(InterfaceTopTagsOptionsPage)
|
||||
self.vboxlayout.setContentsMargins(9, 9, 9, 9)
|
||||
self.vboxlayout.setSpacing(6)
|
||||
@@ -18,65 +18,19 @@ class Ui_InterfaceTopTagsOptionsPage(object):
|
||||
self.label = QtWidgets.QLabel(InterfaceTopTagsOptionsPage)
|
||||
self.label.setObjectName("label")
|
||||
self.vboxlayout.addWidget(self.label)
|
||||
self.horizontalLayout = QtWidgets.QHBoxLayout()
|
||||
self.horizontalLayout.setObjectName("horizontalLayout")
|
||||
self.verticalLayout_2 = QtWidgets.QVBoxLayout()
|
||||
self.verticalLayout_2.setObjectName("verticalLayout_2")
|
||||
self.top_tags_list = EditableTagListView(InterfaceTopTagsOptionsPage)
|
||||
self.top_tags_list.setDragEnabled(True)
|
||||
self.top_tags_list.setDragDropMode(QtWidgets.QAbstractItemView.InternalMove)
|
||||
self.top_tags_list = TagListEditor(InterfaceTopTagsOptionsPage)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Expanding)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.top_tags_list.sizePolicy().hasHeightForWidth())
|
||||
self.top_tags_list.setSizePolicy(sizePolicy)
|
||||
self.top_tags_list.setObjectName("top_tags_list")
|
||||
self.verticalLayout_2.addWidget(self.top_tags_list)
|
||||
self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
|
||||
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
|
||||
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
|
||||
self.horizontalLayout_2.addItem(spacerItem)
|
||||
self.tags_remove_btn = QtWidgets.QPushButton(InterfaceTopTagsOptionsPage)
|
||||
self.tags_remove_btn.setObjectName("tags_remove_btn")
|
||||
self.horizontalLayout_2.addWidget(self.tags_remove_btn)
|
||||
self.tags_add_btn = QtWidgets.QPushButton(InterfaceTopTagsOptionsPage)
|
||||
self.tags_add_btn.setAccessibleName("")
|
||||
self.tags_add_btn.setObjectName("tags_add_btn")
|
||||
self.horizontalLayout_2.addWidget(self.tags_add_btn)
|
||||
self.verticalLayout_2.addLayout(self.horizontalLayout_2)
|
||||
self.horizontalLayout.addLayout(self.verticalLayout_2)
|
||||
self.verticalLayout = QtWidgets.QVBoxLayout()
|
||||
self.verticalLayout.setObjectName("verticalLayout")
|
||||
spacerItem1 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
|
||||
self.verticalLayout.addItem(spacerItem1)
|
||||
self.tags_move_up_btn = QtWidgets.QPushButton(InterfaceTopTagsOptionsPage)
|
||||
self.tags_move_up_btn.setText("")
|
||||
icon = QtGui.QIcon.fromTheme("go-up")
|
||||
self.tags_move_up_btn.setIcon(icon)
|
||||
self.tags_move_up_btn.setObjectName("tags_move_up_btn")
|
||||
self.verticalLayout.addWidget(self.tags_move_up_btn)
|
||||
self.tags_move_down_btn = QtWidgets.QPushButton(InterfaceTopTagsOptionsPage)
|
||||
self.tags_move_down_btn.setText("")
|
||||
icon = QtGui.QIcon.fromTheme("go-down")
|
||||
self.tags_move_down_btn.setIcon(icon)
|
||||
self.tags_move_down_btn.setObjectName("tags_move_down_btn")
|
||||
self.verticalLayout.addWidget(self.tags_move_down_btn)
|
||||
spacerItem2 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
|
||||
self.verticalLayout.addItem(spacerItem2)
|
||||
self.horizontalLayout.addLayout(self.verticalLayout)
|
||||
self.vboxlayout.addLayout(self.horizontalLayout)
|
||||
self.vboxlayout.addWidget(self.top_tags_list)
|
||||
|
||||
self.retranslateUi(InterfaceTopTagsOptionsPage)
|
||||
self.tags_add_btn.clicked.connect(self.top_tags_list.add_empty_row)
|
||||
self.tags_remove_btn.clicked.connect(self.top_tags_list.remove_selected_rows)
|
||||
self.tags_move_up_btn.clicked.connect(self.top_tags_list.move_selected_rows_up)
|
||||
self.tags_move_down_btn.clicked.connect(self.top_tags_list.move_selected_rows_down)
|
||||
QtCore.QMetaObject.connectSlotsByName(InterfaceTopTagsOptionsPage)
|
||||
|
||||
def retranslateUi(self, InterfaceTopTagsOptionsPage):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
self.label.setText(_("Show the below tags above all other tags in the metadata view"))
|
||||
self.tags_remove_btn.setToolTip(_("Remove selected tags"))
|
||||
self.tags_remove_btn.setAccessibleName(_("Remove selected tags"))
|
||||
self.tags_remove_btn.setText(_("Remove tags"))
|
||||
self.tags_add_btn.setText(_("Add new tag"))
|
||||
self.tags_move_up_btn.setToolTip(_("Move tag up"))
|
||||
self.tags_move_up_btn.setAccessibleName(_("Move tag up"))
|
||||
self.tags_move_down_btn.setToolTip(_("Move tag down"))
|
||||
self.tags_move_down_btn.setAccessibleName(_("Move tag down"))
|
||||
from picard.ui.taglistview import EditableTagListView
|
||||
from picard.ui.widgets.taglisteditor import TagListEditor
|
||||
|
||||
73
picard/ui/ui_widget_taglisteditor.py
Normal file
73
picard/ui/ui_widget_taglisteditor.py
Normal file
@@ -0,0 +1,73 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Automatically generated - don't edit.
|
||||
# Use `python setup.py build_ui` to update it.
|
||||
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_TagListEditor(object):
|
||||
def setupUi(self, TagListEditor):
|
||||
TagListEditor.setObjectName("TagListEditor")
|
||||
TagListEditor.resize(400, 300)
|
||||
self.horizontalLayout = QtWidgets.QHBoxLayout(TagListEditor)
|
||||
self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
|
||||
self.horizontalLayout.setSpacing(6)
|
||||
self.horizontalLayout.setObjectName("horizontalLayout")
|
||||
self.verticalLayout = QtWidgets.QVBoxLayout()
|
||||
self.verticalLayout.setObjectName("verticalLayout")
|
||||
self.tag_list_view = EditableListView(TagListEditor)
|
||||
self.tag_list_view.setObjectName("tag_list_view")
|
||||
self.verticalLayout.addWidget(self.tag_list_view)
|
||||
self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
|
||||
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
|
||||
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
|
||||
self.horizontalLayout_2.addItem(spacerItem)
|
||||
self.tags_remove_btn = QtWidgets.QPushButton(TagListEditor)
|
||||
self.tags_remove_btn.setObjectName("tags_remove_btn")
|
||||
self.horizontalLayout_2.addWidget(self.tags_remove_btn)
|
||||
self.tags_add_btn = QtWidgets.QPushButton(TagListEditor)
|
||||
self.tags_add_btn.setObjectName("tags_add_btn")
|
||||
self.horizontalLayout_2.addWidget(self.tags_add_btn)
|
||||
self.verticalLayout.addLayout(self.horizontalLayout_2)
|
||||
self.horizontalLayout.addLayout(self.verticalLayout)
|
||||
self.verticalLayout_2 = QtWidgets.QVBoxLayout()
|
||||
self.verticalLayout_2.setObjectName("verticalLayout_2")
|
||||
spacerItem1 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
|
||||
self.verticalLayout_2.addItem(spacerItem1)
|
||||
self.tags_move_up_btn = QtWidgets.QPushButton(TagListEditor)
|
||||
self.tags_move_up_btn.setText("")
|
||||
icon = QtGui.QIcon.fromTheme("go-up")
|
||||
self.tags_move_up_btn.setIcon(icon)
|
||||
self.tags_move_up_btn.setObjectName("tags_move_up_btn")
|
||||
self.verticalLayout_2.addWidget(self.tags_move_up_btn)
|
||||
self.tags_move_down_btn = QtWidgets.QPushButton(TagListEditor)
|
||||
self.tags_move_down_btn.setText("")
|
||||
icon = QtGui.QIcon.fromTheme("go-down")
|
||||
self.tags_move_down_btn.setIcon(icon)
|
||||
self.tags_move_down_btn.setObjectName("tags_move_down_btn")
|
||||
self.verticalLayout_2.addWidget(self.tags_move_down_btn)
|
||||
spacerItem2 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
|
||||
self.verticalLayout_2.addItem(spacerItem2)
|
||||
self.horizontalLayout.addLayout(self.verticalLayout_2)
|
||||
|
||||
self.retranslateUi(TagListEditor)
|
||||
self.tags_remove_btn.clicked.connect(self.tag_list_view.remove_selected_rows)
|
||||
self.tags_add_btn.clicked.connect(self.tag_list_view.add_empty_row)
|
||||
self.tags_move_up_btn.clicked.connect(self.tag_list_view.move_selected_rows_up)
|
||||
self.tags_move_down_btn.clicked.connect(self.tag_list_view.move_selected_rows_down)
|
||||
QtCore.QMetaObject.connectSlotsByName(TagListEditor)
|
||||
|
||||
def retranslateUi(self, TagListEditor):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
TagListEditor.setWindowTitle(_("Form"))
|
||||
self.tags_remove_btn.setToolTip(_("Remove selected tags"))
|
||||
self.tags_remove_btn.setAccessibleName(_("Remove selected tags"))
|
||||
self.tags_remove_btn.setText(_("Remove tags"))
|
||||
self.tags_add_btn.setText(_("Add new tag"))
|
||||
self.tags_move_up_btn.setToolTip(_("Move tag up"))
|
||||
self.tags_move_up_btn.setAccessibleName(_("Move tag up"))
|
||||
self.tags_move_down_btn.setToolTip(_("Move tag down"))
|
||||
self.tags_move_down_btn.setAccessibleName(_("Move tag down"))
|
||||
from picard.ui.widgets.editablelistview import EditableListView
|
||||
@@ -22,18 +22,10 @@ from PyQt5 import (
|
||||
QtWidgets,
|
||||
)
|
||||
|
||||
from picard.util.tags import (
|
||||
TAG_NAMES,
|
||||
display_tag_name,
|
||||
)
|
||||
|
||||
|
||||
class EditableTagListView(QtWidgets.QListView):
|
||||
class EditableListView(QtWidgets.QListView):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
model = TagListModel()
|
||||
self.setModel(model)
|
||||
self.setItemDelegate(TagItemDelegate())
|
||||
self.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
|
||||
|
||||
def keyPressEvent(self, event):
|
||||
@@ -60,26 +52,26 @@ class EditableTagListView(QtWidgets.QListView):
|
||||
else:
|
||||
super().closeEditor(editor, hint)
|
||||
|
||||
def add_tag(self, tag=""):
|
||||
def add_item(self, value=""):
|
||||
model = self.model()
|
||||
row = model.rowCount()
|
||||
model.insertRow(row)
|
||||
index = model.createIndex(row, 0)
|
||||
model.setData(index, tag)
|
||||
model.setData(index, value)
|
||||
return index
|
||||
|
||||
def clear(self):
|
||||
self.model().update([])
|
||||
|
||||
def update(self, tags):
|
||||
self.model().update(tags)
|
||||
def update(self, values):
|
||||
self.model().update(values)
|
||||
|
||||
@property
|
||||
def tags(self):
|
||||
return self.model().tags
|
||||
def items(self):
|
||||
return self.model().items
|
||||
|
||||
def add_empty_row(self):
|
||||
index = self.add_tag()
|
||||
index = self.add_item()
|
||||
self.setCurrentIndex(index)
|
||||
self.edit(index)
|
||||
|
||||
@@ -128,20 +120,23 @@ class EditableTagListView(QtWidgets.QListView):
|
||||
selection.setCurrentIndex(new_index, QtCore.QItemSelectionModel.Current)
|
||||
|
||||
|
||||
class TagListModel(QtCore.QAbstractListModel):
|
||||
def __init__(self, tags=None, parent=None):
|
||||
class EditableListModel(QtCore.QAbstractListModel):
|
||||
def __init__(self, items=None, parent=None):
|
||||
super().__init__(parent)
|
||||
self._tags = [(tag, display_tag_name(tag)) for tag in tags or []]
|
||||
self._items = [(item, self.get_display_name(item)) for item in items or []]
|
||||
|
||||
def get_display_name(self, item): # pylint: disable=no-self-use
|
||||
return item
|
||||
|
||||
def rowCount(self, parent=QtCore.QModelIndex()):
|
||||
return len(self._tags)
|
||||
return len(self._items)
|
||||
|
||||
def data(self, index, role=QtCore.Qt.DisplayRole):
|
||||
if not index.isValid() or role not in (QtCore.Qt.DisplayRole, QtCore.Qt.EditRole):
|
||||
return None
|
||||
field = 1 if role == QtCore.Qt.DisplayRole else 0
|
||||
try:
|
||||
return self._tags[index.row()][field]
|
||||
return self._items[index.row()][field]
|
||||
except IndexError:
|
||||
return None
|
||||
|
||||
@@ -151,11 +146,11 @@ class TagListModel(QtCore.QAbstractListModel):
|
||||
i = index.row()
|
||||
try:
|
||||
if role == QtCore.Qt.EditRole:
|
||||
display_name = display_tag_name(value) if value else value
|
||||
self._tags[i] = (value, display_name)
|
||||
display_name = self.get_display_name(value) if value else value
|
||||
self._items[i] = (value, display_name)
|
||||
elif role == QtCore.Qt.DisplayRole:
|
||||
current = self._tags[i]
|
||||
self._tags[i] = (current[0], value)
|
||||
current = self._items[i]
|
||||
self._items[i] = (current[0], value)
|
||||
return True
|
||||
except IndexError:
|
||||
return False
|
||||
@@ -174,13 +169,13 @@ class TagListModel(QtCore.QAbstractListModel):
|
||||
def insertRows(self, row, count, parent=QtCore.QModelIndex()):
|
||||
super().beginInsertRows(parent, row, row + count - 1)
|
||||
for i in range(count):
|
||||
self._tags.insert(row, ("", ""))
|
||||
self._items.insert(row, ("", ""))
|
||||
super().endInsertRows()
|
||||
return True
|
||||
|
||||
def removeRows(self, row, count, parent=QtCore.QModelIndex()):
|
||||
super().beginRemoveRows(parent, row, row + count - 1)
|
||||
self._tags = self._tags[:row] + self._tags[row + count:]
|
||||
self._items = self._items[:row] + self._items[row + count:]
|
||||
super().endRemoveRows()
|
||||
return True
|
||||
|
||||
@@ -192,13 +187,13 @@ class TagListModel(QtCore.QAbstractListModel):
|
||||
def supportedDropActions():
|
||||
return QtCore.Qt.MoveAction
|
||||
|
||||
def update(self, tags):
|
||||
def update(self, items):
|
||||
self.beginResetModel()
|
||||
self._tags = [(tag, display_tag_name(tag)) for tag in tags]
|
||||
self._items = [(item, self.get_display_name(item)) for item in items]
|
||||
self.endResetModel()
|
||||
|
||||
def move_row(self, row, new_row):
|
||||
item = self._tags[row]
|
||||
item = self._items[row]
|
||||
self.removeRow(row)
|
||||
self.insertRow(new_row)
|
||||
index = self.index(new_row, 0)
|
||||
@@ -206,15 +201,18 @@ class TagListModel(QtCore.QAbstractListModel):
|
||||
self.setData(index, item[1], QtCore.Qt.DisplayRole)
|
||||
|
||||
@property
|
||||
def tags(self):
|
||||
return (t[0] for t in self._tags)
|
||||
def items(self):
|
||||
return (t[0] for t in self._items)
|
||||
|
||||
|
||||
class TagItemDelegate(QtWidgets.QItemDelegate):
|
||||
@staticmethod
|
||||
def createEditor(parent, option, index):
|
||||
class AutocompleteItemDelegate(QtWidgets.QItemDelegate):
|
||||
def __init__(self, completions, parent=None):
|
||||
super().__init__(parent)
|
||||
self._completions = completions
|
||||
|
||||
def createEditor(self, parent, option, index):
|
||||
editor = QtWidgets.QLineEdit(parent)
|
||||
completer = QtWidgets.QCompleter(TAG_NAMES.keys(), parent)
|
||||
completer = QtWidgets.QCompleter(self._completions, parent)
|
||||
|
||||
def complete(text):
|
||||
parent.setFocus()
|
||||
68
picard/ui/widgets/taglisteditor.py
Normal file
68
picard/ui/widgets/taglisteditor.py
Normal file
@@ -0,0 +1,68 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Picard, the next-generation MusicBrainz tagger
|
||||
# Copyright (C) 2019 Philipp Wolfer
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
from PyQt5 import QtWidgets
|
||||
|
||||
from picard.util.tags import (
|
||||
TAG_NAMES,
|
||||
display_tag_name,
|
||||
)
|
||||
|
||||
from picard.ui.ui_widget_taglisteditor import Ui_TagListEditor
|
||||
from picard.ui.widgets.editablelistview import (
|
||||
AutocompleteItemDelegate,
|
||||
EditableListModel,
|
||||
)
|
||||
|
||||
|
||||
class TagListEditor(QtWidgets.QWidget):
|
||||
def __init__(self, parent):
|
||||
super().__init__(parent)
|
||||
self.ui = Ui_TagListEditor()
|
||||
self.ui.setupUi(self)
|
||||
list_view = self.ui.tag_list_view
|
||||
model = TagListModel()
|
||||
list_view.setModel(model)
|
||||
list_view.setItemDelegate(AutocompleteItemDelegate(
|
||||
sorted(TAG_NAMES.keys())))
|
||||
|
||||
selection = list_view.selectionModel()
|
||||
selection.selectionChanged.connect(self.on_selection_changed)
|
||||
self.on_selection_changed([], [])
|
||||
|
||||
def on_selection_changed(self, selected, deselected):
|
||||
buttons_enabled = len(self.ui.tag_list_view.selectedIndexes()) > 0
|
||||
self.ui.tags_remove_btn.setEnabled(buttons_enabled)
|
||||
self.ui.tags_move_up_btn.setEnabled(buttons_enabled)
|
||||
self.ui.tags_move_down_btn.setEnabled(buttons_enabled)
|
||||
|
||||
def clear(self):
|
||||
self.ui.tag_list_view.update([])
|
||||
|
||||
def update(self, tags):
|
||||
self.ui.tag_list_view.update(tags)
|
||||
|
||||
@property
|
||||
def tags(self):
|
||||
return self.ui.tag_list_view.tags
|
||||
|
||||
|
||||
class TagListModel(EditableListModel):
|
||||
def get_display_name(self, item):
|
||||
return display_tag_name(item)
|
||||
@@ -223,6 +223,18 @@ class MetadataTest(PicardTestCase):
|
||||
self.assertEqual(m1.compare(m2), m2.compare(m1))
|
||||
self.assertEqual(m1.compare(m2), 1)
|
||||
|
||||
def test_compare_with_ignored(self):
|
||||
m1 = Metadata()
|
||||
m1["title"] = "title1"
|
||||
m1["tracknumber"] = "2"
|
||||
m1.length = 360
|
||||
m2 = Metadata()
|
||||
m2["title"] = "title1"
|
||||
m2["tracknumber"] = "3"
|
||||
m2.length = 300
|
||||
self.assertNotEqual(m1.compare(m2), 1)
|
||||
self.assertEqual(m1.compare(m2, ignored=['tracknumber', '~length']), 1)
|
||||
|
||||
def test_compare_lengths(self):
|
||||
m1 = Metadata()
|
||||
m1.length = 360
|
||||
|
||||
@@ -155,20 +155,32 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
<widget class="QLabel" name="compare_ignore_tags_label">
|
||||
<property name="text">
|
||||
<string>Tags to ignore for comparisson:</string>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>181</width>
|
||||
<height>21</height>
|
||||
</size>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="TagListEditor" name="compare_ignore_tags" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</spacer>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>TagListEditor</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>picard.ui.widgets.taglisteditor</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>ignore_regex</tabstop>
|
||||
<tabstop>ignore_hidden_files</tabstop>
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>893</width>
|
||||
<height>698</height>
|
||||
<width>418</width>
|
||||
<height>310</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout">
|
||||
@@ -34,207 +34,25 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="EditableTagListView" name="top_tags_list">
|
||||
<property name="dragEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="dragDropMode">
|
||||
<enum>QAbstractItemView::InternalMove</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="tags_remove_btn">
|
||||
<property name="toolTip">
|
||||
<string>Remove selected tags</string>
|
||||
</property>
|
||||
<property name="accessibleName">
|
||||
<string>Remove selected tags</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Remove tags</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="tags_add_btn">
|
||||
<property name="accessibleName">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Add new tag</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="tags_move_up_btn">
|
||||
<property name="toolTip">
|
||||
<string>Move tag up</string>
|
||||
</property>
|
||||
<property name="accessibleName">
|
||||
<string>Move tag up</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="go-up">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="tags_move_down_btn">
|
||||
<property name="toolTip">
|
||||
<string>Move tag down</string>
|
||||
</property>
|
||||
<property name="accessibleName">
|
||||
<string>Move tag down</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="go-down">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
<widget class="TagListEditor" name="top_tags_list" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>EditableTagListView</class>
|
||||
<extends>QListView</extends>
|
||||
<header>picard.ui.taglistview</header>
|
||||
<slots>
|
||||
<slot>add_empty_row()</slot>
|
||||
<slot>remove_selected_rows()</slot>
|
||||
<slot>move_selected_rows_up()</slot>
|
||||
<slot>move_selected_rows_down()</slot>
|
||||
</slots>
|
||||
<class>TagListEditor</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>picard.ui.widgets.taglisteditor</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>tags_add_btn</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>top_tags_list</receiver>
|
||||
<slot>add_empty_row()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>804</x>
|
||||
<y>673</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>428</x>
|
||||
<y>343</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>tags_remove_btn</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>top_tags_list</receiver>
|
||||
<slot>remove_selected_rows()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>716</x>
|
||||
<y>673</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>428</x>
|
||||
<y>343</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>tags_move_up_btn</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>top_tags_list</receiver>
|
||||
<slot>move_selected_rows_up()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>867</x>
|
||||
<y>344</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>428</x>
|
||||
<y>343</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>tags_move_down_btn</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>top_tags_list</receiver>
|
||||
<slot>move_selected_rows_down()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>867</x>
|
||||
<y>374</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>428</x>
|
||||
<y>343</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
||||
220
ui/widget_taglisteditor.ui
Normal file
220
ui/widget_taglisteditor.ui
Normal file
@@ -0,0 +1,220 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>TagListEditor</class>
|
||||
<widget class="QWidget" name="TagListEditor">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="EditableListView" name="tag_list_view"/>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="tags_remove_btn">
|
||||
<property name="toolTip">
|
||||
<string>Remove selected tags</string>
|
||||
</property>
|
||||
<property name="accessibleName">
|
||||
<string>Remove selected tags</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Remove tags</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="tags_add_btn">
|
||||
<property name="text">
|
||||
<string>Add new tag</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="tags_move_up_btn">
|
||||
<property name="toolTip">
|
||||
<string>Move tag up</string>
|
||||
</property>
|
||||
<property name="accessibleName">
|
||||
<string>Move tag up</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="go-up"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="tags_move_down_btn">
|
||||
<property name="toolTip">
|
||||
<string>Move tag down</string>
|
||||
</property>
|
||||
<property name="accessibleName">
|
||||
<string>Move tag down</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="go-down"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>EditableListView</class>
|
||||
<extends>QListView</extends>
|
||||
<header>picard.ui.widgets.editablelistview</header>
|
||||
<slots>
|
||||
<slot>add_empty_row()</slot>
|
||||
<slot>remove_selected_rows()</slot>
|
||||
<slot>move_selected_rows_up()</slot>
|
||||
<slot>move_selected_rows_down()</slot>
|
||||
</slots>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>tags_remove_btn</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>tag_list_view</receiver>
|
||||
<slot>remove_selected_rows()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>182</x>
|
||||
<y>285</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>155</x>
|
||||
<y>133</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>tags_add_btn</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>tag_list_view</receiver>
|
||||
<slot>add_empty_row()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>269</x>
|
||||
<y>285</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>155</x>
|
||||
<y>133</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>tags_move_up_btn</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>tag_list_view</receiver>
|
||||
<slot>move_selected_rows_up()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>384</x>
|
||||
<y>134</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>181</x>
|
||||
<y>133</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>tags_move_down_btn</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>tag_list_view</receiver>
|
||||
<slot>move_selected_rows_down()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>384</x>
|
||||
<y>164</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>181</x>
|
||||
<y>133</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
Reference in New Issue
Block a user