3
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
4
__docformat__ = 'restructuredtext en'
7
The GUI for conversion to EPUB.
11
from PyQt4.Qt import QDialog, QSpinBox, QDoubleSpinBox, QComboBox, QLineEdit, \
12
QTextEdit, QCheckBox, Qt, QPixmap, QIcon, QListWidgetItem, SIGNAL
13
from lxml.etree import XPath
15
from calibre.gui2.dialogs.choose_format import ChooseFormatDialog
16
from calibre.gui2.dialogs.epub_ui import Ui_Dialog
17
from calibre.gui2 import error_dialog, choose_images, pixmap_to_data, ResizableDialog
18
from calibre.ebooks.epub.from_any import SOURCE_FORMATS, config as epubconfig
19
from calibre.ebooks.metadata import MetaInformation
20
from calibre.ptempfile import PersistentTemporaryFile
21
from calibre.ebooks.metadata.opf2 import OPFCreator
22
from calibre.ebooks.metadata import authors_to_string, string_to_authors
25
class Config(ResizableDialog, Ui_Dialog):
29
def __init__(self, parent, db, row=None, config=epubconfig):
30
ResizableDialog.__init__(self, parent)
32
self.connect(self.category_list, SIGNAL('itemEntered(QListWidgetItem *)'),
33
self.show_category_help)
34
self.connect(self.cover_button, SIGNAL("clicked()"), self.select_cover)
36
self.cover_changed = False
42
base = config().as_string() + '\n\n'
43
defaults = self.db.conversion_options(self.id, self.OUTPUT.lower())
44
defaults = base + (defaults if defaults else '')
45
self.config = config(defaults=defaults)
47
self.config = config()
49
self.get_source_format()
50
self.category_list.setCurrentRow(0)
52
self.setWindowTitle(_('Bulk convert to ')+self.OUTPUT)
54
self.setWindowTitle((_(u'Convert %s to ')%unicode(self.title.text()))+self.OUTPUT)
56
def hide_controls(self):
57
self.source_profile_label.setVisible(False)
58
self.opt_source_profile.setVisible(False)
59
self.dest_profile_label.setVisible(False)
60
self.opt_dest_profile.setVisible(False)
61
self.opt_toc_title.setVisible(False)
62
self.toc_title_label.setVisible(False)
63
self.opt_rescale_images.setVisible(False)
64
self.opt_ignore_tables.setVisible(False)
65
self.opt_prefer_author_sort.setVisible(False)
69
self.__w.append(QIcon(':/images/dialog_information.svg'))
70
self.item1 = QListWidgetItem(self.__w[-1], _('Metadata'), self.category_list)
71
self.__w.append(QIcon(':/images/lookfeel.svg'))
72
self.item2 = QListWidgetItem(self.__w[-1], _('Look & Feel').replace(' ','\n'), self.category_list)
73
self.__w.append(QIcon(':/images/page.svg'))
74
self.item3 = QListWidgetItem(self.__w[-1], _('Page Setup').replace(' ','\n'), self.category_list)
75
self.__w.append(QIcon(':/images/chapters.svg'))
76
self.item4 = QListWidgetItem(self.__w[-1], _('Chapter Detection').replace(' ','\n'), self.category_list)
78
self.initialize_options()
80
def set_help(self, msg):
81
if msg and getattr(msg, 'strip', lambda:True)():
82
self.help_view.setPlainText(msg)
84
def setup_tooltips(self):
85
for opt in self.config.option_set.preferences:
86
g = getattr(self, 'opt_'+opt.name, False)
88
help = opt.help.replace('%default', str(opt.default))
90
g.setToolTip(help.replace('<', '<').replace('>', '>'))
91
g.setWhatsThis(help.replace('<', '<').replace('>', '>'))
92
g.__class__.enterEvent = lambda obj, event: self.set_help(getattr(obj, '_help', obj.toolTip()))
94
def show_category_help(self, item):
95
text = unicode(item.text())
97
_('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,
98
_('Look & Feel') : _('Adjust the look of the generated ebook by specifying things like font sizes.'),
99
_('Page Setup') : _('Specify the page layout settings like margins.'),
100
_('Chapter Detection') : _('Fine tune the detection of chapter and section headings.'),
102
self.set_help(help[text.replace('\n', ' ')])
104
def select_cover(self):
105
files = choose_images(self, 'change cover dialog',
106
_('Choose cover for ') + unicode(self.title.text()))
111
_file = os.path.abspath(_file)
112
if not os.access(_file, os.R_OK):
113
d = error_dialog(self.window, _('Cannot read'),
114
_('You do not have permission to read the file: ') + _file)
117
cf, cover = None, None
119
cf = open(_file, "rb")
122
d = error_dialog(self.window, _('Error reading file'),
123
_("<p>There was an error reading from file: <br /><b>") + _file + "</b></p><br />"+str(e))
127
pix.loadFromData(cover)
129
d = error_dialog(self.window, _('Error reading file'),
130
_file + _(" is not a valid picture"))
133
self.cover_path.setText(_file)
134
self.cover.setPixmap(pix)
135
self.cover_changed = True
138
def initialize_metadata_options(self):
139
all_series = self.db.all_series()
140
all_series.sort(cmp=lambda x, y : cmp(x[1], y[1]))
141
for series in all_series:
142
self.series.addItem(series[1])
143
self.series.setCurrentIndex(-1)
145
if self.row is not None:
146
mi = self.db.get_metadata(self.id, index_is_id=True)
147
self.title.setText(mi.title)
149
self.author.setText(authors_to_string(mi.authors))
151
self.author.setText('')
152
self.publisher.setText(mi.publisher if mi.publisher else '')
153
self.author_sort.setText(mi.author_sort if mi.author_sort else '')
154
self.tags.setText(', '.join(mi.tags if mi.tags else []))
155
self.comment.setText(mi.comments if mi.comments else '')
157
self.series.setCurrentIndex(self.series.findText(mi.series))
158
if mi.series_index is not None:
159
self.series_index.setValue(mi.series_index)
161
cover = self.db.cover(self.id, index_is_id=True)
164
pm.loadFromData(cover)
166
self.cover.setPixmap(pm)
168
def get_title_and_authors(self):
169
title = unicode(self.title.text()).strip()
172
authors = unicode(self.author.text()).strip()
173
authors = string_to_authors(authors) if authors else [_('Unknown')]
174
return title, authors
176
def get_metadata(self):
177
title, authors = self.get_title_and_authors()
178
mi = MetaInformation(title, authors)
179
publisher = unicode(self.publisher.text()).strip()
181
mi.publisher = publisher
182
author_sort = unicode(self.author_sort.text()).strip()
184
mi.author_sort = author_sort
185
comments = unicode(self.comment.toPlainText()).strip()
187
mi.comments = comments
188
mi.series_index = int(self.series_index.value())
189
if self.series.currentIndex() > -1:
190
mi.series = unicode(self.series.currentText()).strip()
191
tags = [t.strip() for t in unicode(self.tags.text()).strip().split(',')]
197
def read_settings(self):
198
for pref in self.config.option_set.preferences:
199
g = getattr(self, 'opt_'+pref.name, False)
201
if isinstance(g, (QSpinBox, QDoubleSpinBox)):
202
self.config.set(pref.name, g.value())
203
elif isinstance(g, (QLineEdit, QTextEdit)):
204
func = getattr(g, 'toPlainText', getattr(g, 'text', None))()
206
self.config.set(pref.name, val if val else None)
207
elif isinstance(g, QComboBox):
208
self.config.set(pref.name, unicode(g.currentText()))
209
elif isinstance(g, QCheckBox):
210
self.config.set(pref.name, bool(g.isChecked()))
211
if self.row is not None:
212
self.db.set_conversion_options(self.id, self.OUTPUT.lower(), self.config.src)
215
def initialize_options(self):
216
self.initialize_metadata_options()
217
values = self.config.parse()
218
for pref in self.config.option_set.preferences:
219
g = getattr(self, 'opt_'+pref.name, False)
221
val = getattr(values, pref.name)
224
if isinstance(g, (QSpinBox, QDoubleSpinBox)):
226
elif isinstance(g, (QLineEdit, QTextEdit)):
227
getattr(g, 'setPlainText', g.setText)(val)
228
getattr(g, 'setCursorPosition', lambda x: x)(0)
229
elif isinstance(g, QComboBox):
230
for value in pref.choices:
232
g.setCurrentIndex(g.findText(val))
233
elif isinstance(g, QCheckBox):
234
g.setCheckState(Qt.Checked if bool(val) else Qt.Unchecked)
237
def get_source_format(self):
238
self.source_format = None
239
if self.row is not None:
240
temp = self.db.formats(self.id, index_is_id=True)
242
error_dialog(self.parent(), _('Cannot convert'),
243
_('This book has no available formats')).exec_()
245
available_formats = [f.upper().strip() for f in temp.split(',')]
246
choices = [fmt.upper() for fmt in SOURCE_FORMATS if fmt.upper() in available_formats]
248
error_dialog(self.parent(), _('No available formats'),
249
_('Cannot convert %s as this book has no supported formats')%(self.title.text())).exec_()
250
elif len(choices) == 1:
251
self.source_format = choices[0]
253
d = ChooseFormatDialog(self.parent(), _('Choose the format to convert to ')+self.OUTPUT, choices)
254
if d.exec_() == QDialog.Accepted:
255
self.source_format = d.format()
258
for opt in ('chapter', 'level1_toc', 'level2_toc', 'level3_toc', 'page',
260
text = unicode(getattr(self, 'opt_'+opt).text())
263
XPath(text,namespaces={'re':'http://exslt.org/regular-expressions'})
264
except Exception, err:
265
error_dialog(self, _('Invalid XPath expression'),
266
_('The expression %s is invalid. Error: %s')%(text, err)
269
mi = self.get_metadata()
272
self.cover_file = None
273
if self.row is not None:
274
self.db.set_metadata(self.id, mi)
275
self.mi = self.db.get_metadata(self.id, index_is_id=True)
276
self.mi.application_id = uuid.uuid4()
277
opf = OPFCreator(os.getcwdu(), self.mi)
278
self.opf_file = PersistentTemporaryFile('.opf')
279
opf.render(self.opf_file)
280
self.opf_file.close()
281
if self.cover_changed:
282
self.db.set_cover(self.id, pixmap_to_data(self.cover.pixmap()))
283
cover = self.db.cover(self.id, index_is_id=True)
285
cf = PersistentTemporaryFile('.jpeg')
289
self.opts = self.config.parse()