Speed up deleting of large numbers of books and show progress while doing so

This commit is contained in:
Kovid Goyal 2010-12-07 17:40:54 -07:00
parent 418b036d4f
commit e50c6d98a4
3 changed files with 72 additions and 13 deletions

View File

@ -5,13 +5,65 @@ __license__ = 'GPL v3'
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
from PyQt4.Qt import QMenu
from functools import partial
from PyQt4.Qt import QMenu, QObject, QTimer
from calibre.gui2 import error_dialog
from calibre.gui2.dialogs.delete_matching_from_device import DeleteMatchingFromDeviceDialog
from calibre.gui2.dialogs.confirm_delete import confirm
from calibre.gui2.actions import InterfaceAction
single_shot = partial(QTimer.singleShot, 10)
class MultiDeleter(QObject):
def __init__(self, gui, rows, callback):
from calibre.gui2.dialogs.progress import ProgressDialog
QObject.__init__(self, gui)
self.model = gui.library_view.model()
self.ids = list(map(self.model.id, rows))
self.gui = gui
self.failures = []
self.deleted_ids = []
self.callback = callback
single_shot(self.delete_one)
self.pd = ProgressDialog(_('Deleting...'), parent=gui,
cancelable=False, min=0, max=len(self.ids))
self.pd.setModal(True)
self.pd.show()
def delete_one(self):
if not self.ids:
self.cleanup()
return
id_ = self.ids.pop()
title = 'id%d'%id_
try:
title = self.model.db.title(id_, index_is_id=True)
self.model.db.delete_book(id_, notify=False, commit=False)
self.deleted_ids.append(id_)
except:
import traceback
self.failures.append((id_, title, traceback.format_exc()))
single_shot(self.delete_one)
self.pd.value += 1
self.pd.set_msg(_('Deleted') + ' ' + title)
def cleanup(self):
self.pd.hide()
self.pd = None
self.model.db.commit()
self.model.db.clean()
self.model.books_deleted()
self.gui.tags_view.recount()
self.callback(self.deleted_ids)
if self.failures:
msg = ['==>'+x[1]+'\n'+x[2] for x in self.failures]
error_dialog(self.gui, _('Failed to delete'),
_('Failed to delete some books, click the Show Details button'
' for details.'), det_msg='\n\n'.join(msg), show=True)
class DeleteAction(InterfaceAction):
name = 'Remove Books'
@ -179,8 +231,13 @@ class DeleteAction(InterfaceAction):
row = None
if ci.isValid():
row = ci.row()
ids_deleted = view.model().delete_books(rows)
self.library_ids_deleted(ids_deleted, row)
if len(rows) < 5:
ids_deleted = view.model().delete_books(rows)
self.library_ids_deleted(ids_deleted, row)
else:
self.__md = MultiDeleter(self.gui, rows,
partial(self.library_ids_deleted, current_row=row))
else:
if not confirm('<p>'+_('The selected books will be '
'<b>permanently deleted</b> '

View File

@ -223,21 +223,22 @@ class BooksModel(QAbstractTableModel): # {{{
def by_author(self):
return self.sorted_on[0] == 'authors'
def books_deleted(self):
self.count_changed()
self.clear_caches()
self.reset()
def delete_books(self, indices):
ids = map(self.id, indices)
for id in ids:
self.db.delete_book(id, notify=False)
self.count_changed()
self.clear_caches()
self.reset()
self.books_deleted()
return ids
def delete_books_by_id(self, ids):
for id in ids:
self.db.delete_book(id)
self.count_changed()
self.clear_caches()
self.reset()
self.books_deleted()
def books_added(self, num):
if num > 0:

View File

@ -953,23 +953,24 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
self.notify('metadata', [id])
return True
def delete_book(self, id, notify=True):
def delete_book(self, id, notify=True, commit=True):
'''
Removes book from the result cache and the underlying database.
If you set commit to False, you must call clean() manually afterwards
'''
try:
path = os.path.join(self.library_path, self.path(id, index_is_id=True))
except:
path = None
self.data.remove(id)
if path and os.path.exists(path):
self.rmtree(path)
parent = os.path.dirname(path)
if len(os.listdir(parent)) == 0:
self.rmtree(parent)
self.conn.execute('DELETE FROM books WHERE id=?', (id,))
self.conn.commit()
self.clean()
if commit:
self.conn.commit()
self.clean()
self.data.books_deleted([id])
if notify:
self.notify('delete', [id])