Make the cover grid customizable

This commit is contained in:
Kovid Goyal 2013-08-02 16:23:24 +05:30
parent 5737ead466
commit 0541a118e6
4 changed files with 255 additions and 14 deletions

View File

@ -111,6 +111,10 @@ defs['tags_browser_category_icons'] = {}
defs['cover_browser_reflections'] = True defs['cover_browser_reflections'] = True
defs['extra_row_spacing'] = 0 defs['extra_row_spacing'] = 0
defs['refresh_book_list_on_bulk_edit'] = True defs['refresh_book_list_on_bulk_edit'] = True
defs['cover_grid_width'] = 0
defs['cover_grid_height'] = 0
defs['cover_grid_color'] = (80, 80, 80)
defs['cover_grid_cache_size'] = 200
del defs del defs
# }}} # }}}

View File

@ -20,8 +20,11 @@ from PyQt4.Qt import (
QUrl, QDrag, QPoint, QPainter, QRect) QUrl, QDrag, QPoint, QPainter, QRect)
from calibre import fit_image from calibre import fit_image
from calibre.gui2 import gprefs
from calibre.utils.config import prefs from calibre.utils.config import prefs
CM_TO_INCH = 0.393701
# Drag 'n Drop {{{ # Drag 'n Drop {{{
def dragMoveEvent(self, event): def dragMoveEvent(self, event):
event.acceptProposedAction() event.acceptProposedAction()
@ -305,15 +308,39 @@ class CoverCache(dict):
def __hash__(self): def __hash__(self):
return id(self) return id(self)
def set_limit(self, limit):
with self.lock:
self.limit = limit
if len(self.items) > self.limit:
extra = len(self.items) - self.limit
remove = tuple(self.iterkeys())[:extra]
for k in remove:
del self.items[k]
class CoverDelegate(QStyledItemDelegate): class CoverDelegate(QStyledItemDelegate):
def __init__(self, parent, width, height): def __init__(self, parent):
super(CoverDelegate, self).__init__(parent) super(CoverDelegate, self).__init__(parent)
self.set_dimensions()
self.cover_cache = CoverCache(limit=gprefs['cover_grid_cache_size'])
self.render_queue = Queue()
def set_dimensions(self):
width = self.original_width = gprefs['cover_grid_width']
height = self.original_height = gprefs['cover_grid_height']
if height < 0.1:
height = max(185, QApplication.instance().desktop().availableGeometry(self.parent()).height() / 5.0)
else:
height *= self.parent().logicalDpiY() * CM_TO_INCH
if width < 0.1:
width = 0.75 * height
else:
width *= self.parent().logicalDpiX() * CM_TO_INCH
self.cover_size = QSize(width, height) self.cover_size = QSize(width, height)
self.item_size = self.cover_size + QSize(8, 8) self.item_size = self.cover_size + QSize(8, 8)
self.spacing = max(10, min(50, int(0.1 * width))) self.spacing = max(10, min(50, int(0.1 * width)))
self.cover_cache = CoverCache()
self.render_queue = Queue()
def sizeHint(self, option, index): def sizeHint(self, option, index):
return self.item_size return self.item_size
@ -366,11 +393,7 @@ class GridView(QListView):
def __init__(self, parent): def __init__(self, parent):
QListView.__init__(self, parent) QListView.__init__(self, parent)
setup_dnd_interface(self) setup_dnd_interface(self)
pal = QPalette(self.palette()) self.set_color()
r = g = b = 0x50
pal.setColor(pal.Base, QColor(r, g, b))
pal.setColor(pal.Text, QColor(Qt.white if (r + g + b)/3.0 < 128 else Qt.black))
self.setPalette(pal)
self.setUniformItemSizes(True) self.setUniformItemSizes(True)
self.setWrapping(True) self.setWrapping(True)
self.setFlow(self.LeftToRight) self.setFlow(self.LeftToRight)
@ -380,7 +403,7 @@ class GridView(QListView):
self.setResizeMode(self.Adjust) self.setResizeMode(self.Adjust)
self.setSelectionMode(self.ExtendedSelection) self.setSelectionMode(self.ExtendedSelection)
self.setVerticalScrollMode(self.ScrollPerPixel) self.setVerticalScrollMode(self.ScrollPerPixel)
self.delegate = CoverDelegate(self, 135, 180) self.delegate = CoverDelegate(self)
self.setItemDelegate(self.delegate) self.setItemDelegate(self.delegate)
self.setSpacing(self.delegate.spacing) self.setSpacing(self.delegate.spacing)
self.ignore_render_requests = Event() self.ignore_render_requests = Event()
@ -390,6 +413,21 @@ class GridView(QListView):
self.gui = parent self.gui = parent
self.context_menu = None self.context_menu = None
def set_color(self):
r, g, b = gprefs['cover_grid_color']
pal = QPalette()
pal.setColor(pal.Base, QColor(r, g, b))
pal.setColor(pal.Text, QColor(Qt.white if (r + g + b)/3.0 < 128 else Qt.black))
self.setPalette(pal)
def refresh_settings(self):
if gprefs['cover_grid_width'] != self.delegate.original_width or gprefs['cover_grid_height'] != self.delegate.original_height:
self.delegate.set_dimensions()
self.setSpacing(self.delegate.spacing)
self.delegate.cover_cache.clear()
self.set_color()
self.delegate.cover_cache.set_limit(gprefs['cover_grid_cache_size'])
def shown(self): def shown(self):
if self.render_thread is None: if self.render_thread is None:
self.render_thread = Thread(target=self.render_covers) self.render_thread = Thread(target=self.render_covers)

View File

@ -5,8 +5,8 @@ __license__ = 'GPL v3'
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>' __copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
from PyQt4.Qt import (QApplication, QFont, QFontInfo, QFontDialog, from PyQt4.Qt import (QApplication, QFont, QFontInfo, QFontDialog, QColorDialog,
QAbstractListModel, Qt, QIcon, QKeySequence) QAbstractListModel, Qt, QIcon, QKeySequence, QPalette, QColor)
from calibre.gui2.preferences import ConfigWidgetBase, test_widget, CommaSeparatedList from calibre.gui2.preferences import ConfigWidgetBase, test_widget, CommaSeparatedList
from calibre.gui2.preferences.look_feel_ui import Ui_Form from calibre.gui2.preferences.look_feel_ui import Ui_Form
@ -18,7 +18,7 @@ from calibre.utils.icu import sort_key
from calibre.gui2.book_details import get_field_list from calibre.gui2.book_details import get_field_list
from calibre.gui2.preferences.coloring import EditRules from calibre.gui2.preferences.coloring import EditRules
class DisplayedFields(QAbstractListModel): # {{{ class DisplayedFields(QAbstractListModel): # {{{
def __init__(self, db, parent=None): def __init__(self, db, parent=None):
QAbstractListModel.__init__(self, parent) QAbstractListModel.__init__(self, parent)
@ -110,6 +110,9 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
r('tag_browser_old_look', gprefs, restart_required=True) r('tag_browser_old_look', gprefs, restart_required=True)
r('bd_show_cover', gprefs) r('bd_show_cover', gprefs)
r('bd_overlay_cover_size', gprefs) r('bd_overlay_cover_size', gprefs)
r('cover_grid_width', gprefs)
r('cover_grid_height', gprefs)
r('cover_grid_cache_size', gprefs)
r('cover_flow_queue_length', config, restart_required=True) r('cover_flow_queue_length', config, restart_required=True)
r('cover_browser_reflections', gprefs) r('cover_browser_reflections', gprefs)
@ -123,7 +126,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
lang = get_lang() lang = get_lang()
if lang is None or lang not in available_translations(): if lang is None or lang not in available_translations():
lang = 'en' lang = 'en'
items = [(l, get_esc_lang(l)) for l in available_translations() \ items = [(l, get_esc_lang(l)) for l in available_translations()
if l != lang] if l != lang]
if lang != 'en': if lang != 'en':
items.append(('en', get_esc_lang('en'))) items.append(('en', get_esc_lang('en')))
@ -170,7 +173,6 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
r('categories_using_hierarchy', db.prefs, setting=CommaSeparatedList, r('categories_using_hierarchy', db.prefs, setting=CommaSeparatedList,
choices=sorted(list(choices), key=sort_key)) choices=sorted(list(choices), key=sort_key))
self.current_font = self.initial_font = None self.current_font = self.initial_font = None
self.change_font_button.clicked.connect(self.change_font) self.change_font_button.clicked.connect(self.change_font)
@ -197,6 +199,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
keys = [unicode(x.toString(QKeySequence.NativeText)) for x in keys] keys = [unicode(x.toString(QKeySequence.NativeText)) for x in keys]
self.fs_help_msg.setText(unicode(self.fs_help_msg.text())%( self.fs_help_msg.setText(unicode(self.fs_help_msg.text())%(
_(' or ').join(keys))) _(' or ').join(keys)))
self.cover_grid_color_button.clicked.connect(self.change_cover_grid_color)
def initialize(self): def initialize(self):
ConfigWidgetBase.initialize(self) ConfigWidgetBase.initialize(self)
@ -215,6 +218,12 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
mi=None mi=None
self.edit_rules.initialize(db.field_metadata, db.prefs, mi, 'column_color_rules') self.edit_rules.initialize(db.field_metadata, db.prefs, mi, 'column_color_rules')
self.icon_rules.initialize(db.field_metadata, db.prefs, mi, 'column_icon_rules') self.icon_rules.initialize(db.field_metadata, db.prefs, mi, 'column_icon_rules')
self.set_cg_color(gprefs['cover_grid_color'])
def set_cg_color(self, val):
pal = QPalette()
pal.setColor(QPalette.Window, QColor(*val))
self.cover_grid_color_label.setPalette(pal)
def restore_defaults(self): def restore_defaults(self):
ConfigWidgetBase.restore_defaults(self) ConfigWidgetBase.restore_defaults(self)
@ -227,6 +236,15 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
self.edit_rules.clear() self.edit_rules.clear()
self.icon_rules.clear() self.icon_rules.clear()
self.changed_signal.emit() self.changed_signal.emit()
self.set_cg_color(gprefs.defaults['cover_grid_color'])
def change_cover_grid_color(self):
col = QColorDialog.getColor(self.cover_grid_color_label.palette().color(QPalette.Window),
self.gui, _('Choose background color for cover grid'))
if col.isValid():
col = tuple(col.getRgb())[:3]
self.set_cg_color(col)
self.changed_signal.emit()
def build_font_obj(self): def build_font_obj(self):
font_info = self.current_font font_info = self.current_font
@ -286,6 +304,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
self.display_model.commit() self.display_model.commit()
self.edit_rules.commit(self.gui.current_db.prefs) self.edit_rules.commit(self.gui.current_db.prefs)
self.icon_rules.commit(self.gui.current_db.prefs) self.icon_rules.commit(self.gui.current_db.prefs)
gprefs['cover_grid_color'] = tuple(self.cover_grid_color_label.palette().color(QPalette.Window).getRgb())[:3]
return rr return rr
def refresh_gui(self, gui): def refresh_gui(self, gui):
@ -296,9 +315,11 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
if hasattr(gui.cover_flow, 'setShowReflections'): if hasattr(gui.cover_flow, 'setShowReflections'):
gui.cover_flow.setShowReflections(gprefs['cover_browser_reflections']) gui.cover_flow.setShowReflections(gprefs['cover_browser_reflections'])
gui.library_view.refresh_row_sizing() gui.library_view.refresh_row_sizing()
gui.grid_view.refresh_settings()
if __name__ == '__main__': if __name__ == '__main__':
from calibre.gui2 import Application from calibre.gui2 import Application
app = Application([]) app = Application([])
test_widget('Interface', 'Look & Feel') test_widget('Interface', 'Look & Feel')

View File

@ -223,6 +223,184 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="tab_5">
<attribute name="icon">
<iconset resource="../../../../resources/images.qrc">
<normaloff>:/images/grid.png</normaloff>:/images/grid.png</iconset>
</attribute>
<attribute name="title">
<string>Cover Grid</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_11">
<property name="text">
<string>Cover &amp;Width: </string>
</property>
<property name="buddy">
<cstring>opt_cover_grid_width</cstring>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="opt_cover_grid_width">
<property name="toolTip">
<string>The width of displayed covers</string>
</property>
<property name="suffix">
<string> cm</string>
</property>
<property name="decimals">
<number>1</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_12">
<property name="text">
<string>Cover &amp;Height: </string>
</property>
<property name="buddy">
<cstring>opt_cover_grid_height</cstring>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="opt_cover_grid_height">
<property name="toolTip">
<string>The height of displayed covers</string>
</property>
<property name="suffix">
<string> cm</string>
</property>
<property name="decimals">
<number>1</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_13">
<property name="text">
<string>A value of zero means set automatically based on screen size</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label_14">
<property name="text">
<string>Background color for the cover grid:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="cover_grid_color_label">
<property name="minimumSize">
<size>
<width>50</width>
<height>50</height>
</size>
</property>
<property name="autoFillBackground">
<bool>true</bool>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="cover_grid_color_button">
<property name="text">
<string>Change &amp;color</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QLabel" name="label_15">
<property name="text">
<string>Number of covers to cache in &amp;memory:</string>
</property>
<property name="buddy">
<cstring>opt_cover_grid_cache_size</cstring>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="opt_cover_grid_cache_size">
<property name="maximum">
<number>3000</number>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>355</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_4"> <widget class="QWidget" name="tab_4">
<attribute name="icon"> <attribute name="icon">
<iconset resource="../../../../resources/images.qrc"> <iconset resource="../../../../resources/images.qrc">