Framework for bulk metadata edits of custom columns

This commit is contained in:
Kovid Goyal 2010-05-05 12:35:59 -06:00
parent b64092f881
commit 9e8d7a3653
2 changed files with 118 additions and 26 deletions

View File

@ -6,6 +6,7 @@ __copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
import sys import sys
from functools import partial
from PyQt4.Qt import QComboBox, QLabel, QSpinBox, QDoubleSpinBox, QDateEdit, \ from PyQt4.Qt import QComboBox, QLabel, QSpinBox, QDoubleSpinBox, QDateEdit, \
QDate, QGroupBox, QVBoxLayout, QPlainTextEdit, QSizePolicy, \ QDate, QGroupBox, QVBoxLayout, QPlainTextEdit, QSizePolicy, \
@ -18,10 +19,11 @@ from calibre.utils.config import tweaks
class Base(object): class Base(object):
def __init__(self, db, col_id): def __init__(self, db, col_id, parent=None):
self.db, self.col_id = db, col_id self.db, self.col_id = db, col_id
self.col_metadata = db.custom_column_num_map[col_id] self.col_metadata = db.custom_column_num_map[col_id]
self.initial_val = None self.initial_val = None
self.setup_ui(parent)
def initialize(self, book_id): def initialize(self, book_id):
val = self.db.get_custom(book_id, num=self.col_id, index_is_id=True) val = self.db.get_custom(book_id, num=self.col_id, index_is_id=True)
@ -43,8 +45,7 @@ class Base(object):
class Bool(Base): class Bool(Base):
def __init__(self, db, col_id, parent=None): def setup_ui(self, parent):
Base.__init__(self, db, col_id)
self.widgets = [QLabel('&'+self.col_metadata['name'], parent), self.widgets = [QLabel('&'+self.col_metadata['name'], parent),
QComboBox(parent)] QComboBox(parent)]
w = self.widgets[1] w = self.widgets[1]
@ -69,8 +70,7 @@ class Bool(Base):
class Int(Base): class Int(Base):
def __init__(self, db, col_id, parent=None): def setup_ui(self, parent):
Base.__init__(self, db, col_id)
self.widgets = [QLabel('&'+self.col_metadata['name']+':', parent), self.widgets = [QLabel('&'+self.col_metadata['name']+':', parent),
QSpinBox(parent)] QSpinBox(parent)]
w = self.widgets[1] w = self.widgets[1]
@ -93,8 +93,7 @@ class Int(Base):
class Float(Int): class Float(Int):
def __init__(self, db, col_id, parent=None): def setup_ui(self, parent):
Base.__init__(self, db, col_id)
self.widgets = [QLabel('&'+self.col_metadata['name']+':', parent), self.widgets = [QLabel('&'+self.col_metadata['name']+':', parent),
QDoubleSpinBox(parent)] QDoubleSpinBox(parent)]
w = self.widgets[1] w = self.widgets[1]
@ -103,8 +102,8 @@ class Float(Int):
class Rating(Int): class Rating(Int):
def __init__(self, db, col_id, parent=None): def setup_ui(self, parent):
Int.__init__(self, db, col_id) Int.setup_ui(self, parent)
w = self.widgets[1] w = self.widgets[1]
w.setRange(0, 5) w.setRange(0, 5)
w.setSuffix(' '+_('stars')) w.setSuffix(' '+_('stars'))
@ -125,8 +124,7 @@ class Rating(Int):
class DateTime(Base): class DateTime(Base):
def __init__(self, db, col_id, parent=None): def setup_ui(self, parent):
Base.__init__(self, db, col_id)
self.widgets = [QLabel('&'+self.col_metadata['name']+':', parent), self.widgets = [QLabel('&'+self.col_metadata['name']+':', parent),
QDateEdit(parent)] QDateEdit(parent)]
w = self.widgets[1] w = self.widgets[1]
@ -153,8 +151,7 @@ class DateTime(Base):
class Comments(Base): class Comments(Base):
def __init__(self, db, col_id, parent=None): def setup_ui(self, parent):
Base.__init__(self, db, col_id)
self._box = QGroupBox(parent) self._box = QGroupBox(parent)
self._box.setTitle('&'+self.col_metadata['name']) self._box.setTitle('&'+self.col_metadata['name'])
self._layout = QVBoxLayout() self._layout = QVBoxLayout()
@ -178,9 +175,8 @@ class Comments(Base):
class Text(Base): class Text(Base):
def __init__(self, db, col_id, parent=None): def setup_ui(self, parent):
Base.__init__(self, db, col_id) values = self.all_values = list(self.db.all_custom(num=self.col_id))
values = self.all_values = list(self.db.all_custom(num=col_id))
values.sort(cmp = lambda x,y: cmp(x.lower(), y.lower())) values.sort(cmp = lambda x,y: cmp(x.lower(), y.lower()))
if self.col_metadata['is_multiple']: if self.col_metadata['is_multiple']:
w = TagsLineEdit(parent, values) w = TagsLineEdit(parent, values)
@ -238,16 +234,16 @@ widgets = {
'comments': Comments, 'comments': Comments,
} }
def field_sort(y, z, x=None):
m1, m2 = x[y], x[z]
n1 = 'zzzzz' if m1['datatype'] == 'comments' else m1['name']
n2 = 'zzzzz' if m2['datatype'] == 'comments' else m2['name']
return cmp(n1.lower(), n2.lower())
def populate_single_metadata_page(left, right, db, book_id, parent=None): def populate_single_metadata_page(left, right, db, book_id, parent=None):
x = db.custom_column_num_map x = db.custom_column_num_map
cols = list(x) cols = list(x)
def field_sort(y, z): cols.sort(cmp=partial(field_sort, x=x))
m1, m2 = x[y], x[z]
n1 = 'zzzzz' if m1['datatype'] == 'comments' else m1['name']
n2 = 'zzzzz' if m2['datatype'] == 'comments' else m2['name']
return cmp(n1.lower(), n2.lower())
cols.sort(cmp=field_sort)
ans = [] ans = []
for i, col in enumerate(cols): for i, col in enumerate(cols):
w = widgets[x[col]['datatype']](db, col, parent) w = widgets[x[col]['datatype']](db, col, parent)
@ -275,6 +271,91 @@ def populate_single_metadata_page(left, right, db, book_id, parent=None):
return ans, items return ans, items
def populate_bulk_metadata_page(left, right, db, book_id, parent=None): class BulkBase(Base):
def get_initial_value(self, book_ids):
values = set([])
for book_id in book_ids:
val = self.db.get_custom(book_id, num=self.col_id, index_is_id=True)
if isinstance(val, list):
val = frozenset(val)
values.add(val)
if len(values) > 1:
break
ans = None
if len(values) == 1:
ans = iter(values).next()
if isinstance(ans, frozenset):
ans = list(ans)
return ans
def initialize(self, book_ids):
self.initial_val = val = self.get_initial_value(book_ids)
val = self.normalize_db_val(val)
self.setter(val)
def commit(self, book_ids, notify=False):
val = self.getter()
val = self.normalize_ui_val(val)
if val != self.initial_val:
for book_id in book_ids:
self.db.set_custom(book_id, val, num=self.col_id, notify=notify)
class BulkBool(BulkBase, Bool):
pass pass
class BulkRating(BulkBase, Rating):
pass
class BulkInt(BulkBase, Int):
pass
class BulkFloat(BulkBase, Float):
pass
class BulkRating(BulkBase, Rating):
pass
class BulkDateTime(BulkBase, DateTime):
pass
class BulkText(BulkBase, Text):
pass
bulk_widgets = {
'bool' : BulkBool,
'rating' : BulkRating,
'int': BulkInt,
'float': BulkFloat,
'datetime': BulkDateTime,
'text' : BulkText,
}
def populate_bulk_metadata_page(layout, db, book_ids, parent=None):
x = db.custom_column_num_map
cols = list(x)
cols.sort(cmp=partial(field_sort, x=x))
ans = []
for i, col in enumerate(cols):
dt = x[col]['datatype']
if dt == 'comments':
continue
w = bulk_widgets[dt](db, col, parent)
ans.append(w)
w.initialize(book_ids)
row = layout.rowCount()
if len(w.widgets) == 1:
layout.addWidget(w.widgets[0], row, 0, 1, -1)
else:
w.widgets[0].setBuddy(w.widgets[1])
for c, widget in enumerate(w.widgets):
layout.addWidget(widget, row, c)
items = []
if len(ans) > 0:
items.append(QSpacerItem(10, 10, QSizePolicy.Minimum,
QSizePolicy.Expanding))
layout.addItem(items[-1], layout.rowCount(), 0, 1, 1)
layout.setRowStretch(layout.rowCount()-1, 100)
return ans, items

View File

@ -3,8 +3,9 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
'''Dialog to edit metadata in bulk''' '''Dialog to edit metadata in bulk'''
import sip
from PyQt4.QtCore import SIGNAL, QObject from PyQt4.QtCore import SIGNAL, QObject
from PyQt4.QtGui import QDialog from PyQt4.QtGui import QDialog, QGridLayout
from calibre.gui2.dialogs.metadata_bulk_ui import Ui_MetadataBulkDialog from calibre.gui2.dialogs.metadata_bulk_ui import Ui_MetadataBulkDialog
from calibre.gui2.dialogs.tag_editor import TagEditor from calibre.gui2.dialogs.tag_editor import TagEditor
@ -48,7 +49,17 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
self.exec_() self.exec_()
def create_custom_column_editors(self): def create_custom_column_editors(self):
pass w = self.central_widget.widget(1)
layout = QGridLayout()
self.custom_column_widgets, self.__cc_spacers = populate_bulk_metadata_page(
layout, self.db, self.ids, w)
#sip.delete(w.layout())
w.setLayout(layout)
self.__custom_col_layouts = [layout]
ans = self.custom_column_widgets
for i in range(len(ans)-1):
w.setTabOrder(ans[i].widgets[-1], ans[i+1].widgets[-1])
def initialize_combos(self): def initialize_combos(self):
self.initalize_authors() self.initalize_authors()