From 7704ca4a0534885e66e4a0d371a46aff76d9159b Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Wed, 1 Dec 2010 19:07:29 +0000 Subject: [PATCH] First pass at enum custom type --- src/calibre/gui2/custom_column_widgets.py | 82 +++++++++++++++++++ src/calibre/gui2/library/delegates.py | 33 ++++++++ src/calibre/gui2/library/models.py | 4 +- src/calibre/gui2/library/views.py | 6 +- .../gui2/preferences/create_custom_column.py | 17 +++- .../gui2/preferences/create_custom_column.ui | 44 ++++++++-- src/calibre/library/custom_columns.py | 7 +- src/calibre/library/field_metadata.py | 2 +- 8 files changed, 176 insertions(+), 19 deletions(-) diff --git a/src/calibre/gui2/custom_column_widgets.py b/src/calibre/gui2/custom_column_widgets.py index 053dd7a743..7ea4fa679f 100644 --- a/src/calibre/gui2/custom_column_widgets.py +++ b/src/calibre/gui2/custom_column_widgets.py @@ -310,6 +310,37 @@ class Series(Base): self.db.set_custom(book_id, val, extra=s_index, num=self.col_id, notify=notify, commit=False) +class Enumeration(Base): + + def setup_ui(self, parent): + self.widgets = [QLabel('&'+self.col_metadata['name']+':', parent), + QComboBox(parent)] + w = self.widgets[1] + print self.col_metadata['display'] + vals = self.col_metadata['display']['enum_values'] + w.addItem('') + for v in vals: + w.addItem(v) + + def initialize(self, book_id): + val = self.db.get_custom(book_id, num=self.col_id, index_is_id=True) + if val is None: + val = '' + self.initial_val = val + val = self.normalize_db_val(val) + idx = self.widgets[1].findText(val) + if idx < 0: + idx = 0 + self.widgets[1].setCurrentIndex(idx) + + def setter(self, val): + if val is None: + val = '' + self.widgets[1].setCurrentIndex(self.widgets[1].findText(val)) + + def getter(self): + return unicode(self.widgets[1].currentText()) + widgets = { 'bool' : Bool, 'rating' : Rating, @@ -319,6 +350,7 @@ widgets = { 'text' : Text, 'comments': Comments, 'series': Series, + 'enumeration': Enumeration } def field_sort(y, z, x=None): @@ -551,6 +583,55 @@ class BulkSeries(BulkBase): self.db.set_custom_bulk(book_ids, val, extras=extras, num=self.col_id, notify=notify) +class BulkEnumeration(BulkBase, Enumeration): + + def get_initial_value(self, book_ids): + value = None + for book_id in book_ids: + val = self.db.get_custom(book_id, num=self.col_id, index_is_id=True) + if value is not None and value != val: + return ' nochange ' + value = val + return value + + def setup_ui(self, parent): + self.widgets = [QLabel('&'+self.col_metadata['name']+':', parent), + QComboBox(parent)] + w = self.widgets[1] + print self.col_metadata['display'] + vals = self.col_metadata['display']['enum_values'] + w.addItem('Do Not Change') + w.addItem('') + for v in vals: + w.addItem(v) + + def getter(self): + if self.widgets[1].currentIndex() == 0: + return ' nochange ' + return unicode(self.widgets[1].currentText()) + + def setter(self, val): + if val == ' nochange ': + self.widgets[1].setCurrentIndex(0) + else: + self.widgets[1].setCurrentIndex(self.widgets[1].findText(val)) + + def commit(self, book_ids, notify=False): + val = self.gui_val + val = self.normalize_ui_val(val) + if val != self.initial_val and val != ' nochange ': + self.db.set_custom_bulk(book_ids, val, num=self.col_id, notify=notify) + + def normalize_ui_val(self, val): + if val == '': + return None + return val + + def normalize_db_val(self, val): + if val is None: + return '' + return val + class RemoveTags(QWidget): def __init__(self, parent, values): @@ -656,4 +737,5 @@ bulk_widgets = { 'datetime': BulkDateTime, 'text' : BulkText, 'series': BulkSeries, + 'enumeration': BulkEnumeration, } diff --git a/src/calibre/gui2/library/delegates.py b/src/calibre/gui2/library/delegates.py index f9ba612bc9..f5610bf849 100644 --- a/src/calibre/gui2/library/delegates.py +++ b/src/calibre/gui2/library/delegates.py @@ -254,6 +254,39 @@ class CcTextDelegate(QStyledItemDelegate): # {{{ # }}} +class CcEnumDelegate(QStyledItemDelegate): # {{{ + ''' + Delegate for text/int/float data. + ''' + + def createEditor(self, parent, option, index): + m = index.model() + col = m.column_map[index.column()] + print 'delegate' + editor = QComboBox(parent) + editor.addItem('') + for v in m.custom_columns[col]['display']['enum_values']: + editor.addItem(v) + return editor + + def setModelData(self, editor, model, index): + val = unicode(editor.currentText()) + if val == '': + val = None + model.setData(index, QVariant(val), Qt.EditRole) + + def setEditorData(self, editor, index): + m = index.model() + val = m.db.data[index.row()][m.custom_columns[m.column_map[index.column()]]['rec_index']] + if val is None: + val = '' + idx = editor.findText(val) + if idx < 0: + editor.setCurrentIndex(0) + else: + editor.setCurrentIndex(idx) +# }}} + class CcCommentsDelegate(QStyledItemDelegate): # {{{ ''' Delegate for comments data. diff --git a/src/calibre/gui2/library/models.py b/src/calibre/gui2/library/models.py index 4ef2c34c03..99639ed0a7 100644 --- a/src/calibre/gui2/library/models.py +++ b/src/calibre/gui2/library/models.py @@ -634,7 +634,7 @@ class BooksModel(QAbstractTableModel): # {{{ for col in self.custom_columns: idx = self.custom_columns[col]['rec_index'] datatype = self.custom_columns[col]['datatype'] - if datatype in ('text', 'comments', 'composite'): + if datatype in ('text', 'comments', 'composite', 'enumeration'): self.dc[col] = functools.partial(text_type, idx=idx, mult=self.custom_columns[col]['is_multiple']) elif datatype in ('int', 'float'): @@ -719,7 +719,7 @@ class BooksModel(QAbstractTableModel): # {{{ typ = cc['datatype'] label=self.db.field_metadata.key_to_label(colhead) s_index = None - if typ in ('text', 'comments'): + if typ in ('text', 'comments', 'enumeration'): val = unicode(value.toString()).strip() val = val if val else None if typ == 'bool': diff --git a/src/calibre/gui2/library/views.py b/src/calibre/gui2/library/views.py index fadb8314e3..f724ca7b58 100644 --- a/src/calibre/gui2/library/views.py +++ b/src/calibre/gui2/library/views.py @@ -14,7 +14,8 @@ from PyQt4.Qt import QTableView, Qt, QAbstractItemView, QMenu, pyqtSignal, \ from calibre.gui2.library.delegates import RatingDelegate, PubDateDelegate, \ TextDelegate, DateDelegate, TagsDelegate, CcTextDelegate, \ - CcBoolDelegate, CcCommentsDelegate, CcDateDelegate, CcTemplateDelegate + CcBoolDelegate, CcCommentsDelegate, CcDateDelegate, CcTemplateDelegate, \ + CcEnumDelegate from calibre.gui2.library.models import BooksModel, DeviceBooksModel from calibre.utils.config import tweaks, prefs from calibre.gui2 import error_dialog, gprefs @@ -76,6 +77,7 @@ class BooksView(QTableView): # {{{ self.publisher_delegate = TextDelegate(self) self.text_delegate = TextDelegate(self) self.cc_text_delegate = CcTextDelegate(self) + self.cc_enum_delegate = CcEnumDelegate(self) self.cc_bool_delegate = CcBoolDelegate(self) self.cc_comments_delegate = CcCommentsDelegate(self) self.cc_template_delegate = CcTemplateDelegate(self) @@ -427,6 +429,8 @@ class BooksView(QTableView): # {{{ self.setItemDelegateForColumn(cm.index(colhead), self.rating_delegate) elif cc['datatype'] == 'composite': self.setItemDelegateForColumn(cm.index(colhead), self.cc_template_delegate) + elif cc['datatype'] == 'enumeration': + self.setItemDelegateForColumn(cm.index(colhead), self.cc_enum_delegate) else: dattr = colhead+'_delegate' delegate = colhead if hasattr(self, dattr) else 'text' diff --git a/src/calibre/gui2/preferences/create_custom_column.py b/src/calibre/gui2/preferences/create_custom_column.py index ebf33784d4..d4d2b2678c 100644 --- a/src/calibre/gui2/preferences/create_custom_column.py +++ b/src/calibre/gui2/preferences/create_custom_column.py @@ -40,6 +40,8 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn): 'text':_('Yes/No'), 'is_multiple':False}, 9:{'datatype':'composite', 'text':_('Column built from other columns'), 'is_multiple':False}, + 10:{'datatype':'enumeration', + 'text':_('Fixed set of text values'), 'is_multiple':False}, } def __init__(self, parent, editing, standard_colheads, standard_colnames): @@ -90,7 +92,7 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn): if c['display'].get('date_format', None): self.date_format_box.setText(c['display'].get('date_format', '')) elif ct == 'composite': - self.composite_box.setText(c['display'].get('composite_template', '')) + self.composite_box.setText(c['display'].get('enum_values', '')) self.datatype_changed() self.exec_() @@ -103,7 +105,8 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn): getattr(self, 'date_format_'+x).setVisible(col_type == 'datetime') for x in ('box', 'default_label', 'label'): getattr(self, 'composite_'+x).setVisible(col_type == 'composite') - + for x in ('box', 'default_label', 'label'): + getattr(self, 'enum_'+x).setVisible(col_type == 'enumeration') def accept(self): col = unicode(self.column_name_box.text()) @@ -145,17 +148,23 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn): return self.simple_error('', _('The heading %s is already used')%col_heading) display_dict = {} + if col_type == 'datetime': if self.date_format_box.text(): display_dict = {'date_format':unicode(self.date_format_box.text())} else: display_dict = {'date_format': None} - - if col_type == 'composite': + elif col_type == 'composite': if not self.composite_box.text(): return self.simple_error('', _('You must enter a template for' ' composite columns')) display_dict = {'composite_template':unicode(self.composite_box.text())} + elif col_type == 'enumeration': + if not self.enum_box.text(): + return self.simple_error('', _('You must enter at least one' + ' value for enumeration columns')) + display_dict = {'enum_values': + [v.strip() for v in unicode(self.enum_box.text()).split(',')]} db = self.parent.gui.library_view.model().db key = db.field_metadata.custom_field_prefix+col diff --git a/src/calibre/gui2/preferences/create_custom_column.ui b/src/calibre/gui2/preferences/create_custom_column.ui index 640becca8c..03c191d34e 100644 --- a/src/calibre/gui2/preferences/create_custom_column.ui +++ b/src/calibre/gui2/preferences/create_custom_column.ui @@ -10,7 +10,7 @@ 0 0 528 - 199 + 212 @@ -24,7 +24,7 @@ - + QLayout::SetDefaultConstraint @@ -56,7 +56,7 @@ - + @@ -69,7 +69,7 @@ - + Column heading in the library view and category name in the tag browser @@ -86,7 +86,7 @@ - + @@ -105,7 +105,7 @@ - + @@ -147,7 +147,7 @@ - + @@ -184,7 +184,7 @@ - + Qt::Vertical @@ -197,6 +197,34 @@ + + + + Values + + + + + + + + + + 0 + 0 + + + + + + + + Default: (nothing) + + + + + diff --git a/src/calibre/library/custom_columns.py b/src/calibre/library/custom_columns.py index fdd78e89f8..b477c89fe5 100644 --- a/src/calibre/library/custom_columns.py +++ b/src/calibre/library/custom_columns.py @@ -18,7 +18,7 @@ from calibre.utils.date import parse_date class CustomColumns(object): CUSTOM_DATA_TYPES = frozenset(['rating', 'text', 'comments', 'datetime', - 'int', 'float', 'bool', 'series', 'composite']) + 'int', 'float', 'bool', 'series', 'composite', 'enumeration']) def custom_table_names(self, num): return 'custom_column_%d'%num, 'books_custom_column_%d_link'%num @@ -144,7 +144,8 @@ class CustomColumns(object): 'comments': lambda x,d: adapt_text(x, {'is_multiple':False}), 'datetime' : adapt_datetime, 'text':adapt_text, - 'series':adapt_text + 'series':adapt_text, + 'enumeration': adapt_text } # Create Tag Browser categories for custom columns @@ -558,7 +559,7 @@ class CustomColumns(object): if datatype in ('rating', 'int'): dt = 'INT' - elif datatype in ('text', 'comments', 'series', 'composite'): + elif datatype in ('text', 'comments', 'series', 'composite', 'enumeration'): dt = 'TEXT' elif datatype in ('float',): dt = 'REAL' diff --git a/src/calibre/library/field_metadata.py b/src/calibre/library/field_metadata.py index d10dc5da71..f5a156d3a7 100644 --- a/src/calibre/library/field_metadata.py +++ b/src/calibre/library/field_metadata.py @@ -83,7 +83,7 @@ class FieldMetadata(dict): ''' VALID_DATA_TYPES = frozenset([None, 'rating', 'text', 'comments', 'datetime', - 'int', 'float', 'bool', 'series', 'composite']) + 'int', 'float', 'bool', 'series', 'composite', 'enumeration']) # Builtin metadata {{{