EPUB Output: Have the GUI use a UUID for package identifier when creating EPUBs

This commit is contained in:
Kovid Goyal 2009-03-27 12:28:15 -07:00
parent ea0ec8293b
commit d14682525c

View File

@ -6,14 +6,14 @@ __docformat__ = 'restructuredtext en'
''' '''
The GUI for conversion to EPUB. The GUI for conversion to EPUB.
''' '''
import os import os, uuid
from PyQt4.Qt import QDialog, QSpinBox, QDoubleSpinBox, QComboBox, QLineEdit, \ from PyQt4.Qt import QDialog, QSpinBox, QDoubleSpinBox, QComboBox, QLineEdit, \
QTextEdit, QCheckBox, Qt, QPixmap, QIcon, QListWidgetItem, SIGNAL QTextEdit, QCheckBox, Qt, QPixmap, QIcon, QListWidgetItem, SIGNAL
from lxml.etree import XPath from lxml.etree import XPath
from calibre.gui2.dialogs.choose_format import ChooseFormatDialog from calibre.gui2.dialogs.choose_format import ChooseFormatDialog
from calibre.gui2.dialogs.epub_ui import Ui_Dialog from calibre.gui2.dialogs.epub_ui import Ui_Dialog
from calibre.gui2 import error_dialog, choose_images, pixmap_to_data, ResizableDialog from calibre.gui2 import error_dialog, choose_images, pixmap_to_data, ResizableDialog
from calibre.ebooks.epub.from_any import SOURCE_FORMATS, config as epubconfig from calibre.ebooks.epub.from_any import SOURCE_FORMATS, config as epubconfig
from calibre.ebooks.metadata import MetaInformation from calibre.ebooks.metadata import MetaInformation
@ -23,16 +23,16 @@ from calibre.ebooks.metadata import authors_to_string, string_to_authors
class Config(ResizableDialog, Ui_Dialog): class Config(ResizableDialog, Ui_Dialog):
OUTPUT = 'EPUB' OUTPUT = 'EPUB'
def __init__(self, parent, db, row=None, config=epubconfig): def __init__(self, parent, db, row=None, config=epubconfig):
ResizableDialog.__init__(self, parent) ResizableDialog.__init__(self, parent)
self.hide_controls() self.hide_controls()
self.connect(self.category_list, SIGNAL('itemEntered(QListWidgetItem *)'), self.connect(self.category_list, SIGNAL('itemEntered(QListWidgetItem *)'),
self.show_category_help) self.show_category_help)
self.connect(self.cover_button, SIGNAL("clicked()"), self.select_cover) self.connect(self.cover_button, SIGNAL("clicked()"), self.select_cover)
self.cover_changed = False self.cover_changed = False
self.db = db self.db = db
self.id = None self.id = None
@ -52,7 +52,7 @@ class Config(ResizableDialog, Ui_Dialog):
self.setWindowTitle(_('Bulk convert to ')+self.OUTPUT) self.setWindowTitle(_('Bulk convert to ')+self.OUTPUT)
else: else:
self.setWindowTitle((_(u'Convert %s to ')%unicode(self.title.text()))+self.OUTPUT) self.setWindowTitle((_(u'Convert %s to ')%unicode(self.title.text()))+self.OUTPUT)
def hide_controls(self): def hide_controls(self):
self.source_profile_label.setVisible(False) self.source_profile_label.setVisible(False)
self.opt_source_profile.setVisible(False) self.opt_source_profile.setVisible(False)
@ -63,7 +63,7 @@ class Config(ResizableDialog, Ui_Dialog):
self.opt_rescale_images.setVisible(False) self.opt_rescale_images.setVisible(False)
self.opt_ignore_tables.setVisible(False) self.opt_ignore_tables.setVisible(False)
self.opt_prefer_author_sort.setVisible(False) self.opt_prefer_author_sort.setVisible(False)
def initialize(self): def initialize(self):
self.__w = [] self.__w = []
self.__w.append(QIcon(':/images/dialog_information.svg')) self.__w.append(QIcon(':/images/dialog_information.svg'))
@ -76,11 +76,11 @@ class Config(ResizableDialog, Ui_Dialog):
self.item4 = QListWidgetItem(self.__w[-1], _('Chapter Detection').replace(' ','\n'), self.category_list) self.item4 = QListWidgetItem(self.__w[-1], _('Chapter Detection').replace(' ','\n'), self.category_list)
self.setup_tooltips() self.setup_tooltips()
self.initialize_options() self.initialize_options()
def set_help(self, msg): def set_help(self, msg):
if msg and getattr(msg, 'strip', lambda:True)(): if msg and getattr(msg, 'strip', lambda:True)():
self.help_view.setPlainText(msg) self.help_view.setPlainText(msg)
def setup_tooltips(self): def setup_tooltips(self):
for opt in self.config.option_set.preferences: for opt in self.config.option_set.preferences:
g = getattr(self, 'opt_'+opt.name, False) g = getattr(self, 'opt_'+opt.name, False)
@ -90,19 +90,19 @@ class Config(ResizableDialog, Ui_Dialog):
g.setToolTip(help.replace('<', '&lt;').replace('>', '&gt;')) g.setToolTip(help.replace('<', '&lt;').replace('>', '&gt;'))
g.setWhatsThis(help.replace('<', '&lt;').replace('>', '&gt;')) g.setWhatsThis(help.replace('<', '&lt;').replace('>', '&gt;'))
g.__class__.enterEvent = lambda obj, event: self.set_help(getattr(obj, '_help', obj.toolTip())) g.__class__.enterEvent = lambda obj, event: self.set_help(getattr(obj, '_help', obj.toolTip()))
def show_category_help(self, item): def show_category_help(self, item):
text = unicode(item.text()) text = unicode(item.text())
help = { help = {
_('Metadata') : _('Specify metadata such as title and author for the book.\n\nMetadata will be updated in the database as well as the generated %s file.')%self.OUTPUT, _('Metadata') : _('Specify metadata such as title and author for the book.\n\nMetadata will be updated in the database as well as the generated %s file.')%self.OUTPUT,
_('Look & Feel') : _('Adjust the look of the generated ebook by specifying things like font sizes.'), _('Look & Feel') : _('Adjust the look of the generated ebook by specifying things like font sizes.'),
_('Page Setup') : _('Specify the page layout settings like margins.'), _('Page Setup') : _('Specify the page layout settings like margins.'),
_('Chapter Detection') : _('Fine tune the detection of chapter and section headings.'), _('Chapter Detection') : _('Fine tune the detection of chapter and section headings.'),
} }
self.set_help(help[text.replace('\n', ' ')]) self.set_help(help[text.replace('\n', ' ')])
def select_cover(self): def select_cover(self):
files = choose_images(self, 'change cover dialog', files = choose_images(self, 'change cover dialog',
_('Choose cover for ') + unicode(self.title.text())) _('Choose cover for ') + unicode(self.title.text()))
if not files: if not files:
return return
@ -110,7 +110,7 @@ class Config(ResizableDialog, Ui_Dialog):
if _file: if _file:
_file = os.path.abspath(_file) _file = os.path.abspath(_file)
if not os.access(_file, os.R_OK): if not os.access(_file, os.R_OK):
d = error_dialog(self.window, _('Cannot read'), d = error_dialog(self.window, _('Cannot read'),
_('You do not have permission to read the file: ') + _file) _('You do not have permission to read the file: ') + _file)
d.exec_() d.exec_()
return return
@ -118,7 +118,7 @@ class Config(ResizableDialog, Ui_Dialog):
try: try:
cf = open(_file, "rb") cf = open(_file, "rb")
cover = cf.read() cover = cf.read()
except IOError, e: except IOError, e:
d = error_dialog(self.window, _('Error reading file'), d = error_dialog(self.window, _('Error reading file'),
_("<p>There was an error reading from file: <br /><b>") + _file + "</b></p><br />"+str(e)) _("<p>There was an error reading from file: <br /><b>") + _file + "</b></p><br />"+str(e))
d.exec_() d.exec_()
@ -134,14 +134,14 @@ class Config(ResizableDialog, Ui_Dialog):
self.cover.setPixmap(pix) self.cover.setPixmap(pix)
self.cover_changed = True self.cover_changed = True
self.cpixmap = pix self.cpixmap = pix
def initialize_metadata_options(self): def initialize_metadata_options(self):
all_series = self.db.all_series() all_series = self.db.all_series()
all_series.sort(cmp=lambda x, y : cmp(x[1], y[1])) all_series.sort(cmp=lambda x, y : cmp(x[1], y[1]))
for series in all_series: for series in all_series:
self.series.addItem(series[1]) self.series.addItem(series[1])
self.series.setCurrentIndex(-1) self.series.setCurrentIndex(-1)
if self.row is not None: if self.row is not None:
mi = self.db.get_metadata(self.id, index_is_id=True) mi = self.db.get_metadata(self.id, index_is_id=True)
self.title.setText(mi.title) self.title.setText(mi.title)
@ -157,14 +157,14 @@ class Config(ResizableDialog, Ui_Dialog):
self.series.setCurrentIndex(self.series.findText(mi.series)) self.series.setCurrentIndex(self.series.findText(mi.series))
if mi.series_index is not None: if mi.series_index is not None:
self.series_index.setValue(mi.series_index) self.series_index.setValue(mi.series_index)
cover = self.db.cover(self.id, index_is_id=True) cover = self.db.cover(self.id, index_is_id=True)
if cover: if cover:
pm = QPixmap() pm = QPixmap()
pm.loadFromData(cover) pm.loadFromData(cover)
if not pm.isNull(): if not pm.isNull():
self.cover.setPixmap(pm) self.cover.setPixmap(pm)
def get_title_and_authors(self): def get_title_and_authors(self):
title = unicode(self.title.text()).strip() title = unicode(self.title.text()).strip()
if not title: if not title:
@ -172,7 +172,7 @@ class Config(ResizableDialog, Ui_Dialog):
authors = unicode(self.author.text()).strip() authors = unicode(self.author.text()).strip()
authors = string_to_authors(authors) if authors else [_('Unknown')] authors = string_to_authors(authors) if authors else [_('Unknown')]
return title, authors return title, authors
def get_metadata(self): def get_metadata(self):
title, authors = self.get_title_and_authors() title, authors = self.get_title_and_authors()
mi = MetaInformation(title, authors) mi = MetaInformation(title, authors)
@ -191,9 +191,9 @@ class Config(ResizableDialog, Ui_Dialog):
tags = [t.strip() for t in unicode(self.tags.text()).split(',')] tags = [t.strip() for t in unicode(self.tags.text()).split(',')]
if tags: if tags:
mi.tags = tags mi.tags = tags
return mi return mi
def read_settings(self): def read_settings(self):
for pref in self.config.option_set.preferences: for pref in self.config.option_set.preferences:
g = getattr(self, 'opt_'+pref.name, False) g = getattr(self, 'opt_'+pref.name, False)
@ -209,9 +209,9 @@ class Config(ResizableDialog, Ui_Dialog):
elif isinstance(g, QCheckBox): elif isinstance(g, QCheckBox):
self.config.set(pref.name, bool(g.isChecked())) self.config.set(pref.name, bool(g.isChecked()))
if self.row is not None: if self.row is not None:
self.db.set_conversion_options(self.id, self.OUTPUT.lower(), self.config.src) self.db.set_conversion_options(self.id, self.OUTPUT.lower(), self.config.src)
def initialize_options(self): def initialize_options(self):
self.initialize_metadata_options() self.initialize_metadata_options()
values = self.config.parse() values = self.config.parse()
@ -232,16 +232,16 @@ class Config(ResizableDialog, Ui_Dialog):
g.setCurrentIndex(g.findText(val)) g.setCurrentIndex(g.findText(val))
elif isinstance(g, QCheckBox): elif isinstance(g, QCheckBox):
g.setCheckState(Qt.Checked if bool(val) else Qt.Unchecked) g.setCheckState(Qt.Checked if bool(val) else Qt.Unchecked)
def get_source_format(self): def get_source_format(self):
self.source_format = None self.source_format = None
if self.row is not None: if self.row is not None:
temp = self.db.formats(self.id, index_is_id=True) temp = self.db.formats(self.id, index_is_id=True)
if not temp: if not temp:
error_dialog(self.parent(), _('Cannot convert'), error_dialog(self.parent(), _('Cannot convert'),
_('This book has no available formats')).exec_() _('This book has no available formats')).exec_()
available_formats = [f.upper().strip() for f in temp.split(',')] available_formats = [f.upper().strip() for f in temp.split(',')]
choices = [fmt.upper() for fmt in SOURCE_FORMATS if fmt.upper() in available_formats] choices = [fmt.upper() for fmt in SOURCE_FORMATS if fmt.upper() in available_formats]
if not choices: if not choices:
@ -253,7 +253,7 @@ class Config(ResizableDialog, Ui_Dialog):
d = ChooseFormatDialog(self.parent(), _('Choose the format to convert to ')+self.OUTPUT, choices) d = ChooseFormatDialog(self.parent(), _('Choose the format to convert to ')+self.OUTPUT, choices)
if d.exec_() == QDialog.Accepted: if d.exec_() == QDialog.Accepted:
self.source_format = d.format() self.source_format = d.format()
def accept(self): def accept(self):
for opt in ('chapter', 'level1_toc', 'level2_toc', 'level3_toc', 'page', for opt in ('chapter', 'level1_toc', 'level2_toc', 'level3_toc', 'page',
'page_names'): 'page_names'):
@ -263,7 +263,7 @@ class Config(ResizableDialog, Ui_Dialog):
XPath(text,namespaces={'re':'http://exslt.org/regular-expressions'}) XPath(text,namespaces={'re':'http://exslt.org/regular-expressions'})
except Exception, err: except Exception, err:
error_dialog(self, _('Invalid XPath expression'), error_dialog(self, _('Invalid XPath expression'),
_('The expression %s is invalid. Error: %s')%(text, err) _('The expression %s is invalid. Error: %s')%(text, err)
).exec_() ).exec_()
return return
mi = self.get_metadata() mi = self.get_metadata()
@ -272,6 +272,7 @@ class Config(ResizableDialog, Ui_Dialog):
if self.row is not None: if self.row is not None:
self.db.set_metadata(self.id, mi) self.db.set_metadata(self.id, mi)
self.mi = self.db.get_metadata(self.id, index_is_id=True) self.mi = self.db.get_metadata(self.id, index_is_id=True)
self.mi.application_id = uuid.uuid4()
opf = OPFCreator(os.getcwdu(), self.mi) opf = OPFCreator(os.getcwdu(), self.mi)
self.opf_file = PersistentTemporaryFile('.opf') self.opf_file = PersistentTemporaryFile('.opf')
opf.render(self.opf_file) opf.render(self.opf_file)
@ -286,5 +287,5 @@ class Config(ResizableDialog, Ui_Dialog):
self.cover_file = cf self.cover_file = cf
self.opts = self.config.parse() self.opts = self.config.parse()
QDialog.accept(self) QDialog.accept(self)