From a6e8d0eb71240269f5dfde3a80e1820591923fbf Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 5 Nov 2017 15:04:57 +0530 Subject: [PATCH] Allow configuring the metadata fields displayed in the popup book details window. To configure, simply click the 'Configure' link at the bottom of the window. --- src/calibre/db/backend.py | 1 + src/calibre/gui2/book_details.py | 31 +++---- src/calibre/gui2/dialogs/book_info.py | 98 ++++++++++++++++++++--- src/calibre/gui2/preferences/look_feel.py | 58 +++++++------- src/calibre/gui2/ui.py | 8 +- src/calibre/gui2/viewer/ui.py | 2 +- 6 files changed, 138 insertions(+), 60 deletions(-) diff --git a/src/calibre/db/backend.py b/src/calibre/db/backend.py index 1ed2e6c661..57c593e15f 100644 --- a/src/calibre/db/backend.py +++ b/src/calibre/db/backend.py @@ -471,6 +471,7 @@ class DB(object): ('uuid', False), ('comments', True), ('id', False), ('pubdate', False), ('last_modified', False), ('size', False), ('languages', False), ] + defs['popup_book_display_fields'] = [('title', True)] + [(f[0], True) for f in defs['book_display_fields'] if f[0] != 'title'] defs['qv_display_fields'] = [('title', True), ('authors', True), ('series', True)] defs['virtual_libraries'] = {} defs['virtual_lib_on_startup'] = defs['cs_virtual_lib_on_startup'] = '' diff --git a/src/calibre/gui2/book_details.py b/src/calibre/gui2/book_details.py index b1abe7b130..23ac90acc5 100644 --- a/src/calibre/gui2/book_details.py +++ b/src/calibre/gui2/book_details.py @@ -87,9 +87,14 @@ def init_manage_action(ac, field, value): return ac -def render_html(mi, css, vertical, widget, all_fields=False, render_data_func=None): # {{{ - table, comment_fields = (render_data_func or render_data)(mi, all_fields=all_fields, - use_roman_numbers=config['use_roman_numerals_for_series_number']) +def render_html(mi, css, vertical, widget, all_fields=False, render_data_func=None, pref_name='book_display_fields'): # {{{ + func = render_data_func or render_data + try: + table, comment_fields = func(mi, all_fields=all_fields, + use_roman_numbers=config['use_roman_numerals_for_series_number'], pref_name=pref_name) + except TypeError: + table, comment_fields = func(mi, all_fields=all_fields, + use_roman_numbers=config['use_roman_numerals_for_series_number']) def color_to_string(col): ans = '#000000' @@ -145,29 +150,27 @@ def render_html(mi, css, vertical, widget, all_fields=False, render_data_func=No return ans -def get_field_list(fm, use_defaults=False): +def get_field_list(fm, use_defaults=False, pref_name='book_display_fields'): from calibre.gui2.ui import get_gui db = get_gui().current_db if use_defaults: src = db.prefs.defaults else: - old_val = gprefs.get('book_display_fields', None) - if old_val is not None and not db.prefs.has_setting( - 'book_display_fields'): + old_val = gprefs.get(pref_name, None) + if old_val is not None and not db.prefs.has_setting(pref_name): src = gprefs else: src = db.prefs - fieldlist = list(src['book_display_fields']) - names = frozenset([x[0] for x in fieldlist]) - for field in fm.displayable_field_keys(): - if field not in names: - fieldlist.append((field, True)) + fieldlist = list(src[pref_name]) + names = frozenset(x[0] for x in fieldlist) available = frozenset(fm.displayable_field_keys()) + for field in available - names: + fieldlist.append((field, True)) return [(f, d) for f, d in fieldlist if f in available] -def render_data(mi, use_roman_numbers=True, all_fields=False): - field_list = get_field_list(getattr(mi, 'field_metadata', field_metadata)) +def render_data(mi, use_roman_numbers=True, all_fields=False, pref_name='book_display_fields'): + field_list = get_field_list(getattr(mi, 'field_metadata', field_metadata), pref_name=pref_name) field_list = [(x, all_fields or display) for x, display in field_list] return mi_to_html(mi, field_list=field_list, use_roman_numbers=use_roman_numbers, rtl=is_rtl(), rating_font=rating_font(), default_author_link=default_author_link()) diff --git a/src/calibre/gui2/dialogs/book_info.py b/src/calibre/gui2/dialogs/book_info.py index 324f75bfe7..bb24ce0d80 100644 --- a/src/calibre/gui2/dialogs/book_info.py +++ b/src/calibre/gui2/dialogs/book_info.py @@ -1,21 +1,71 @@ #!/usr/bin/env python2 -from __future__ import (unicode_literals, division, absolute_import, - print_function) -__license__ = 'GPL v3' -__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net' -__docformat__ = 'restructuredtext en' +# License: GPLv3 Copyright: 2008, Kovid Goyal +from __future__ import absolute_import, division, print_function, unicode_literals +from functools import partial from PyQt5.Qt import ( - QCoreApplication, QModelIndex, QTimer, Qt, pyqtSignal, QWidget, - QGridLayout, QDialog, QPixmap, QSize, QPalette, QShortcut, QKeySequence, - QSplitter, QVBoxLayout, QCheckBox, QPushButton, QIcon, QBrush) + QBrush, QCheckBox, QCoreApplication, QDialog, QGridLayout, QHBoxLayout, QIcon, + QKeySequence, QLabel, QListView, QModelIndex, QPalette, QPixmap, QPushButton, + QShortcut, QSize, QSplitter, Qt, QTimer, QToolButton, QVBoxLayout, QWidget, + pyqtSignal +) from PyQt5.QtWebKitWidgets import QWebView -from calibre.gui2 import gprefs, NO_URL_FORMATTING from calibre import fit_image -from calibre.gui2.book_details import render_html, details_context_menu_event, css +from calibre.gui2 import NO_URL_FORMATTING, gprefs +from calibre.gui2.book_details import css, details_context_menu_event, render_html +from calibre.gui2.ui import get_gui from calibre.gui2.widgets import CoverView +from calibre.gui2.widgets2 import Dialog + + +class Configure(Dialog): + + def __init__(self, db, parent=None): + self.db = db + Dialog.__init__(self, _('Configure the book details window'), 'book-details-popup-conf', parent) + + def setup_ui(self): + from calibre.gui2.preferences.look_feel import DisplayedFields, move_field_up, move_field_down + self.l = QVBoxLayout(self) + self.field_display_order = fdo = QListView(self) + self.model = DisplayedFields(self.db, fdo, pref_name='popup_book_display_fields') + self.model.initialize() + fdo.setModel(self.model) + fdo.setAlternatingRowColors(True) + del self.db + self.l.addWidget(QLabel('Select displayed metadata')) + h = QHBoxLayout() + h.addWidget(fdo) + v = QVBoxLayout() + self.mub = b = QToolButton(self) + b.clicked.connect(partial(move_field_up, fdo, self.model)) + b.setIcon(QIcon(I('arrow-up.png'))) + b.setToolTip(_('Move the selected field up')) + v.addWidget(b), v.addStretch(10) + self.mud = b = QToolButton(self) + b.setIcon(QIcon(I('arrow-down.png'))) + b.setToolTip(_('Move the selected field down')) + b.clicked.connect(partial(move_field_down, fdo, self.model)) + v.addWidget(b) + h.addLayout(v) + + self.l.addLayout(h) + self.l.addWidget(QLabel('

' + _( + 'Note that comments will always be displayed at the end, regardless of the order you assign here'))) + + b = self.bb.addButton(_('Restore &defaults'), self.bb.ActionRole) + b.clicked.connect(self.restore_defaults) + self.l.addWidget(self.bb) + self.setMinimumHeight(500) + + def restore_defaults(self): + self.model.initialize(use_defaults=True) + + def accept(self): + self.model.commit() + return Dialog.accept(self) class Details(QWebView): @@ -74,7 +124,11 @@ class BookInfo(QDialog): self.fit_cover = QCheckBox(_('Fit &cover within view'), self) self.fit_cover.setChecked(gprefs.get('book_info_dialog_fit_cover', True)) - l2.addWidget(self.fit_cover, l2.rowCount(), 0, 1, -1) + l2.addWidget(self.fit_cover, l2.rowCount(), 0, 1, 1) + self.clabel = QLabel('

{}'.format( + _('Configure this view'), _('Configure'))) + self.clabel.linkActivated.connect(self.configure) + l2.addWidget(self.clabel, l2.rowCount() - 1, 1, 1, 1) self.previous_button = QPushButton(QIcon(I('previous.png')), _('&Previous'), self) self.previous_button.clicked.connect(self.previous) l2.addWidget(self.previous_button, l2.rowCount(), 0) @@ -108,6 +162,14 @@ class BookInfo(QDialog): except Exception: pass + def configure(self): + d = Configure(get_gui().current_db, self) + if d.exec_() == d.Accepted: + if self.current_row is not None: + mi = self.view.model().get_book_display_info(self.current_row) + if mi is not None: + self.refresh(self.current_row, mi=mi) + def link_clicked(self, qurl): link = unicode(qurl.toString(NO_URL_FORMATTING)) self.link_delegate(link) @@ -212,8 +274,20 @@ class BookInfo(QDialog): dpr = self.devicePixelRatio() self.cover_pixmap.setDevicePixelRatio(dpr) self.resize_cover() - html = render_html(mi, self.css, True, self, all_fields=True) + html = render_html(mi, self.css, True, self, pref_name='popup_book_display_fields') self.details.setHtml(html) self.marked = mi.marked self.cover.setBackgroundBrush(self.marked_brush if mi.marked else self.normal_brush) self.update_cover_tooltip() + + +if __name__ == '__main__': + from calibre.gui2 import Application + from calibre.library import db + app = Application([]) + app.current_db = db() + get_gui.ans = app + d = Configure(app.current_db) + d.exec_() + del d + del app diff --git a/src/calibre/gui2/preferences/look_feel.py b/src/calibre/gui2/preferences/look_feel.py index 0d6c46cbaa..4b538fa566 100644 --- a/src/calibre/gui2/preferences/look_feel.py +++ b/src/calibre/gui2/preferences/look_feel.py @@ -217,18 +217,20 @@ class IdLinksEditor(Dialog): class DisplayedFields(QAbstractListModel): # {{{ - def __init__(self, db, parent=None): + def __init__(self, db, parent=None, pref_name=None): + self.pref_name = pref_name or 'book_display_fields' QAbstractListModel.__init__(self, parent) self.fields = [] self.db = db self.changed = False + def get_field_list(self, use_defaults=False): + return get_field_list(self.db.field_metadata, use_defaults=use_defaults, pref_name=self.pref_name) + def initialize(self, use_defaults=False): self.beginResetModel() - self.fields = [[x[0], x[1]] for x in - get_field_list(self.db.field_metadata, - use_defaults=use_defaults)] + self.fields = [[x[0], x[1]] for x in self.get_field_list(use_defaults=use_defaults)] self.endResetModel() self.changed = True @@ -273,7 +275,7 @@ class DisplayedFields(QAbstractListModel): # {{{ def commit(self): if self.changed: - self.db.new_api.set_pref('book_display_fields', self.fields) + self.db.new_api.set_pref(self.pref_name, self.fields) def move(self, idx, delta): row = idx.row() + delta @@ -287,6 +289,26 @@ class DisplayedFields(QAbstractListModel): # {{{ self.changed = True return idx + +def move_field_up(widget, model): + idx = widget.currentIndex() + if idx.isValid(): + idx = model.move(idx, -1) + if idx is not None: + sm = widget.selectionModel() + sm.select(idx, sm.ClearAndSelect) + widget.setCurrentIndex(idx) + + +def move_field_down(widget, model): + idx = widget.currentIndex() + if idx.isValid(): + idx = model.move(idx, 1) + if idx is not None: + sm = widget.selectionModel() + sm.select(idx, sm.ClearAndSelect) + widget.setCurrentIndex(idx) + # }}} @@ -478,18 +500,18 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): self.field_display_order) self.display_model.dataChanged.connect(self.changed_signal) self.field_display_order.setModel(self.display_model) - self.df_up_button.clicked.connect(partial(self.move_field_up, + self.df_up_button.clicked.connect(partial(move_field_up, self.field_display_order, self.display_model)) - self.df_down_button.clicked.connect(partial(self.move_field_down, + self.df_down_button.clicked.connect(partial(move_field_down, self.field_display_order, self.display_model)) self.qv_display_model = QVDisplayedFields(self.gui.current_db, self.qv_display_order) self.qv_display_model.dataChanged.connect(self.changed_signal) self.qv_display_order.setModel(self.qv_display_model) - self.qv_up_button.clicked.connect(partial(self.move_field_up, + self.qv_up_button.clicked.connect(partial(move_field_up, self.qv_display_order, self.qv_display_model)) - self.qv_down_button.clicked.connect(partial(self.move_field_down, + self.qv_down_button.clicked.connect(partial(move_field_down, self.qv_display_order, self.qv_display_model)) self.edit_rules = EditRules(self.tabWidget) @@ -702,24 +724,6 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): self.font_display.setText(name + ' [%dpt]'%fi.pointSize()) - def move_field_up(self, widget, model): - idx = widget.currentIndex() - if idx.isValid(): - idx = model.move(idx, -1) - if idx is not None: - sm = widget.selectionModel() - sm.select(idx, sm.ClearAndSelect) - widget.setCurrentIndex(idx) - - def move_field_down(self, widget, model): - idx = widget.currentIndex() - if idx.isValid(): - idx = model.move(idx, 1) - if idx is not None: - sm = widget.selectionModel() - sm.select(idx, sm.ClearAndSelect) - widget.setCurrentIndex(idx) - def change_font(self, *args): fd = QFontDialog(self.build_font_obj(), self) if fd.exec_() == fd.Accepted: diff --git a/src/calibre/gui2/ui.py b/src/calibre/gui2/ui.py index c60fb139f8..aaa490539c 100644 --- a/src/calibre/gui2/ui.py +++ b/src/calibre/gui2/ui.py @@ -87,11 +87,8 @@ class Listener(Thread): # {{{ # }}} -_gui = None - - def get_gui(): - return _gui + return getattr(get_gui, 'ans', None) def add_quick_start_guide(library_view, refresh_cover_browser=None): @@ -138,7 +135,6 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{ shutting_down = False def __init__(self, opts, parent=None, gui_debug=None): - global _gui MainWindow.__init__(self, opts, parent=parent, disable_automatic_gc=True) self.setWindowIcon(QApplication.instance().windowIcon()) self.jobs_pointer = Pointer(self) @@ -147,7 +143,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{ self.proceed_question = ProceedQuestion(self) self.job_error_dialog = JobError(self) self.keyboard = Manager(self) - _gui = self + get_gui.ans = self self.opts = opts self.device_connected = None self.gui_debug = gui_debug diff --git a/src/calibre/gui2/viewer/ui.py b/src/calibre/gui2/viewer/ui.py index 51d0ee3c5b..ce7b9c4102 100644 --- a/src/calibre/gui2/viewer/ui.py +++ b/src/calibre/gui2/viewer/ui.py @@ -93,7 +93,7 @@ class Metadata(QWebView): # {{{ from calibre.gui2.book_details import render_html, css from calibre.ebooks.metadata.book.render import mi_to_html - def render_data(mi, use_roman_numbers=True, all_fields=False): + def render_data(mi, use_roman_numbers=True, all_fields=False, pref_name='book_display_fields'): return mi_to_html( mi, use_roman_numbers=use_roman_numbers, rating_font=rating_font(), rtl=is_rtl(), default_author_link=default_author_link()