From 4404b6ff95f1800284eee39a425b989634f994b4 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 9 Oct 2014 09:37:32 +0530 Subject: [PATCH] Catalogs: CSV Output: Allow changing the order of fields in the generated CSV catalog by using drag and drop to re-arrange the fields in the create catalog dialog. Fixes #1379048 [[Enhancement] Specify column order in csv calalogue](https://bugs.launchpad.net/calibre/+bug/1379048) --- src/calibre/customize/__init__.py | 12 ++-- src/calibre/gui2/catalog/catalog_csv_xml.py | 77 +++++++++++++-------- src/calibre/gui2/catalog/catalog_csv_xml.ui | 47 ------------- 3 files changed, 55 insertions(+), 81 deletions(-) delete mode 100644 src/calibre/gui2/catalog/catalog_csv_xml.ui diff --git a/src/calibre/customize/__init__.py b/src/calibre/customize/__init__.py index 1e2cca5317..650cfa2bde 100644 --- a/src/calibre/customize/__init__.py +++ b/src/calibre/customize/__init__.py @@ -474,7 +474,7 @@ class CatalogPlugin(Plugin): # {{{ return db.get_data_as_dict(ids=opts.ids) def get_output_fields(self, db, opts): - # Return a list of requested fields, with opts.sort_by first + # Return a list of requested fields all_std_fields = set( ['author_sort','authors','comments','cover','formats', 'id','isbn','library_name','ondevice','pubdate','publisher', @@ -489,7 +489,8 @@ class CatalogPlugin(Plugin): # {{{ if opts.fields != 'all': # Make a list from opts.fields - requested_fields = set(opts.fields.split(',')) + of = [x.strip() for x in opts.fields.split(',')] + requested_fields = set(of) # Validate requested_fields if requested_fields - all_fields: @@ -500,16 +501,13 @@ class CatalogPlugin(Plugin): # {{{ (current_library_name(), ', '.join(sorted(list(all_fields))))) raise ValueError("unable to generate catalog with specified fields") - fields = list(all_fields & requested_fields) + fields = [x for x in of if x in all_fields] else: - fields = list(all_fields) + fields = sorted(all_fields, key=self._field_sorter) if not opts.connected_device['is_device_connected'] and 'ondevice' in fields: fields.pop(int(fields.index('ondevice'))) - fields = sorted(fields, key=self._field_sorter) - if opts.sort_by and opts.sort_by in fields: - fields.insert(0,fields.pop(int(fields.index(opts.sort_by)))) return fields def initialize(self): diff --git a/src/calibre/gui2/catalog/catalog_csv_xml.py b/src/calibre/gui2/catalog/catalog_csv_xml.py index bbb765e0c6..4af77444da 100644 --- a/src/calibre/gui2/catalog/catalog_csv_xml.py +++ b/src/calibre/gui2/catalog/catalog_csv_xml.py @@ -7,11 +7,10 @@ __copyright__ = '2009, Kovid Goyal ' __docformat__ = 'restructuredtext en' from calibre.gui2 import gprefs -from calibre.gui2.catalog.catalog_csv_xml_ui import Ui_Form -from calibre.library import db as db_ -from PyQt5.Qt import QWidget, QListWidgetItem +from calibre.gui2.ui import get_gui +from PyQt5.Qt import QWidget, QListWidgetItem, Qt, QVBoxLayout, QLabel, QListWidget -class PluginWidget(QWidget, Ui_Form): +class PluginWidget(QWidget): TITLE = _('CSV/XML Options') HELP = _('Options specific to')+' CSV/XML '+_('output') @@ -20,42 +19,66 @@ class PluginWidget(QWidget, Ui_Form): def __init__(self, parent=None): QWidget.__init__(self, parent) - self.setupUi(self) + self.l = l = QVBoxLayout(self) + self.la = la = QLabel(_('Fields to include in output:')) + la.setWordWrap(True) + l.addWidget(la) + self.db_fields = QListWidget(self) + l.addWidget(self.db_fields) + self.la2 = la = QLabel(_('Drag and drop to re-arrange fields')) + l.addWidget(la) + self.db_fields.setDragEnabled(True) + self.db_fields.setDragDropMode(QListWidget.InternalMove) + self.db_fields.setDefaultDropAction(Qt.MoveAction) + self.db_fields.setAlternatingRowColors(True) + self.db_fields.setObjectName("db_fields") + + def initialize(self, catalog_name, db): + self.name = catalog_name from calibre.library.catalogs import FIELDS - self.all_fields = [] - for x in FIELDS: - if x != 'all': - self.all_fields.append(x) - QListWidgetItem(x, self.db_fields) + db = get_gui().current_db + self.all_fields = {x for x in FIELDS if x != 'all'} | set(db.custom_field_keys()) + sort_order = gprefs.get(self.name + '_db_fields_sort_order', {}) + fm = db.field_metadata - db = db_() - for x in sorted(db.custom_field_keys()): - self.all_fields.append(x) - QListWidgetItem(x, self.db_fields) + def name(x): + if x == 'isbn': + return 'ISBN' + if x == 'library_name': + return _('Library Name') + if x.endswith('_index'): + return name(x[:-len('_index')]) + ' ' + _('Number') + return fm[x].get('name') or x - fm = db.field_metadata[x] - if fm['datatype'] == 'series': - QListWidgetItem(x+'_index', self.db_fields) + def key(x): + return (sort_order.get(x, 10000), name(x)) + + self.db_fields.clear() + for x in sorted(self.all_fields, key=key): + QListWidgetItem(name(x) + ' (%s)' % x, self.db_fields).setData(Qt.UserRole, x) + if x.startswith('#') and fm[x]['datatype'] == 'series': + x += '_index' + QListWidgetItem(name(x) + ' (%s)' % x, self.db_fields).setData(Qt.UserRole, x) - def initialize(self, name, db): - self.name = name - fields = gprefs.get(name+'_db_fields', self.all_fields) # Restore the activated fields from last use + fields = frozenset(gprefs.get(self.name+'_db_fields', self.all_fields)) for x in range(self.db_fields.count()): item = self.db_fields.item(x) - item.setSelected(unicode(item.text()) in fields) + item.setCheckState(Qt.Checked if unicode(item.data(Qt.UserRole)) in fields else Qt.Unchecked) def options(self): # Save the currently activated fields - fields = [] - for x in range(self.db_fields.count()): + fields, all_fields = [], [] + for x in xrange(self.db_fields.count()): item = self.db_fields.item(x) - if item.isSelected(): - fields.append(unicode(item.text())) + all_fields.append(unicode(item.data(Qt.UserRole))) + if item.checkState() == Qt.Checked: + fields.append(unicode(item.data(Qt.UserRole))) gprefs.set(self.name+'_db_fields', fields) + gprefs.set(self.name + '_db_fields_sort_order', {x:i for i, x in enumerate(all_fields)}) # Return a dictionary with current options for this widget - if len(self.db_fields.selectedItems()): - return {'fields':[unicode(i.text()) for i in self.db_fields.selectedItems()]} + if len(fields): + return {'fields':fields} else: return {'fields':['all']} diff --git a/src/calibre/gui2/catalog/catalog_csv_xml.ui b/src/calibre/gui2/catalog/catalog_csv_xml.ui deleted file mode 100644 index 9634f66dcd..0000000000 --- a/src/calibre/gui2/catalog/catalog_csv_xml.ui +++ /dev/null @@ -1,47 +0,0 @@ - - - Form - - - - 0 - 0 - 579 - 411 - - - - Form - - - - - - Fields to include in output: - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - - - - - - 0 - 0 - - - - - - - QAbstractItemView::MultiSelection - - - - - - - -