Show the number of currently selected books in the status bar at the bottom of the book list

This commit is contained in:
Kovid Goyal 2013-05-19 14:25:09 +05:30
parent 70340d8b7c
commit 2c12c1aa3d
3 changed files with 77 additions and 21 deletions

View File

@ -161,18 +161,18 @@ class StatusBar(QStatusBar): # {{{
def __init__(self, parent=None): def __init__(self, parent=None):
QStatusBar.__init__(self, parent) QStatusBar.__init__(self, parent)
self.default_message = __appname__ + ' ' + _('version') + ' ' + \
self.get_version() + ' ' + _('created by Kovid Goyal')
self.device_string = '' self.device_string = ''
self.update_label = UpdateLabel('') self.update_label = UpdateLabel('')
self.total = self.current = self.selected = 0
self.addPermanentWidget(self.update_label) self.addPermanentWidget(self.update_label)
self.update_label.setVisible(False) self.update_label.setVisible(False)
self._font = QFont() self._font = QFont()
self._font.setBold(True) self._font.setBold(True)
self.setFont(self._font) self.setFont(self._font)
self.defmsg = QLabel(self.default_message) self.defmsg = QLabel('')
self.defmsg.setFont(self._font) self.defmsg.setFont(self._font)
self.addWidget(self.defmsg) self.addWidget(self.defmsg)
self.set_label()
def initialize(self, systray=None): def initialize(self, systray=None):
self.systray = systray self.systray = systray
@ -180,18 +180,42 @@ class StatusBar(QStatusBar): # {{{
def device_connected(self, devname): def device_connected(self, devname):
self.device_string = _('Connected ') + devname self.device_string = _('Connected ') + devname
self.defmsg.setText(self.default_message + ' ..::.. ' + self.set_label()
self.device_string)
def update_state(self, total, current, selected):
self.total, self.current, self.selected = total, current, selected
self.set_label()
def set_label(self):
try:
self._set_label()
except:
import traceback
traceback.print_exc()
def _set_label(self):
msg = '%s %s %s' % (__appname__, _('version'), get_version())
if self.device_string:
msg += ' ..::.. ' + self.device_string
else:
msg += _(' %(created)s %(name)s') % dict(created=_('created by'), name='Kovid Goyal')
if self.total != self.current:
base = _('%(num)d of %(total)d books') % dict(num=self.current, total=self.total)
else:
base = _('%d books') % self.total
if self.selected > 0:
base = _('%(num)s, %(sel)d selected') % dict(num=base, sel=self.selected)
self.defmsg.setText('%s [%s]' % (msg, base))
self.clearMessage() self.clearMessage()
def device_disconnected(self): def device_disconnected(self):
self.device_string = '' self.device_string = ''
self.set_label()
self.defmsg.setText(self.default_message) self.defmsg.setText(self.default_message)
self.clearMessage() self.clearMessage()
def get_version(self):
return get_version()
def show_message(self, msg, timeout=0): def show_message(self, msg, timeout=0):
self.showMessage(msg, timeout) self.showMessage(msg, timeout)
if self.notifier is not None and not config['disable_tray_notification']: if self.notifier is not None and not config['disable_tray_notification']:
@ -312,9 +336,15 @@ class LayoutMixin(object): # {{{
def read_layout_settings(self): def read_layout_settings(self):
# View states are restored automatically when set_database is called # View states are restored automatically when set_database is called
for x in ('cb', 'tb', 'bd'): for x in ('cb', 'tb', 'bd'):
getattr(self, x+'_splitter').restore_state() getattr(self, x+'_splitter').restore_state()
def update_status_bar(self, *args):
v = self.current_view()
selected = len(v.selectionModel().selectedRows())
total, current = v.model().counts()
self.status_bar.update_state(total, current, selected)
# }}} # }}}

View File

@ -6,7 +6,7 @@ __copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
import functools, re, os, traceback, errno, time import functools, re, os, traceback, errno, time
from collections import defaultdict from collections import defaultdict, namedtuple
from PyQt4.Qt import (QAbstractTableModel, Qt, pyqtSignal, QIcon, QImage, from PyQt4.Qt import (QAbstractTableModel, Qt, pyqtSignal, QIcon, QImage,
QModelIndex, QVariant, QDateTime, QColor, QPixmap) QModelIndex, QVariant, QDateTime, QColor, QPixmap)
@ -29,6 +29,8 @@ from calibre.gui2.library import DEFAULT_SORT
from calibre.utils.localization import calibre_langcode_to_name from calibre.utils.localization import calibre_langcode_to_name
from calibre.library.coloring import color_row_key from calibre.library.coloring import color_row_key
Counts = namedtuple('Counts', 'total current')
def human_readable(size, precision=1): def human_readable(size, precision=1):
""" Convert a size in bytes into megabytes """ """ Convert a size in bytes into megabytes """
return ('%.'+str(precision)+'f') % ((size/(1024.*1024.)),) return ('%.'+str(precision)+'f') % ((size/(1024.*1024.)),)
@ -46,7 +48,7 @@ def default_image():
_default_image = QImage(I('default_cover.png')) _default_image = QImage(I('default_cover.png'))
return _default_image return _default_image
class ColumnColor(object): class ColumnColor(object): # {{{
def __init__(self, formatter, colors): def __init__(self, formatter, colors):
self.mi = None self.mi = None
@ -70,9 +72,9 @@ class ColumnColor(object):
return color return color
except: except:
pass pass
# }}}
class ColumnIcon(object): # {{{
class ColumnIcon(object):
def __init__(self, formatter): def __init__(self, formatter):
self.mi = None self.mi = None
@ -108,6 +110,7 @@ class ColumnIcon(object):
return icon_bitmap return icon_bitmap
except: except:
pass pass
# }}}
class BooksModel(QAbstractTableModel): # {{{ class BooksModel(QAbstractTableModel): # {{{
@ -281,6 +284,13 @@ class BooksModel(QAbstractTableModel): # {{{
self._clear_caches() self._clear_caches()
self.count_changed_signal.emit(self.db.count()) self.count_changed_signal.emit(self.db.count())
def counts(self):
if self.db.data.search_restriction_applied():
total = self.db.data.get_search_restriction_book_count()
else:
total = self.db.count()
return Counts(total, self.count())
def row_indices(self, index): def row_indices(self, index):
''' Return list indices of all cells in index.row()''' ''' Return list indices of all cells in index.row()'''
return [self.index(index.row(), c) for c in range(self.columnCount(None))] return [self.index(index.row(), c) for c in range(self.columnCount(None))]
@ -1195,6 +1205,12 @@ class DeviceBooksModel(BooksModel): # {{{
self.editable = ['title', 'authors', 'collections'] self.editable = ['title', 'authors', 'collections']
self.book_in_library = None self.book_in_library = None
def counts(self):
return Counts(len(self.db), len(self.map))
def count_changed(self, *args):
self.count_changed_signal.emit(len(self.db))
def mark_for_deletion(self, job, rows, rows_are_ids=False): def mark_for_deletion(self, job, rows, rows_are_ids=False):
db_indices = rows if rows_are_ids else self.indices(rows) db_indices = rows if rows_are_ids else self.indices(rows)
db_items = [self.db[i] for i in db_indices if -1 < i < len(self.db)] db_items = [self.db[i] for i in db_indices if -1 < i < len(self.db)]
@ -1234,11 +1250,13 @@ class DeviceBooksModel(BooksModel): # {{{
if not succeeded: if not succeeded:
indices = self.row_indices(self.index(row, 0)) indices = self.row_indices(self.index(row, 0))
self.dataChanged.emit(indices[0], indices[-1]) self.dataChanged.emit(indices[0], indices[-1])
self.count_changed()
def paths_deleted(self, paths): def paths_deleted(self, paths):
self.map = list(range(0, len(self.db))) self.map = list(range(0, len(self.db)))
self.resort(False) self.resort(False)
self.research(True) self.research(True)
self.count_changed()
def is_row_marked_for_deletion(self, row): def is_row_marked_for_deletion(self, row):
try: try:
@ -1301,6 +1319,7 @@ class DeviceBooksModel(BooksModel): # {{{
self.last_search = text self.last_search = text
if self.last_search: if self.last_search:
self.searched.emit(True) self.searched.emit(True)
self.count_changed()
def research(self, reset=True): def research(self, reset=True):
self.search(self.last_search, reset) self.search(self.last_search, reset)
@ -1370,6 +1389,7 @@ class DeviceBooksModel(BooksModel): # {{{
self.map = list(range(0, len(db))) self.map = list(range(0, len(db)))
self.research(reset=False) self.research(reset=False)
self.resort() self.resort()
self.count_changed()
def cover(self, row): def cover(self, row):
item = self.db[self.map[row]] item = self.db[self.map[row]]

View File

@ -325,6 +325,11 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
if self.library_view.model().rowCount(None) < 3: if self.library_view.model().rowCount(None) < 3:
self.library_view.resizeColumnsToContents() self.library_view.resizeColumnsToContents()
for view in ('library', 'memory', 'card_a', 'card_b'):
v = getattr(self, '%s_view' % view)
v.selectionModel().selectionChanged.connect(self.update_status_bar)
v.model().count_changed_signal.connect(self.update_status_bar)
self.library_view.model().count_changed() self.library_view.model().count_changed()
self.bars_manager.database_changed(self.library_view.model().db) self.bars_manager.database_changed(self.library_view.model().db)
self.library_view.model().database_changed.connect(self.bars_manager.database_changed, self.library_view.model().database_changed.connect(self.bars_manager.database_changed,
@ -661,6 +666,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
# Reset the view in case something changed while it was invisible # Reset the view in case something changed while it was invisible
self.current_view().reset() self.current_view().reset()
self.set_number_of_books_shown() self.set_number_of_books_shown()
self.update_status_bar()
def job_exception(self, job, dialog_title=_('Conversion Error')): def job_exception(self, job, dialog_title=_('Conversion Error')):
if not hasattr(self, '_modeless_dialogs'): if not hasattr(self, '_modeless_dialogs'):