Add progress dialog for saving to disk. Also Fix #1624 (Reading meta-data dialogue box doesn't close in windows XP)

This commit is contained in:
Kovid Goyal 2009-01-18 14:00:47 -08:00
parent 8d6bc29107
commit edd7ef00b1
8 changed files with 196 additions and 55 deletions

View File

@ -10,11 +10,6 @@ Based on ideas from comiclrf created by FangornUK.
import os, sys, shutil, traceback, textwrap import os, sys, shutil, traceback, textwrap
from uuid import uuid4 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): def create_pdf(pages, profile, opts, thumbnail=None):
width, height = PROFILES[profile] width, height = PROFILES[profile]
if not _reportlab: from reportlab.pdfgen import canvas
raise RuntimeError('Failed to load reportlab')
pdf = canvas.Canvas(filename=opts.output, pagesize=(width,height+15)) pdf = canvas.Canvas(filename=opts.output, pagesize=(width,height+15))
pdf.setAuthor(opts.author) pdf.setAuthor(opts.author)
pdf.setTitle(opts.title) pdf.setTitle(opts.title)

View File

@ -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)

View File

@ -0,0 +1,72 @@
<ui version="4.0" >
<class>Dialog</class>
<widget class="QDialog" name="Dialog" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>712</width>
<height>308</height>
</rect>
</property>
<property name="windowTitle" >
<string>Dialog</string>
</property>
<property name="windowIcon" >
<iconset resource="../images.qrc" >
<normaloff>:/images/jobs.svg</normaloff>:/images/jobs.svg</iconset>
</property>
<layout class="QGridLayout" name="gridLayout" >
<item row="0" column="0" >
<widget class="QLabel" name="title" >
<property name="font" >
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text" >
<string>TextLabel</string>
</property>
<property name="alignment" >
<set>Qt::AlignCenter</set>
</property>
<property name="wordWrap" >
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0" >
<widget class="QProgressBar" name="bar" >
<property name="value" >
<number>0</number>
</property>
</widget>
</item>
<item row="2" column="0" >
<widget class="QLabel" name="message" >
<property name="text" >
<string>TextLabel</string>
</property>
<property name="alignment" >
<set>Qt::AlignCenter</set>
</property>
<property name="wordWrap" >
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="0" >
<widget class="QDialogButtonBox" name="button_box" >
<property name="standardButtons" >
<set>QDialogButtonBox::Abort</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources>
<include location="../images.qrc" />
</resources>
<connections/>
</ui>

View File

@ -198,13 +198,18 @@ class BooksModel(QAbstractTableModel):
''' 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))]
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] rows = [row.row() for row in rows]
if single_format is None: if single_format is None:
return self.db.export_to_dir(path, rows, self.sorted_on[0] == 'authors', return self.db.export_to_dir(path, rows,
single_dir=single_dir) self.sorted_on[0] == 'authors',
single_dir=single_dir,
callback=callback)
else: 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): def delete_books(self, indices):

View File

@ -28,6 +28,7 @@ from calibre.gui2.cover_flow import CoverFlow, DatabaseImages, pictureflowerror
from calibre.library.database import LibraryDatabase from calibre.library.database import LibraryDatabase
from calibre.gui2.dialogs.scheduler import Scheduler from calibre.gui2.dialogs.scheduler import Scheduler
from calibre.gui2.update import CheckForUpdates 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_window import MainWindow, option_parser as _option_parser
from calibre.gui2.main_ui import Ui_MainWindow from calibre.gui2.main_ui import Ui_MainWindow
from calibre.gui2.device import DeviceManager 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') root = choose_dir(self, 'recursive book import root dir dialog', 'Select root folder')
if not root: if not root:
return return
progress = QProgressDialog('', '&'+_('Stop'), progress = ProgressDialog(_('Adding books recursively...'),
0, 0, self) min=0, max=0, parent=self)
progress.setWindowModality(Qt.ApplicationModal)
progress.setWindowTitle(_('Adding books recursively...'))
progress.show() progress.show()
def callback(msg): def callback(msg):
if msg != '.': if msg != '.':
progress.setLabelText((_('Added ')+msg) if msg else _('Searching...')) progress.set_msg((_('Added ')+msg) if msg else _('Searching...'))
stop = progress.wasCanceled()
QApplication.processEvents() QApplication.processEvents()
QApplication.sendPostedEvents() QApplication.sendPostedEvents()
QApplication.flush() QApplication.flush()
return stop return progress.canceled
try: try:
duplicates = self.library_view.model().db.recursive_import(root, single, callback=callback) duplicates = self.library_view.model().db.recursive_import(root, single, callback=callback)
finally: finally:
progress.hide() progress.hide()
progress.close()
if duplicates: if duplicates:
files = _('<p>Books with the same title as the following already exist in the database. Add them anyway?<ul>') files = _('<p>Books with the same title as the following already exist in the database. Add them anyway?<ul>')
for mi, formats in duplicates: for mi, formats in duplicates:
files += '<li>'+mi.title+'</li>\n' files += '<li>'+mi.title+'</li>\n'
d = WarningDialog(_('Duplicates found!'), _('Duplicates found!'), files+'</ul></p>', self) d = WarningDialog(_('Duplicates found!'), _('Duplicates found!'),
files+'</ul></p>', self)
if d.exec_() == QDialog.Accepted: if d.exec_() == QDialog.Accepted:
for mi, formats in duplicates: for mi, formats in duplicates:
self.library_view.model().db.import_book(mi, formats ) self.library_view.model().db.import_book(mi, formats )
@ -686,15 +684,13 @@ class Main(MainWindow, Ui_MainWindow):
return return
# Get format and metadata information # Get format and metadata information
formats, metadata, names, infos = [], [], [], [] formats, metadata, names, infos = [], [], [], []
progress = QProgressDialog(_('Reading metadata...'), _('Stop'), 0, len(paths), self) progress = ProgressDialog(_('Adding books...'), _('Reading metadata...'),
progress.setWindowTitle(_('Adding books...')) min=0, max=len(paths), parent=self)
progress.setWindowModality(Qt.ApplicationModal)
progress.setLabelText(_('Reading metadata...'))
progress.show() progress.show()
try: try:
for c, book in enumerate(paths): for c, book in enumerate(paths):
progress.setValue(c) progress.set_value(c)
if progress.wasCanceled(): if progress.canceled:
return return
format = os.path.splitext(book)[1] format = os.path.splitext(book)[1]
format = format[1:] if format else None 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), infos.append({'title':mi.title, 'authors':', '.join(mi.authors),
'cover':self.default_thumbnail, 'tags':[]}) 'cover':self.default_thumbnail, 'tags':[]})
title = mi.title if isinstance(mi.title, unicode) else mi.title.decode(preferred_encoding, 'replace') 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: if not to_device:
progress.setLabelText(_('Adding books to database...')) progress.set_msg(_('Adding books to database...'))
model = self.library_view.model() model = self.library_view.model()
paths = list(paths) paths = list(paths)
duplicates, number_added = model.add_books(paths, formats, metadata) duplicates, number_added = model.add_books(paths, formats, metadata)
progress.cancel()
if duplicates: if duplicates:
files = _('<p>Books with the same title as the following already exist in the database. Add them anyway?<ul>') files = _('<p>Books with the same title as the following already exist in the database. Add them anyway?<ul>')
for mi in duplicates[2]: for mi in duplicates[2]:
@ -734,9 +729,7 @@ class Main(MainWindow, Ui_MainWindow):
else: else:
self.upload_books(paths, list(map(sanitize_file_name, names)), infos, on_card=on_card) self.upload_books(paths, list(map(sanitize_file_name, names)), infos, on_card=on_card)
finally: finally:
progress.setValue(progress.maximum())
progress.hide() progress.hide()
progress.close()
def upload_books(self, files, names, metadata, on_card=False, memory=None): 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) self.save_to_disk(checked, True)
def save_to_disk(self, checked, single_dir=False, single_format=None): def save_to_disk(self, checked, single_dir=False, single_format=None):
rows = self.current_view().selectionModel().selectedRows() rows = self.current_view().selectionModel().selectedRows()
if not rows or len(rows) == 0: if not rows or len(rows) == 0:
d = error_dialog(self, _('Cannot save to disk'), _('No books selected')) d = error_dialog(self, _('Cannot save to disk'), _('No books selected'))
d.exec_() d.exec_()
return 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')) dir = choose_dir(self, 'save to disk dialog', _('Choose destination directory'))
if not dir: if not dir:
return return
if self.current_view() == self.library_view:
failures = self.current_view().model().save_to_disk(rows, dir, progress.show()
single_dir=single_dir, single_format=single_format) QApplication.processEvents()
if failures and single_format is not None: QApplication.sendPostedEvents()
msg = _('<p>Could not save the following books to disk, because the %s format is not available for them:<ul>')%single_format.upper() QApplication.flush()
for f in failures: try:
msg += '<li>%s</li>'%f[1] if self.current_view() == self.library_view:
msg += '</ul>' failures = self.current_view().model().save_to_disk(rows, dir,
warning_dialog(self, _('Could not save some ebooks'), msg).exec_() single_dir=single_dir, callback=callback,
QDesktopServices.openUrl(QUrl('file:'+dir)) single_format=single_format)
else: if failures and single_format is not None:
paths = self.current_view().model().paths(rows) msg = _('<p>Could not save the following books to disk, because the %s format is not available for them:<ul>')%single_format.upper()
self.device_manager.save_books(Dispatcher(self.books_saved), paths, dir) for f in failures:
msg += '<li>%s</li>'%f[1]
msg += '</ul>'
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): def books_saved(self, job):
if job.exception is not None: if job.exception is not None:

View File

@ -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')] 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, 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): if not os.path.exists(dir):
raise IOError('Target directory does not exist: '+dir) raise IOError('Target directory does not exist: '+dir)
by_author = {} by_author = {}
count = 0
for index in indices: for index in indices:
id = index if index_is_id else self.id(index) id = index if index_is_id else self.id(index)
au = self.conn.get('SELECT author_sort FROM books WHERE id=?', 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: if not au:
au = _('Unknown') au = _('Unknown')
au = au.split(',')[0] au = au.split(',')[0]
else:
au = au.replace(',', ';')
if not by_author.has_key(au): if not by_author.has_key(au):
by_author[au] = [] by_author[au] = []
by_author[au].append(index) 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 print 'Error setting metadata for book:', mi.title
traceback.print_exc() traceback.print_exc()
f.close() f.close()
count += 1
if callable(callback):
if not callback(count, mi.title):
return
def import_book(self, mi, formats): def import_book(self, mi, formats):
@ -1569,12 +1573,13 @@ ALTER TABLE books ADD COLUMN isbn TEXT DEFAULT "" COLLATE NOCASE;
return duplicates 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) dir = os.path.abspath(dir)
if not index_is_id: if not index_is_id:
indices = map(self.id, indices) indices = map(self.id, indices)
failures = [] failures = []
for id in indices: for count, id in enumerate(indices):
try: try:
data = self.format(id, format, index_is_id=True) data = self.format(id, format, index_is_id=True)
if not data: if not data:
@ -1599,6 +1604,9 @@ ALTER TABLE books ADD COLUMN isbn TEXT DEFAULT "" COLLATE NOCASE;
except: except:
pass pass
f.close() f.close()
if callable(callback):
if not callback(count, title):
break
return failures return failures

View File

@ -38,7 +38,6 @@ class cmd_commit(_cmd_commit):
print attributes['summary'] print attributes['summary']
return attributes['summary'] return attributes['summary']
def expand_bug(self, msg, nick, config, bug_tracker, type='trac'): def expand_bug(self, msg, nick, config, bug_tracker, type='trac'):
prefix = '%s_%s_'%(type, nick) prefix = '%s_%s_'%(type, nick)
username = config.get_user_option(prefix+'username') username = config.get_user_option(prefix+'username')

View File

@ -7,6 +7,7 @@ clarin.com
''' '''
from calibre import strftime from calibre import strftime
from calibre.web.feeds.news import BasicNewsRecipe
class Clarin(BasicNewsRecipe): class Clarin(BasicNewsRecipe):
title = 'Clarin' title = 'Clarin'
@ -47,4 +48,4 @@ class Clarin(BasicNewsRecipe):
rest = artl.partition('-0')[-1] rest = artl.partition('-0')[-1]
lmain = rest.partition('.')[0] lmain = rest.partition('.')[0]
return 'http://www.servicios.clarin.com/notas/jsp/clarin/v9/notas/imprimir.jsp?pagid=' + lmain return 'http://www.servicios.clarin.com/notas/jsp/clarin/v9/notas/imprimir.jsp?pagid=' + lmain