Allow auto scrolling through the list of books by pressing the X key. The speed of scrolling can be controlled in Preferences->Look & feel->Cover browser. Useful to have a "slideshow" of book covers

Fixes #1917634 [[Enhancement] Add slideshow capability to cover browser](https://bugs.launchpad.net/calibre/+bug/1917634)
This commit is contained in:
Kovid Goyal 2021-04-15 18:01:48 +05:30
parent 095515b6e8
commit 75333c2b54
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
12 changed files with 138 additions and 34 deletions

View File

@ -782,3 +782,5 @@ calibre has several keyboard shortcuts to save you time and mouse movement. Thes
- Re-apply the current sort - Re-apply the current sort
* - :kbd:`Ctrl+Q` * - :kbd:`Ctrl+Q`
- Quit calibre - Quit calibre
* - :kbd:`X`
- Toggle autoscroll of the book list

View File

@ -970,6 +970,12 @@ class ActionOpenFolder(InterfaceActionBase):
' calibre library') ' calibre library')
class ActionAutoscrollBooks(InterfaceActionBase):
name = 'Autoscroll Books'
actual_plugin = 'calibre.gui2.actions.auto_scroll:AutoscrollBooksAction'
description = _('Auto scroll through the list of books')
class ActionSendToDevice(InterfaceActionBase): class ActionSendToDevice(InterfaceActionBase):
name = 'Send To Device' name = 'Send To Device'
actual_plugin = 'calibre.gui2.actions.device:SendToDeviceAction' actual_plugin = 'calibre.gui2.actions.device:SendToDeviceAction'
@ -1115,7 +1121,7 @@ plugins += [ActionAdd, ActionFetchAnnotations, ActionGenerateCatalog,
ActionCopyToLibrary, ActionTweakEpub, ActionUnpackBook, ActionNextMatch, ActionStore, ActionCopyToLibrary, ActionTweakEpub, ActionUnpackBook, ActionNextMatch, ActionStore,
ActionPluginUpdater, ActionPickRandom, ActionEditToC, ActionSortBy, ActionPluginUpdater, ActionPickRandom, ActionEditToC, ActionSortBy,
ActionMarkBooks, ActionEmbed, ActionTemplateTester, ActionTagMapper, ActionAuthorMapper, ActionMarkBooks, ActionEmbed, ActionTemplateTester, ActionTagMapper, ActionAuthorMapper,
ActionVirtualLibrary, ActionBrowseAnnotations, ActionTemplateFunctions] ActionVirtualLibrary, ActionBrowseAnnotations, ActionTemplateFunctions, ActionAutoscrollBooks]
# }}} # }}}

View File

@ -2219,7 +2219,7 @@ class Cache(object):
for book in self._search(expr, virtual_fields=virtual_fields): for book in self._search(expr, virtual_fields=virtual_fields):
c[book].append(lib) c[book].append(lib)
except Exception as e: except Exception as e:
c[book].append(_('[Error in virtual library {0}: {1}]').format(lib, str(e))) c[book].append(_('[Error in Virtual library {0}: {1}]').format(lib, str(e)))
self.vls_for_books_cache = {b:tuple(sorted(libs, key=sort_key)) for b, libs in c.items()} self.vls_for_books_cache = {b:tuple(sorted(libs, key=sort_key)) for b, libs in c.items()}
if not book_ids: if not book_ids:
book_ids = self._all_book_ids() book_ids = self._all_book_ids()

View File

@ -124,7 +124,8 @@ def create_defs():
'Edit Metadata', 'Send To Device', 'Save To Disk', 'Edit Metadata', 'Send To Device', 'Save To Disk',
'Connect Share', 'Copy To Library', None, 'Connect Share', 'Copy To Library', None,
'Convert Books', 'View', 'Open Folder', 'Show Book Details', 'Convert Books', 'View', 'Open Folder', 'Show Book Details',
'Similar Books', 'Tweak ePub', None, 'Remove Books', 'Similar Books', 'Tweak ePub', None, 'Remove Books', None,
'Autoscroll Books'
) )
defs['show_splash_screen'] = True defs['show_splash_screen'] = True
@ -198,6 +199,7 @@ def create_defs():
defs['browse_annots_restrict_to_type'] = None defs['browse_annots_restrict_to_type'] = None
defs['browse_annots_use_stemmer'] = True defs['browse_annots_use_stemmer'] = True
defs['annots_export_format'] = 'txt' defs['annots_export_format'] = 'txt'
defs['books_autoscroll_time'] = 2.0
create_defs() create_defs()

View File

@ -0,0 +1,23 @@
#!/usr/bin/env python
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
# License: GPLv3 Copyright: 2015, Kovid Goyal <kovid at kovidgoyal.net>
from calibre.gui2.actions import InterfaceAction
class AutoscrollBooksAction(InterfaceAction):
name = 'Autoscroll Books'
action_spec = (_('Auto scroll through the book list'), 'auto-scroll.png',
_('Auto scroll through the book list, particularly useful with the cover browser open'), _('X'))
dont_add_to = frozenset(('context-menu-device', 'menubar-device'))
action_type = 'current'
def genesis(self):
self.qaction.triggered.connect(self.gui.toggle_auto_scroll)
def location_selected(self, loc):
enabled = loc == 'library'
self.qaction.setEnabled(enabled)
self.menuless_qaction.setEnabled(enabled)

View File

@ -9,15 +9,19 @@ __docformat__ = 'restructuredtext en'
Module to implement the Cover Flow feature Module to implement the Cover Flow feature
''' '''
import sys, os, time import os
import sys
import time
from qt.core import (
QAction, QApplication, QDialog, QFont, QImage, QItemSelectionModel,
QKeySequence, QLabel, QSize, QSizePolicy, QStackedLayout, Qt, QTimer, pyqtSignal
)
from qt.core import (QImage, QSizePolicy, QTimer, QDialog, Qt, QSize, QAction,
QStackedLayout, QLabel, pyqtSignal, QKeySequence, QFont, QApplication, QItemSelectionModel)
from calibre.ebooks.metadata import rating_to_stars
from calibre.constants import islinux from calibre.constants import islinux
from calibre.gui2 import (config, available_height, available_width, gprefs, from calibre.ebooks.metadata import rating_to_stars
rating_font) from calibre.gui2 import (
available_height, available_width, config, gprefs, rating_font
)
from calibre_extensions import pictureflow from calibre_extensions import pictureflow
@ -190,6 +194,12 @@ class CoverFlow(pictureflow.PictureFlow):
if not gprefs['cover_browser_reflections']: if not gprefs['cover_browser_reflections']:
self.setShowReflections(False) self.setShowReflections(False)
def one_auto_scroll(self):
if self.currentSlide() >= self.count() - 1:
self.setCurrentSlide(0)
else:
self.showNext()
def set_subtitle_font(self, for_ratings=True): def set_subtitle_font(self, for_ratings=True):
if for_ratings: if for_ratings:
self.setSubtitleFont(QFont(rating_font())) self.setSubtitleFont(QFont(rating_font()))
@ -295,6 +305,28 @@ class CoverFlowMixin(object):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
pass pass
def one_auto_scroll(self):
cb_visible = self.cover_flow is not None and self.cb_splitter.button.isChecked()
if cb_visible:
self.cover_flow.one_auto_scroll()
else:
self.library_view.show_next_book()
def toggle_auto_scroll(self):
if not hasattr(self, 'auto_scroll_timer'):
self.auto_scroll_timer = t = QTimer(self)
t.timeout.connect(self.one_auto_scroll)
if self.auto_scroll_timer.isActive():
self.auto_scroll_timer.stop()
else:
self.one_auto_scroll()
self.auto_scroll_timer.start(int(1000 * gprefs['books_autoscroll_time']))
def update_auto_scroll_timeout(self):
if hasattr(self, 'auto_scroll_timer') and self.auto_scroll_timer.isActive():
self.auto_scroll_timer.stop()
self.toggle_auto_scroll()
def init_cover_flow_mixin(self): def init_cover_flow_mixin(self):
self.cover_flow = None self.cover_flow = None
self.cf_last_updated_at = None self.cf_last_updated_at = None

View File

@ -1290,6 +1290,14 @@ class BooksView(QTableView): # {{{
self.set_current_row(row, select=False) self.set_current_row(row, select=False)
break break
def show_next_book(self):
ci = self.currentIndex()
if not ci.isValid():
self.set_current_row()
return
n = (ci.row() + 1) % self.model().rowCount(QModelIndex())
self.set_current_row(n)
@property @property
def next_id(self): def next_id(self):
''' '''

View File

@ -1518,6 +1518,7 @@ void PictureFlow::timerEvent(QTimerEvent* event)
void PictureFlow::dataChanged() { d->dataChanged(); } void PictureFlow::dataChanged() { d->dataChanged(); }
void PictureFlow::emitcurrentChanged(int index) { emit currentChanged(index); } void PictureFlow::emitcurrentChanged(int index) { emit currentChanged(index); }
int PictureFlow::count() const { return d->slideCount(); }
int FlowImages::count() { return 0; } int FlowImages::count() { return 0; }
QImage FlowImages::image(int index) { Q_UNUSED(index); return QImage(); } QImage FlowImages::image(int index) { Q_UNUSED(index); return QImage(); }

View File

@ -1,6 +1,6 @@
/**************************************************************************** /****************************************************************************
** **
* (C) Copyright 2007 Trolltech ASA * (C) Copyright 2007 Trolltech ASA
* All rights reserved. * All rights reserved.
** **
* This is version of the Pictureflow animated image show widget modified by Trolltech ASA. * This is version of the Pictureflow animated image show widget modified by Trolltech ASA.
@ -77,15 +77,15 @@ signals:
class PictureFlowPrivate; class PictureFlowPrivate;
/*! /*!
Class PictureFlow implements an image show widget with animation effect Class PictureFlow implements an image show widget with animation effect
like Apple's CoverFlow (in iTunes and iPod). Images are arranged in form like Apple's CoverFlow (in iTunes and iPod). Images are arranged in form
of slides, one main slide is shown at the center with few slides on of slides, one main slide is shown at the center with few slides on
the left and right sides of the center slide. When the next or previous the left and right sides of the center slide. When the next or previous
slide is brought to the front, the whole slides flow to the right or slide is brought to the front, the whole slides flow to the right or
the right with smooth animation effect; until the new slide is finally the right with smooth animation effect; until the new slide is finally
placed at the center. placed at the center.
*/ */
class PictureFlow : public QWidget class PictureFlow : public QWidget
{ {
Q_OBJECT Q_OBJECT
@ -98,7 +98,7 @@ Q_OBJECT
public: public:
/*! /*!
Creates a new PictureFlow widget. Creates a new PictureFlow widget.
*/ */
PictureFlow(QWidget* parent = 0, int queueLength = 3); PictureFlow(QWidget* parent = 0, int queueLength = 3);
/*! /*!
@ -110,42 +110,44 @@ public:
Set the images to be displayed by this widget. Set the images to be displayed by this widget.
*/ */
void setImages(FlowImages *images); void setImages(FlowImages *images);
int count() const;
/*! /*!
Returns the dimension of each slide (in pixels). Returns the dimension of each slide (in pixels).
*/ */
QSize slideSize() const; QSize slideSize() const;
/*! /*!
Sets the dimension of each slide (in pixels). Do not use this method directly Sets the dimension of each slide (in pixels). Do not use this method directly
instead use resize which automatically sets an appropriate slide size. instead use resize which automatically sets an appropriate slide size.
*/ */
void setSlideSize(QSize size); void setSlideSize(QSize size);
/*! /*!
Returns whether aspect ration is preserved when scaling images Returns whether aspect ration is preserved when scaling images
*/ */
bool preserveAspectRatio() const; bool preserveAspectRatio() const;
/*! /*!
Whether to preserve aspect ration when scaling images Whether to preserve aspect ration when scaling images
*/ */
void setPreserveAspectRatio(bool preserve); void setPreserveAspectRatio(bool preserve);
/*! /*!
Turn the reflections on/off. Turn the reflections on/off.
*/ */
void setShowReflections(bool show); void setShowReflections(bool show);
bool showReflections() const; bool showReflections() const;
/*! /*!
Returns the font used to render subtitles Returns the font used to render subtitles
*/ */
QFont subtitleFont() const; QFont subtitleFont() const;
/*! /*!
Sets the font used to render subtitles Sets the font used to render subtitles
*/ */
void setSubtitleFont(QFont font); void setSubtitleFont(QFont font);
@ -158,20 +160,20 @@ public:
Returns QImage of specified slide. Returns QImage of specified slide.
This function will be called only whenever necessary, e.g. the 100th slide This function will be called only whenever necessary, e.g. the 100th slide
will not be retrived when only the first few slides are visible. will not be retrived when only the first few slides are visible.
*/ */
virtual QImage slide(int index) const; virtual QImage slide(int index) const;
/*! /*!
Returns the index of slide currently shown in the middle of the viewport. Returns the index of slide currently shown in the middle of the viewport.
*/ */
int currentSlide() const; int currentSlide() const;
public slots: public slots:
/*! /*!
Sets slide to be shown in the middle of the viewport. No animation Sets slide to be shown in the middle of the viewport. No animation
effect will be produced, unlike using showSlide. effect will be produced, unlike using showSlide.
*/ */
void setCurrentSlide(int index); void setCurrentSlide(int index);
/*! /*!
@ -194,12 +196,12 @@ public slots:
Go to specified slide using animation effect. Go to specified slide using animation effect.
*/ */
void showSlide(int index); void showSlide(int index);
/*! /*!
Clear all caches and redraw Clear all caches and redraw
*/ */
void dataChanged(); void dataChanged();
void emitcurrentChanged(int index); void emitcurrentChanged(int index);
signals: signals:
@ -221,4 +223,3 @@ private:
qreal device_pixel_ratio() const; qreal device_pixel_ratio() const;
qreal last_device_pixel_ratio; qreal last_device_pixel_ratio;
}; };

View File

@ -35,6 +35,7 @@ public :
PictureFlow(QWidget *parent /TransferThis/ = 0, int queueLength = 3); PictureFlow(QWidget *parent /TransferThis/ = 0, int queueLength = 3);
void setImages(FlowImages *images); void setImages(FlowImages *images);
int count();
QSize slideSize() const; QSize slideSize() const;

View File

@ -425,6 +425,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
r('cover_grid_show_title', gprefs) r('cover_grid_show_title', gprefs)
r('tag_browser_show_counts', gprefs) r('tag_browser_show_counts', gprefs)
r('tag_browser_item_padding', gprefs) r('tag_browser_item_padding', gprefs)
r('books_autoscroll_time', gprefs)
r('qv_respects_vls', gprefs) r('qv_respects_vls', gprefs)
r('qv_dclick_changes_column', gprefs) r('qv_dclick_changes_column', gprefs)
@ -806,6 +807,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
getattr(gui, view + '_view').set_row_header_visibility() getattr(gui, view + '_view').set_row_header_visibility()
gui.library_view.refresh_row_sizing() gui.library_view.refresh_row_sizing()
gui.grid_view.refresh_settings() gui.grid_view.refresh_settings()
gui.update_auto_scroll_timeout()
qv = get_quickview_action_plugin() qv = get_quickview_action_plugin()
if qv: if qv:
qv.refill_quickview() qv.refill_quickview()

View File

@ -1251,6 +1251,32 @@ them to all have the same width and height</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="9" column="0">
<widget class="QLabel" name="label_26">
<property name="text">
<string>Show ne&amp;xt cover during auto scroll after:</string>
</property>
<property name="buddy">
<cstring>opt_books_autoscroll_time</cstring>
</property>
</widget>
</item>
<item row="9" column="1">
<widget class="QDoubleSpinBox" name="opt_books_autoscroll_time">
<property name="suffix">
<string> seconds</string>
</property>
<property name="decimals">
<number>1</number>
</property>
<property name="minimum">
<double>1.000000000000000</double>
</property>
<property name="maximum">
<double>100000.000000000000000</double>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="quickview_tab"> <widget class="QWidget" name="quickview_tab">