From edd7ef00b183647ff5f766fd35c8aaedfce8adc2 Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Sun, 18 Jan 2009 14:00:47 -0800
Subject: [PATCH] Add progress dialog for saving to disk. Also Fix #1624
(Reading meta-data dialogue box doesn't close in windows XP)
---
src/calibre/ebooks/lrf/comic/convert_from.py | 12 +--
src/calibre/gui2/dialogs/progress.py | 48 +++++++++++
src/calibre/gui2/dialogs/progress.ui | 72 ++++++++++++++++
src/calibre/gui2/library.py | 13 ++-
src/calibre/gui2/main.py | 84 +++++++++++--------
src/calibre/library/database.py | 18 ++--
src/calibre/trac/bzr_commit_plugin.py | 1 -
.../web/feeds/recipes/recipe_clarin.py | 3 +-
8 files changed, 196 insertions(+), 55 deletions(-)
create mode 100644 src/calibre/gui2/dialogs/progress.py
create mode 100644 src/calibre/gui2/dialogs/progress.ui
diff --git a/src/calibre/ebooks/lrf/comic/convert_from.py b/src/calibre/ebooks/lrf/comic/convert_from.py
index 0569bf3733..c22f1745ae 100755
--- a/src/calibre/ebooks/lrf/comic/convert_from.py
+++ b/src/calibre/ebooks/lrf/comic/convert_from.py
@@ -10,11 +10,6 @@ Based on ideas from comiclrf created by FangornUK.
import os, sys, shutil, traceback, textwrap
from uuid import uuid4
-try:
- from reportlab.pdfgen import canvas
- _reportlab = True
-except:
- _reportlab = False
@@ -396,10 +391,9 @@ def create_lrf(pages, profile, opts, thumbnail=None):
def create_pdf(pages, profile, opts, thumbnail=None):
width, height = PROFILES[profile]
-
- if not _reportlab:
- raise RuntimeError('Failed to load reportlab')
-
+
+ from reportlab.pdfgen import canvas
+
pdf = canvas.Canvas(filename=opts.output, pagesize=(width,height+15))
pdf.setAuthor(opts.author)
pdf.setTitle(opts.title)
diff --git a/src/calibre/gui2/dialogs/progress.py b/src/calibre/gui2/dialogs/progress.py
new file mode 100644
index 0000000000..2543cefb4d
--- /dev/null
+++ b/src/calibre/gui2/dialogs/progress.py
@@ -0,0 +1,48 @@
+#!/usr/bin/env python
+__license__ = 'GPL v3'
+__copyright__ = '2009, Kovid Goyal kovid@kovidgoyal.net'
+__docformat__ = 'restructuredtext en'
+
+''''''
+
+from PyQt4.Qt import QDialog, SIGNAL, Qt
+
+from calibre.gui2.dialogs.progress_ui import Ui_Dialog
+
+class ProgressDialog(QDialog, Ui_Dialog):
+
+ def __init__(self, title, msg='', min=0, max=99, parent=None):
+ QDialog.__init__(self, parent)
+ self.setupUi(self)
+ self.setWindowTitle(title)
+ self.title.setText(title)
+ self.message.setText(msg)
+ self.setWindowModality(Qt.ApplicationModal)
+ self.set_min(min)
+ self.set_max(max)
+ self.canceled = False
+
+ self.connect(self.button_box, SIGNAL('rejected()'), self._canceled)
+
+ def set_msg(self, msg=''):
+ self.message.setText(msg)
+
+ def set_value(self, val):
+ self.bar.setValue(val)
+
+ def set_min(self, min):
+ self.bar.setMinimum(min)
+
+ def set_max(self, max):
+ self.bar.setMaximum(max)
+
+ def _canceled(self, *args):
+ self.canceled = True
+ self.button_box.setDisabled(True)
+ self.title.setText(_('Aborting...'))
+
+ def keyPressEvent(self, ev):
+ if ev.key() == Qt.Key_Escape:
+ self._canceled()
+ else:
+ QDialog.keyPressEvent(self, ev)
\ No newline at end of file
diff --git a/src/calibre/gui2/dialogs/progress.ui b/src/calibre/gui2/dialogs/progress.ui
new file mode 100644
index 0000000000..60488be62d
--- /dev/null
+++ b/src/calibre/gui2/dialogs/progress.ui
@@ -0,0 +1,72 @@
+
+ Dialog
+
+
+
+ 0
+ 0
+ 712
+ 308
+
+
+
+ Dialog
+
+
+
+ :/images/jobs.svg:/images/jobs.svg
+
+
+ -
+
+
+
+ 75
+ true
+
+
+
+ TextLabel
+
+
+ Qt::AlignCenter
+
+
+ true
+
+
+
+ -
+
+
+ 0
+
+
+
+ -
+
+
+ TextLabel
+
+
+ Qt::AlignCenter
+
+
+ true
+
+
+
+ -
+
+
+ QDialogButtonBox::Abort
+
+
+
+
+
+
+
+
+
+
diff --git a/src/calibre/gui2/library.py b/src/calibre/gui2/library.py
index 792d011883..8a737fd608 100644
--- a/src/calibre/gui2/library.py
+++ b/src/calibre/gui2/library.py
@@ -198,13 +198,18 @@ class BooksModel(QAbstractTableModel):
''' Return list indices of all cells in index.row()'''
return [ self.index(index.row(), c) for c in range(self.columnCount(None))]
- def save_to_disk(self, rows, path, single_dir=False, single_format=None):
+ def save_to_disk(self, rows, path, single_dir=False, single_format=None,
+ callback=None):
rows = [row.row() for row in rows]
if single_format is None:
- return self.db.export_to_dir(path, rows, self.sorted_on[0] == 'authors',
- single_dir=single_dir)
+ return self.db.export_to_dir(path, rows,
+ self.sorted_on[0] == 'authors',
+ single_dir=single_dir,
+ callback=callback)
else:
- return self.db.export_single_format_to_dir(path, rows, single_format)
+ return self.db.export_single_format_to_dir(path, rows,
+ single_format,
+ callback=callback)
def delete_books(self, indices):
diff --git a/src/calibre/gui2/main.py b/src/calibre/gui2/main.py
index 6ff5df412d..b04ad18c97 100644
--- a/src/calibre/gui2/main.py
+++ b/src/calibre/gui2/main.py
@@ -28,6 +28,7 @@ from calibre.gui2.cover_flow import CoverFlow, DatabaseImages, pictureflowerror
from calibre.library.database import LibraryDatabase
from calibre.gui2.dialogs.scheduler import Scheduler
from calibre.gui2.update import CheckForUpdates
+from calibre.gui2.dialogs.progress import ProgressDialog
from calibre.gui2.main_window import MainWindow, option_parser as _option_parser
from calibre.gui2.main_ui import Ui_MainWindow
from calibre.gui2.device import DeviceManager
@@ -598,29 +599,26 @@ class Main(MainWindow, Ui_MainWindow):
root = choose_dir(self, 'recursive book import root dir dialog', 'Select root folder')
if not root:
return
- progress = QProgressDialog('', '&'+_('Stop'),
- 0, 0, self)
- progress.setWindowModality(Qt.ApplicationModal)
- progress.setWindowTitle(_('Adding books recursively...'))
+ progress = ProgressDialog(_('Adding books recursively...'),
+ min=0, max=0, parent=self)
progress.show()
def callback(msg):
if msg != '.':
- progress.setLabelText((_('Added ')+msg) if msg else _('Searching...'))
- stop = progress.wasCanceled()
+ progress.set_msg((_('Added ')+msg) if msg else _('Searching...'))
QApplication.processEvents()
QApplication.sendPostedEvents()
QApplication.flush()
- return stop
+ return progress.canceled
try:
duplicates = self.library_view.model().db.recursive_import(root, single, callback=callback)
finally:
- progress.hide()
- progress.close()
+ progress.hide()
if duplicates:
files = _('Books with the same title as the following already exist in the database. Add them anyway?
')
for mi, formats in duplicates:
files += '- '+mi.title+'
\n'
- d = WarningDialog(_('Duplicates found!'), _('Duplicates found!'), files+'
', self)
+ d = WarningDialog(_('Duplicates found!'), _('Duplicates found!'),
+ files+'', self)
if d.exec_() == QDialog.Accepted:
for mi, formats in duplicates:
self.library_view.model().db.import_book(mi, formats )
@@ -686,15 +684,13 @@ class Main(MainWindow, Ui_MainWindow):
return
# Get format and metadata information
formats, metadata, names, infos = [], [], [], []
- progress = QProgressDialog(_('Reading metadata...'), _('Stop'), 0, len(paths), self)
- progress.setWindowTitle(_('Adding books...'))
- progress.setWindowModality(Qt.ApplicationModal)
- progress.setLabelText(_('Reading metadata...'))
+ progress = ProgressDialog(_('Adding books...'), _('Reading metadata...'),
+ min=0, max=len(paths), parent=self)
progress.show()
try:
for c, book in enumerate(paths):
- progress.setValue(c)
- if progress.wasCanceled():
+ progress.set_value(c)
+ if progress.canceled:
return
format = os.path.splitext(book)[1]
format = format[1:] if format else None
@@ -713,15 +709,14 @@ class Main(MainWindow, Ui_MainWindow):
infos.append({'title':mi.title, 'authors':', '.join(mi.authors),
'cover':self.default_thumbnail, 'tags':[]})
title = mi.title if isinstance(mi.title, unicode) else mi.title.decode(preferred_encoding, 'replace')
- progress.setLabelText(_('Read metadata from ')+title)
+ progress.set_msg(_('Read metadata from ')+title)
if not to_device:
- progress.setLabelText(_('Adding books to database...'))
+ progress.set_msg(_('Adding books to database...'))
model = self.library_view.model()
paths = list(paths)
duplicates, number_added = model.add_books(paths, formats, metadata)
- progress.cancel()
if duplicates:
files = _('Books with the same title as the following already exist in the database. Add them anyway?
')
for mi in duplicates[2]:
@@ -734,9 +729,7 @@ class Main(MainWindow, Ui_MainWindow):
else:
self.upload_books(paths, list(map(sanitize_file_name, names)), infos, on_card=on_card)
finally:
- progress.setValue(progress.maximum())
progress.hide()
- progress.close()
def upload_books(self, files, names, metadata, on_card=False, memory=None):
'''
@@ -979,28 +972,49 @@ class Main(MainWindow, Ui_MainWindow):
self.save_to_disk(checked, True)
def save_to_disk(self, checked, single_dir=False, single_format=None):
+
rows = self.current_view().selectionModel().selectedRows()
if not rows or len(rows) == 0:
d = error_dialog(self, _('Cannot save to disk'), _('No books selected'))
d.exec_()
return
-
+
+ progress = ProgressDialog(_('Saving to disk...'), min=0, max=len(rows),
+ parent=self)
+
+ def callback(count, msg):
+ progress.set_value(count)
+ progress.set_msg(_('Saved')+' '+msg)
+ QApplication.processEvents()
+ QApplication.sendPostedEvents()
+ QApplication.flush()
+ return not progress.canceled
+
dir = choose_dir(self, 'save to disk dialog', _('Choose destination directory'))
if not dir:
return
- if self.current_view() == self.library_view:
- failures = self.current_view().model().save_to_disk(rows, dir,
- single_dir=single_dir, single_format=single_format)
- if failures and single_format is not None:
- msg = _('Could not save the following books to disk, because the %s format is not available for them:
')%single_format.upper()
- for f in failures:
- msg += '- %s
'%f[1]
- msg += '
'
- warning_dialog(self, _('Could not save some ebooks'), msg).exec_()
- QDesktopServices.openUrl(QUrl('file:'+dir))
- else:
- paths = self.current_view().model().paths(rows)
- self.device_manager.save_books(Dispatcher(self.books_saved), paths, dir)
+
+ progress.show()
+ QApplication.processEvents()
+ QApplication.sendPostedEvents()
+ QApplication.flush()
+ try:
+ if self.current_view() == self.library_view:
+ failures = self.current_view().model().save_to_disk(rows, dir,
+ single_dir=single_dir, callback=callback,
+ single_format=single_format)
+ if failures and single_format is not None:
+ msg = _('Could not save the following books to disk, because the %s format is not available for them:
')%single_format.upper()
+ for f in failures:
+ msg += '- %s
'%f[1]
+ msg += '
'
+ warning_dialog(self, _('Could not save some ebooks'), msg).exec_()
+ QDesktopServices.openUrl(QUrl('file:'+dir))
+ else:
+ paths = self.current_view().model().paths(rows)
+ self.device_manager.save_books(Dispatcher(self.books_saved), paths, dir)
+ finally:
+ progress.hide()
def books_saved(self, job):
if job.exception is not None:
diff --git a/src/calibre/library/database.py b/src/calibre/library/database.py
index ecf272618f..2979c95d26 100644
--- a/src/calibre/library/database.py
+++ b/src/calibre/library/database.py
@@ -1390,10 +1390,11 @@ ALTER TABLE books ADD COLUMN isbn TEXT DEFAULT "" COLLATE NOCASE;
return [i[0] for i in self.conn.get('SELECT id FROM books')]
def export_to_dir(self, dir, indices, byauthor=False, single_dir=False,
- index_is_id=False):
+ index_is_id=False, callback=None):
if not os.path.exists(dir):
raise IOError('Target directory does not exist: '+dir)
by_author = {}
+ count = 0
for index in indices:
id = index if index_is_id else self.id(index)
au = self.conn.get('SELECT author_sort FROM books WHERE id=?',
@@ -1403,8 +1404,6 @@ ALTER TABLE books ADD COLUMN isbn TEXT DEFAULT "" COLLATE NOCASE;
if not au:
au = _('Unknown')
au = au.split(',')[0]
- else:
- au = au.replace(',', ';')
if not by_author.has_key(au):
by_author[au] = []
by_author[au].append(index)
@@ -1456,6 +1455,11 @@ ALTER TABLE books ADD COLUMN isbn TEXT DEFAULT "" COLLATE NOCASE;
print 'Error setting metadata for book:', mi.title
traceback.print_exc()
f.close()
+ count += 1
+ if callable(callback):
+ if not callback(count, mi.title):
+ return
+
def import_book(self, mi, formats):
@@ -1569,12 +1573,13 @@ ALTER TABLE books ADD COLUMN isbn TEXT DEFAULT "" COLLATE NOCASE;
return duplicates
- def export_single_format_to_dir(self, dir, indices, format, index_is_id=False):
+ def export_single_format_to_dir(self, dir, indices, format,
+ index_is_id=False, callback=None):
dir = os.path.abspath(dir)
if not index_is_id:
indices = map(self.id, indices)
failures = []
- for id in indices:
+ for count, id in enumerate(indices):
try:
data = self.format(id, format, index_is_id=True)
if not data:
@@ -1599,6 +1604,9 @@ ALTER TABLE books ADD COLUMN isbn TEXT DEFAULT "" COLLATE NOCASE;
except:
pass
f.close()
+ if callable(callback):
+ if not callback(count, title):
+ break
return failures
diff --git a/src/calibre/trac/bzr_commit_plugin.py b/src/calibre/trac/bzr_commit_plugin.py
index 8cd8b53d09..5ec6e8f7e8 100644
--- a/src/calibre/trac/bzr_commit_plugin.py
+++ b/src/calibre/trac/bzr_commit_plugin.py
@@ -38,7 +38,6 @@ class cmd_commit(_cmd_commit):
print attributes['summary']
return attributes['summary']
-
def expand_bug(self, msg, nick, config, bug_tracker, type='trac'):
prefix = '%s_%s_'%(type, nick)
username = config.get_user_option(prefix+'username')
diff --git a/src/calibre/web/feeds/recipes/recipe_clarin.py b/src/calibre/web/feeds/recipes/recipe_clarin.py
index 1840be291f..f4b810f087 100644
--- a/src/calibre/web/feeds/recipes/recipe_clarin.py
+++ b/src/calibre/web/feeds/recipes/recipe_clarin.py
@@ -7,6 +7,7 @@ clarin.com
'''
from calibre import strftime
+from calibre.web.feeds.news import BasicNewsRecipe
class Clarin(BasicNewsRecipe):
title = 'Clarin'
@@ -47,4 +48,4 @@ class Clarin(BasicNewsRecipe):
rest = artl.partition('-0')[-1]
lmain = rest.partition('.')[0]
return 'http://www.servicios.clarin.com/notas/jsp/clarin/v9/notas/imprimir.jsp?pagid=' + lmain
-
\ No newline at end of file
+