Start bulk convert. Replace error and warning dialogs with Qts.

This commit is contained in:
John Schember 2009-05-07 19:12:39 -04:00
parent f92cc8ac2d
commit 4b5392bf01
6 changed files with 87 additions and 267 deletions

View File

@ -7,7 +7,7 @@ from PyQt4.Qt import QThread, SIGNAL, QMutex, QWaitCondition, Qt
from calibre.gui2.dialogs.progress import ProgressDialog from calibre.gui2.dialogs.progress import ProgressDialog
from calibre.constants import preferred_encoding from calibre.constants import preferred_encoding
from calibre.gui2.widgets import WarningDialog from calibre.gui2 import warning_dialog
class Add(QThread): class Add(QThread):
@ -113,13 +113,13 @@ class AddFiles(Add):
def process_duplicates(self): def process_duplicates(self):
if self.duplicates: if self.duplicates:
files = _('<p>Books with the same title as the following already ' files = ''
'exist in the database. Add them anyway?<ul>')
for mi in self.duplicates[2]: for mi in self.duplicates[2]:
files += '<li>'+mi.title+'</li>\n' files += mi.title+'\n'
d = WarningDialog (_('Duplicates found!'), d = warning_dialog(_('Duplicates found!'),
_('Duplicates found!'), _('Books with the same title as the following already '
files+'</ul></p>', parent=self._parent) 'exist in the database. Add them anyway?'),
files, parent=self._parent)
if d.exec_() == d.Accepted: if d.exec_() == d.Accepted:
num = self.db.add_books(*self.duplicates, num = self.db.add_books(*self.duplicates,
**dict(add_duplicates=True))[1] **dict(add_duplicates=True))[1]
@ -221,16 +221,16 @@ class AddRecursive(Add):
def process_duplicates(self): def process_duplicates(self):
if self.duplicates: if self.duplicates:
files = _('<p>Books with the same title as the following already ' files = ''
'exist in the database. Add them anyway?<ul>')
for mi in self.duplicates: for mi in self.duplicates:
title = mi[0].title title = mi[0].title
if not isinstance(title, unicode): if not isinstance(title, unicode):
title = title.decode(preferred_encoding, 'replace') title = title.decode(preferred_encoding, 'replace')
files += '<li>'+title+'</li>\n' files += title+'\n'
d = WarningDialog (_('Duplicates found!'), d = warning_dialog(_('Duplicates found!'),
_('Duplicates found!'), _('Books with the same title as the following already '
files+'</ul></p>', parent=self._parent) 'exist in the database. Add them anyway?'),
files, parent=self._parent)
if d.exec_() == d.Accepted: if d.exec_() == d.Accepted:
for mi, formats in self.duplicates: for mi, formats in self.duplicates:
self.db.import_book(mi, formats, notify=False) self.db.import_book(mi, formats, notify=False)

View File

@ -21,18 +21,12 @@ from calibre.gui2 import config, error_dialog, Dispatcher, dynamic, \
pixmap_to_data, warning_dialog, \ pixmap_to_data, warning_dialog, \
info_dialog info_dialog
from calibre.ebooks.metadata import authors_to_string from calibre.ebooks.metadata import authors_to_string
from calibre.gui2.dialogs.conversion_error import ConversionErrorDialog
from calibre import sanitize_file_name, preferred_encoding from calibre import sanitize_file_name, preferred_encoding
from calibre.utils.filenames import ascii_filename from calibre.utils.filenames import ascii_filename
from calibre.devices.errors import FreeSpaceError from calibre.devices.errors import FreeSpaceError
from calibre.utils.smtp import compose_mail, sendmail, extract_email_address, \ from calibre.utils.smtp import compose_mail, sendmail, extract_email_address, \
config as email_config config as email_config
def warning(title, msg, details, parent):
from calibre.gui2.widgets import WarningDialog
WarningDialog(title, msg, details, parent).exec_()
class DeviceJob(Job): class DeviceJob(Job):
def __init__(self, func, *args, **kwargs): def __init__(self, func, *args, **kwargs):
@ -530,13 +524,13 @@ class DeviceGUI(object):
good.append(title) good.append(title)
if errors: if errors:
errors = '\n'.join([ errors = '\n'.join([
'<li><b>%s</b><br>%s<br>%s<br></li>' % '%s\n\n%s\n%s\n' %
(title, e, tb.replace('\n', '<br>')) for \ (title, e, tb) for \
title, e, tb in errors title, e, tb in errors
]) ])
ConversionErrorDialog(self, _('Failed to email books'), error_dialog(self, _('Failed to email books'),
'<p>'+_('Failed to email the following books:')+\ _('Failed to email the following books:'),
'<ul>%s</ul>'%errors, '%s'%errors,
show=True) show=True)
else: else:
self.status_bar.showMessage(_('Sent by email:') + ', '.join(good), self.status_bar.showMessage(_('Sent by email:') + ', '.join(good),

View File

@ -1,113 +0,0 @@
<ui version="4.0" >
<class>Dialog</class>
<widget class="QDialog" name="Dialog" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>727</width>
<height>432</height>
</rect>
</property>
<property name="windowTitle" >
<string>Dialog</string>
</property>
<property name="windowIcon" >
<iconset resource="../images.qrc" >
<normaloff>:/images/dialog_warning.svg</normaloff>:/images/dialog_warning.svg</iconset>
</property>
<layout class="QGridLayout" name="gridLayout" >
<item row="0" column="0" >
<widget class="QLabel" name="msg" >
<property name="text" >
<string>TextLabel</string>
</property>
<property name="wordWrap" >
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0" >
<layout class="QHBoxLayout" name="horizontalLayout" >
<item>
<layout class="QVBoxLayout" name="verticalLayout" >
<item>
<widget class="QLabel" name="label" >
<property name="text" >
<string/>
</property>
<property name="pixmap" >
<pixmap resource="../images.qrc" >:/images/dialog_warning.svg</pixmap>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer" >
<property name="orientation" >
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0" >
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QTextEdit" name="details" />
</item>
</layout>
</item>
<item row="2" column="0" >
<widget class="QDialogButtonBox" name="buttonBox" >
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons" >
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources>
<include location="../images.qrc" />
</resources>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>Dialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel" >
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel" >
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>Dialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel" >
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel" >
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -24,7 +24,7 @@ from calibre.gui2 import APP_UID, warning_dialog, choose_files, error_dialog, \
max_available_height, config, info_dialog, \ max_available_height, config, info_dialog, \
available_width, GetMetadata available_width, GetMetadata
from calibre.gui2.cover_flow import CoverFlow, DatabaseImages, pictureflowerror from calibre.gui2.cover_flow import CoverFlow, DatabaseImages, pictureflowerror
from calibre.gui2.widgets import ProgressIndicator, WarningDialog from calibre.gui2.widgets import ProgressIndicator
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.dialogs.progress import ProgressDialog
@ -36,8 +36,8 @@ from calibre.gui2.jobs2 import JobManager
from calibre.gui2.dialogs.metadata_single import MetadataSingleDialog from calibre.gui2.dialogs.metadata_single import MetadataSingleDialog
from calibre.gui2.dialogs.metadata_bulk import MetadataBulkDialog from calibre.gui2.dialogs.metadata_bulk import MetadataBulkDialog
from calibre.gui2.dialogs.jobs import JobsDialog from calibre.gui2.dialogs.jobs import JobsDialog
from calibre.gui2.dialogs.conversion_error import ConversionErrorDialog from calibre.gui2.tools import convert_single_ebook, convert_bulk_ebook, \
from calibre.gui2.tools import convert_single_ebook, fetch_scheduled_recipe fetch_scheduled_recipe
from calibre.gui2.dialogs.config import ConfigDialog from calibre.gui2.dialogs.config import ConfigDialog
from calibre.gui2.dialogs.search import SearchDialog from calibre.gui2.dialogs.search import SearchDialog
from calibre.gui2.dialogs.choose_format import ChooseFormatDialog from calibre.gui2.dialogs.choose_format import ChooseFormatDialog
@ -89,7 +89,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
self.persistent_files = [] self.persistent_files = []
self.metadata_dialogs = [] self.metadata_dialogs = []
self.default_thumbnail = None self.default_thumbnail = None
self.device_error_dialog = ConversionErrorDialog(self, self.device_error_dialog = error_dialog(self, _('Error'),
_('Error communicating with device'), ' ') _('Error communicating with device'), ' ')
self.device_error_dialog.setModal(Qt.NonModal) self.device_error_dialog.setModal(Qt.NonModal)
self.tb_wrapper = textwrap.TextWrapper(width=40) self.tb_wrapper = textwrap.TextWrapper(width=40)
@ -865,16 +865,16 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
db = self.library_view.model().refresh_ids( db = self.library_view.model().refresh_ids(
x.updated, cr) x.updated, cr)
if x.failures: if x.failures:
details = ['<li><b>%s:</b> %s</li>'%(title, reason) for title, details = ['%s: %s'%(title, reason) for title,
reason in x.failures.values()] reason in x.failures.values()]
details = '<p><ul>%s</ul></p>'%(''.join(details)) details = '%s\n'%('\n'.join(details))
WarningDialog(_('Failed to download some metadata'), warning_dialog(_('Failed to download some metadata'),
_('Failed to download metadata for the following:'), _('Failed to download metadata for the following:'),
details, self).exec_() details, self).exec_()
else: else:
err = _('<b>Failed to download metadata:')+\ err = _('<b>Failed to download metadata:')+\
'</b><br><pre>'+x.tb+'</pre>' '</b><br><pre>'+x.tb+'</pre>'
ConversionErrorDialog(self, _('Error'), err, error_dialog(self, _('Error'), err,
show=True) show=True)
@ -1051,24 +1051,23 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
return [self.library_view.model().db.id(r) for r in rows] return [self.library_view.model().db.id(r) for r in rows]
def convert_bulk(self, checked): def convert_bulk(self, checked):
r = self.get_books_for_conversion() row_ids = self.get_books_for_conversion()
if r is None: if row_ids is None: return
return previous = self.library_view.currentIndex()
comics, others = r rows = [x.row() for x in \
self.library_view.selectionModel().selectedRows()]
res = convert_bulk_ebooks(self, jobs, changed, bad = convert_bulk_ebook(self,
self.library_view.model().db, comics, others) self.library_view.model().db, row_ids)
if res is None:
return
jobs, changed = res
for func, args, desc, fmt, id, temp_files in jobs: for func, args, desc, fmt, id, temp_files in jobs:
job = self.job_manager.run_job(Dispatcher(self.book_converted), if id not in bad:
job = self.job_manager.run_job(Dispatcher(self.book_converted),
func, args=args, description=desc) func, args=args, description=desc)
self.conversion_jobs[job] = (temp_files, fmt, id) self.conversion_jobs[job] = (temp_files, fmt, id)
if changed: if changed:
self.library_view.model().resort(reset=False) self.library_view.model().refresh_rows(rows)
self.library_view.model().research() current = self.library_view.currentIndex()
self.library_view.model().current_changed(current, previous)
def convert_single(self, checked): def convert_single(self, checked):
row_ids = self.get_books_for_conversion() row_ids = self.get_books_for_conversion()
@ -1431,7 +1430,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
return return
if isinstance(job.exception, JobKilled): if isinstance(job.exception, JobKilled):
return return
ConversionErrorDialog(self, _('Conversion Error'), job.gui_text(), error_dialog(self, _('Conversion Error'), job.gui_text(),
show=True) show=True)

View File

@ -71,109 +71,60 @@ def convert_single_ebook(parent, db, book_ids, auto_conversion=False, out_format
return jobs, changed, bad return jobs, changed, bad
def convert_bulk_ebooks(*args): def convert_bulk_ebook(parent, db, book_ids):
pass return [], False, []
#(fmt, parent, db, comics, others): changed = False
if others:
d = get_dialog(fmt)(parent, db)
if d.exec_() != QDialog.Accepted:
others, user_mi = [], None
else:
opts = d.opts
opts.verbose = 2
user_mi = d.user_mi
if comics:
comic_opts = ComicConf.get_bulk_conversion_options(parent)
if not comic_opts:
comics = []
bad_rows = []
jobs = [] jobs = []
total = sum(map(len, (others, comics))) bad = []
total = len(book_ids)
if total == 0: if total == 0:
return return None, None, None
parent.status_bar.showMessage(_('Starting Bulk conversion of %d books')%total, 2000) parent.status_bar.showMessage(_('Starting conversion of %d books') % total, 2000)
for i, row in enumerate(others+comics): d = SingleConfig(parent, db)
row_id = db.id(row) if d.exec_() != QDialog.Accepted:
if row in others: return jobs, changed, bad
data = None
for _fmt in EPUB_PREFERRED_SOURCE_FORMATS:
try:
data = db.format(row, _fmt.upper())
if data is not None:
break
except:
continue
if data is None:
bad_rows.append(row)
continue
options = opts.copy()
mi = db.get_metadata(row)
if user_mi is not None:
if user_mi.series_index == 1:
user_mi.series_index = None
mi.smart_update(user_mi)
db.set_metadata(db.id(row), mi)
opf = OPFCreator(os.getcwdu(), mi)
opf_file = PersistentTemporaryFile('.opf')
opf.render(opf_file)
opf_file.close()
pt = PersistentTemporaryFile('.'+_fmt.lower())
pt.write(data)
pt.close()
of = PersistentTemporaryFile('.'+fmt)
of.close()
cover = db.cover(row)
cf = None
if cover:
cf = PersistentTemporaryFile('.jpeg')
cf.write(cover)
cf.close()
options.cover = cf.name
options.output = of.name
options.from_opf = opf_file.name
args = [options, pt.name]
desc = _('Convert book %d of %d (%s)')%(i+1, total, repr(mi.title))
temp_files = [cf] if cf is not None else []
temp_files.extend([opf_file, pt, of])
jobs.append(('any2'+fmt, args, desc, fmt.upper(), row_id, temp_files))
else:
options = comic_opts.copy()
mi = db.get_metadata(row)
if mi.title:
options.title = mi.title
if mi.authors:
options.author = ','.join(mi.authors)
data = None
for _fmt in ['cbz', 'cbr']:
try:
data = db.format(row, _fmt.upper())
if data is not None:
break
except:
continue
pt = PersistentTemporaryFile('.'+_fmt.lower()) out_format = d.output_format
pt.write(data) recs = cPickle.loads(d.recommendations)
pt.close()
of = PersistentTemporaryFile('.'+fmt)
of.close()
setattr(options, 'output', of.name)
options.verbose = 1
args = [pt.name, options]
desc = _('Convert book %d of %d (%s)')%(i+1, total, repr(mi.title))
jobs.append(('comic2'+fmt, args, desc, fmt.upper(), row_id, [pt, of]))
if bad_rows: for i, book_id in enumerate(book_ids):
temp_files = []
try:
d = SingleConfig(parent, db, book_id, None, out_format)
d.accept()
mi = db.get_metadata(book_id, True)
in_file = db.format_abspath(book_id, d.input_format, True)
out_file = PersistentTemporaryFile('.' + output_format)
out_file.write(output_format)
out_file.close()
desc = _('Convert book %d of %d (%s)') % (i + 1, total, repr(mi.title))
args = [in_file, out_file.name, recs]
temp_files = [out_file]
jobs.append(('gui_convert', args, desc, d.output_format.upper(), book_id, temp_files))
changed = True
except NoSupportedInputFormats:
bad.append(book_id)
if bad != []:
res = [] res = []
for row in bad_rows: for id in bad:
title = db.title(row) title = db.title(id, True)
res.append('<li>%s</li>'%title) res.append('%s'%title)
msg = _('<p>Could not convert %d of %d books, because no suitable source format was found.<ul>%s</ul>')%(len(res), total, '\n'.join(res)) msg = '%s' % '\n'.join(res)
warning_dialog(parent, _('Could not convert some books'), msg).exec_() warning_dialog(parent, _('Could not convert some books'),
_('Could not convert %d of %d books, because no suitable source format was found.' % (len(res), total)),
msg).exec_()
return jobs, False return jobs, changed, bad
def _fetch_news(data, fmt): def _fetch_news(data, fmt):
pt = PersistentTemporaryFile(suffix='_feeds2%s.%s'%(fmt.lower(), fmt.lower())) pt = PersistentTemporaryFile(suffix='_feeds2%s.%s'%(fmt.lower(), fmt.lower()))

View File

@ -19,7 +19,6 @@ from calibre import fit_image
from calibre.utils.fontconfig import find_font_families from calibre.utils.fontconfig import find_font_families
from calibre.ebooks.metadata.meta import metadata_from_filename from calibre.ebooks.metadata.meta import metadata_from_filename
from calibre.utils.config import prefs from calibre.utils.config import prefs
from calibre.gui2.dialogs.warning_ui import Ui_Dialog as Ui_WarningDialog
class ProgressIndicator(QWidget): class ProgressIndicator(QWidget):
@ -56,16 +55,6 @@ class ProgressIndicator(QWidget):
self.movie.setPaused(True) self.movie.setPaused(True)
self.setVisible(False) self.setVisible(False)
class WarningDialog(QDialog, Ui_WarningDialog):
def __init__(self, title, msg, details, parent=None):
QDialog.__init__(self, parent)
self.setupUi(self)
self.setWindowTitle(title)
self.msg.setText(msg)
self.details.setText(details)
class FilenamePattern(QWidget, Ui_Form): class FilenamePattern(QWidget, Ui_Form):
def __init__(self, parent): def __init__(self, parent):