2
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
3
from __future__ import with_statement
6
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
7
__docformat__ = 'restructuredtext en'
11
from PyQt4.Qt import QString, SIGNAL, QAbstractListModel, Qt, QVariant, QFont
13
from calibre.gui2 import ResizableDialog, NONE
14
from calibre.ebooks.conversion.config import GuiRecommendations, save_specifics, \
16
from calibre.gui2.convert.single_ui import Ui_Dialog
17
from calibre.gui2.convert.metadata import MetadataWidget
18
from calibre.gui2.convert.look_and_feel import LookAndFeelWidget
19
from calibre.gui2.convert.page_setup import PageSetupWidget
20
from calibre.gui2.convert.structure_detection import StructureDetectionWidget
21
from calibre.gui2.convert.toc import TOCWidget
23
from calibre.ebooks.conversion.plumber import Plumber, supported_input_formats
24
from calibre.customize.ui import available_output_formats
25
from calibre.customize.conversion import OptionRecommendation
26
from calibre.utils.config import prefs
27
from calibre.utils.logging import Log
29
class NoSupportedInputFormats(Exception):
32
def sort_formats_by_preference(formats, prefs):
35
x = prefs.index(x.upper())
39
y = prefs.index(y.upper())
43
return sorted(formats, cmp=fcmp)
45
class GroupModel(QAbstractListModel):
47
def __init__(self, widgets):
48
self.widgets = widgets
49
QAbstractListModel.__init__(self)
51
def rowCount(self, *args):
52
return len(self.widgets)
54
def data(self, index, role):
56
widget = self.widgets[index.row()]
59
if role == Qt.DisplayRole:
60
return QVariant(widget.config_title())
61
if role == Qt.DecorationRole:
62
return QVariant(widget.config_icon())
63
if role == Qt.FontRole:
69
class Config(ResizableDialog, Ui_Dialog):
71
Configuration dialog for single book conversion. If accepted, has the
72
following important attributes
74
input_path - Path to input file
75
output_format - Output format (without a leading .)
76
input_format - Input format (without a leading .)
77
opf_path - Path to OPF file with user specified metadata
78
cover_path - Path to user specified cover (can be None)
79
recommendations - A pickled list of 3 tuples in the same format as the
80
recommendations member of the Input/Output plugins.
83
def __init__(self, parent, db, book_id,
84
preferred_input_format=None, preferred_output_format=None):
85
ResizableDialog.__init__(self, parent)
87
if preferred_input_format is None and db is not None:
88
recs = load_specifics(db, book_id)
90
preferred_input_format = recs.get('gui_preferred_input_format',
93
self.setup_input_output_formats(db, book_id, preferred_input_format,
94
preferred_output_format)
95
self.db, self.book_id = db, book_id
98
self.connect(self.input_formats, SIGNAL('currentIndexChanged(QString)'),
100
self.connect(self.output_formats, SIGNAL('currentIndexChanged(QString)'),
102
self.connect(self.groups, SIGNAL('activated(QModelIndex)'),
104
self.connect(self.groups, SIGNAL('clicked(QModelIndex)'),
106
self.connect(self.groups, SIGNAL('entered(QModelIndex)'),
107
self.show_group_help)
108
self.groups.setMouseTracking(True)
111
def input_format(self):
112
return unicode(self.input_formats.currentText()).lower()
115
def output_format(self):
116
return unicode(self.output_formats.currentText()).lower()
119
def setup_pipeline(self, *args):
120
oidx = self.groups.currentIndex().row()
121
input_format = self.input_format
122
output_format = self.output_format
123
input_path = self.db.format_abspath(self.book_id, input_format,
125
self.input_path = input_path
126
output_path = 'dummy.'+output_format
129
self.plumber = Plumber(input_path, output_path, log)
131
def widget_factory(cls):
132
return cls(self.stack, self.plumber.get_option_by_name,
133
self.plumber.get_option_help, self.db, self.book_id)
136
self.mw = widget_factory(MetadataWidget)
137
self.setWindowTitle(_('Convert')+ ' ' + unicode(self.mw.title.text()))
138
lf = widget_factory(LookAndFeelWidget)
139
ps = widget_factory(PageSetupWidget)
140
sd = widget_factory(StructureDetectionWidget)
141
toc = widget_factory(TOCWidget)
144
name = self.plumber.output_plugin.name.lower().replace(' ', '_')
146
output_widget = __import__('calibre.gui2.convert.'+name,
148
pw = output_widget.PluginWidget
149
pw.ICON = ':/images/back.svg'
150
pw.HELP = _('Options specific to the output format.')
151
output_widget = widget_factory(pw)
155
name = self.plumber.input_plugin.name.lower().replace(' ', '_')
157
input_widget = __import__('calibre.gui2.convert.'+name,
159
pw = input_widget.PluginWidget
160
pw.ICON = ':/images/forward.svg'
161
pw.HELP = _('Options specific to the input format.')
162
input_widget = widget_factory(pw)
167
c = self.stack.currentWidget()
169
self.stack.removeWidget(c)
171
widgets = [self.mw, lf, ps, sd, toc]
172
if input_widget is not None:
173
widgets.append(input_widget)
174
if output_widget is not None:
175
widgets.append(output_widget)
177
self.stack.addWidget(w)
178
self.connect(w, SIGNAL('set_help(PyQt_PyObject)'),
179
self.help.setPlainText)
181
self._groups_model = GroupModel(widgets)
182
self.groups.setModel(self._groups_model)
184
idx = oidx if -1 < oidx < self._groups_model.rowCount() else 0
185
self.groups.setCurrentIndex(self._groups_model.index(idx))
186
self.stack.setCurrentIndex(idx)
189
def setup_input_output_formats(self, db, book_id, preferred_input_format,
190
preferred_output_format):
191
if preferred_output_format:
192
preferred_output_format = preferred_output_format.lower()
193
available_formats = db.formats(book_id, index_is_id=True)
194
if not available_formats:
195
available_formats = ''
196
available_formats = set([x.lower() for x in
197
available_formats.split(',')])
198
input_formats = set([x.lower() for x in supported_input_formats()])
200
sorted(available_formats.intersection(input_formats))
201
if not input_formats:
202
raise NoSupportedInputFormats
203
output_formats = sorted(available_output_formats())
204
output_formats.remove('oeb')
205
preferred_input_format = preferred_input_format if \
206
preferred_input_format in input_formats else \
207
sort_formats_by_preference(input_formats,
208
prefs['input_format_order'])[0]
209
preferred_output_format = preferred_output_format if \
210
preferred_output_format in output_formats else \
211
sort_formats_by_preference(output_formats,
212
prefs['output_format'])[0]
213
self.input_formats.addItems(list(map(QString, [x.upper() for x in
215
self.output_formats.addItems(list(map(QString, [x.upper() for x in
217
self.input_formats.setCurrentIndex(input_formats.index(preferred_input_format))
218
self.output_formats.setCurrentIndex(output_formats.index(preferred_output_format))
220
def show_pane(self, index):
221
self.stack.setCurrentIndex(index.row())
224
recs = GuiRecommendations()
225
for w in self._groups_model.widgets:
226
if not w.pre_commit_check():
228
x = w.commit(save_defaults=False)
230
self.opf_file, self.cover_file = self.mw.opf_file, self.mw.cover_file
231
self._recommendations = recs
232
if self.db is not None:
233
recs['gui_preferred_input_format'] = self.input_format
234
save_specifics(self.db, self.book_id, recs)
235
ResizableDialog.accept(self)
238
def recommendations(self):
239
recs = [(k, v, OptionRecommendation.HIGH) for k, v in
240
self._recommendations.items()]
241
return cPickle.dumps(recs, -1)
243
def show_group_help(self, index):
244
widget = self._groups_model.widgets[index.row()]
245
self.help.setPlainText(widget.HELP)
248
if __name__ == '__main__':
249
from calibre.library.database2 import LibraryDatabase2
250
from calibre.gui2 import images_rc, Application
253
db = LibraryDatabase2('/home/kovid/documents/library')
254
d = Config(None, db, 594)