Edit metadata dialog: Add special author edit

Edit metadata dialog: Facilitate editing the authors for books that have
a large number of authors, by using a dedicated "Edit authors" tool, which
you can launch by right clicking the authors in the edit metadata dialog
and selecting "Edit authors". Fixes #1255288 [[Enhancement] Display authors in editable popup window](https://bugs.launchpad.net/calibre/+bug/1255288)
This commit is contained in:
Kovid Goyal 2013-11-27 15:05:05 +05:30
parent 7f4cce12f4
commit 0199955e0e
2 changed files with 213 additions and 0 deletions

View File

@ -0,0 +1,198 @@
#!/usr/bin/env python
# vim:fileencoding=utf-8
from __future__ import (unicode_literals, division, absolute_import,
print_function)
__license__ = 'GPL v3'
__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
from collections import OrderedDict
from PyQt4.Qt import (
QDialog, QGridLayout, QDialogButtonBox, QListWidget, QApplication, Qt,
pyqtSignal, QSize, QPushButton, QIcon, QStyledItemDelegate, QLabel)
from calibre.utils.config_base import tweaks
from calibre.gui2 import gprefs
from calibre.gui2.complete2 import EditWithComplete
from calibre.ebooks.metadata import string_to_authors
class ItemDelegate(QStyledItemDelegate):
edited = pyqtSignal(object)
def __init__(self, all_authors, parent):
QStyledItemDelegate.__init__(self, parent)
self.all_authors = all_authors
def sizeHint(self, *args):
return QStyledItemDelegate.sizeHint(self, *args) + QSize(0, 15)
def setEditorData(self, editor, index):
name = unicode(index.data(Qt.DisplayRole).toString())
editor.setText(name)
editor.lineEdit().selectAll()
def setModelData(self, editor, model, index):
authors = string_to_authors(unicode(editor.text()))
model.setData(index, authors[0])
self.edited.emit(index.row())
def createEditor(self, parent, option, index):
self.ed = EditWithComplete(parent)
self.ed.setFocusPolicy(Qt.StrongFocus)
init_line_edit(self.ed, self.all_authors)
return self.ed
class List(QListWidget):
def __init__(self, all_authors, parent):
QListWidget.__init__(self, parent)
self.setDragEnabled(True)
self.setSelectionMode(self.ExtendedSelection)
self.setDropIndicatorShown(True)
self.setDragDropMode(self.InternalMove)
self.setAlternatingRowColors(True)
self.d = ItemDelegate(all_authors, self)
self.d.edited.connect(self.edited, type=Qt.QueuedConnection)
self.setItemDelegate(self.d)
def delete_selected(self):
for item in self.selectedItems():
self.takeItem(self.row(item))
def keyPressEvent(self, ev):
if ev.key() == Qt.Key_Delete:
self.delete_selected()
ev.accept()
return
return QListWidget.keyPressEvent(self, ev)
def addItem(self, *args):
try:
return QListWidget.addItem(self, *args)
finally:
self.mark_as_editable()
def addItems(self, *args):
try:
return QListWidget.addItems(self, *args)
finally:
self.mark_as_editable()
def mark_as_editable(self):
for i in xrange(self.count()):
item = self.item(i)
item.setFlags(item.flags() | Qt.ItemIsEditable)
def edited(self, i):
item = self.item(i)
q = unicode(item.text())
remove = []
for j in xrange(self.count()):
if i != j and unicode(self.item(j).text()) == q:
remove.append(j)
for x in sorted(remove, reverse=True):
self.takeItem(x)
class Edit(EditWithComplete):
returnPressed = pyqtSignal()
def keyPressEvent(self, ev):
if ev.key() in (Qt.Key_Return, Qt.Key_Enter):
ev.accept()
self.returnPressed.emit()
return
return EditWithComplete.keyPressEvent(self, ev)
def init_line_edit(a, all_authors):
a.set_separator('&')
a.set_space_before_sep(True)
a.set_add_separator(tweaks['authors_completer_append_separator'])
a.update_items_cache(all_authors)
class AuthorsEdit(QDialog):
def __init__(self, all_authors, current_authors, parent=None):
QDialog.__init__(self, parent)
self.l = l = QGridLayout()
self.setLayout(l)
self.setWindowTitle(_('Edit authors'))
self.la = QLabel(_(
'Edit the authors for this book. You can drag and drop to re-arrange authors'))
self.la.setWordWrap(True)
l.addWidget(self.la, 0, 0, 1, 3)
self.al = al = List(all_authors, self)
al.addItems(current_authors)
l.addWidget(al, 1, 0, 1, 3)
self.author = a = Edit(self)
init_line_edit(a, all_authors)
a.lineEdit().setPlaceholderText(_('Enter an author to add'))
a.returnPressed.connect(self.add_author)
l.addWidget(a, 2, 0)
self.ab = b = QPushButton(_('&Add'))
b.setIcon(QIcon(I('plus.png')))
l.addWidget(b, 2, 1)
b.clicked.connect(self.add_author)
self.db = b = QPushButton(_('&Remove selected'))
l.addWidget(b, 2, 2)
b.setIcon(QIcon(I('minus.png')))
b.clicked.connect(self.al.delete_selected)
self.bb = bb = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
bb.accepted.connect(self.accept)
bb.rejected.connect(self.reject)
l.addWidget(bb, 3, 0, 1, 3)
l.setColumnStretch(0, 10)
self.resize(self.sizeHint() + QSize(150, 100))
geom = gprefs.get('authors-edit-geometry', None)
if geom is not None:
self.restoreGeometry(geom)
self.author.setFocus(Qt.OtherFocusReason)
def save_geometry(self):
gprefs.set('authors-edit-geometry', bytearray(self.saveGeometry()))
def accept(self):
self.save_geometry()
return QDialog.accept(self)
def reject(self):
self.save_geometry()
return QDialog.reject(self)
@property
def authors(self):
ans = []
for i in xrange(self.al.count()):
ans.append(unicode(self.al.item(i).text()))
return ans or [_('Unknown')]
def add_author(self):
text = self.author.text().strip()
authors = OrderedDict((icu_lower(x), (i, x)) for i, x in enumerate(self.authors))
if text:
for author in string_to_authors(text):
la = icu_lower(author)
if la in authors and authors[la][1] != author:
# Case change
i = authors[la][0]
authors[la] = (i, author)
self.al.item(i).setText(author)
else:
self.al.addItem(author)
authors[la] = author
self.author.setText('')
if __name__ == '__main__':
app = QApplication([])
d = AuthorsEdit(['kovid goyal', 'divok layog', 'other author'], ['kovid goyal', 'other author'])
d.exec_()
print (d.authors)

View File

@ -227,6 +227,21 @@ class AuthorsEdit(EditWithComplete):
self.setSizeAdjustPolicy(self.AdjustToMinimumContentsLengthWithIcon)
self.manage_authors_signal = manage_authors
manage_authors.triggered.connect(self.manage_authors)
self.lineEdit().createStandardContextMenu = self.createStandardContextMenu
def createStandardContextMenu(self):
menu = QLineEdit.createStandardContextMenu(self.lineEdit())
menu.addSeparator()
menu.addAction(_('&Edit authors'), self.edit_authors)
return menu
def edit_authors(self):
all_authors = self.lineEdit().all_items
current_authors = self.current_val
from calibre.gui2.dialogs.authors_edit import AuthorsEdit
d = AuthorsEdit(all_authors, current_authors, self)
if d.exec_() == d.Accepted:
self.current_val = d.authors
def manage_authors(self):
if self.original_val != self.current_val: