Merge pull request #1379 from phw/PICARD-1677-ignore-tags

PICARD-1677: Tags can be ignored for metadata comparisson
This commit is contained in:
Philipp Wolfer
2019-11-29 17:02:15 +01:00
committed by GitHub
13 changed files with 484 additions and 312 deletions

View File

@@ -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

View File

@@ -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:

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View 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

View File

@@ -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()

View 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)

View File

@@ -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

View File

@@ -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>

View File

@@ -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
View 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>