~ubuntu-branches/ubuntu/karmic/calibre/karmic

« back to all changes in this revision

Viewing changes to src/calibre/gui2/convert/single.py

  • Committer: Bazaar Package Importer
  • Author(s): Martin Pitt
  • Date: 2009-07-30 12:49:41 UTC
  • mfrom: (1.3.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20090730124941-qjdsmri25zt8zocn
Tags: 0.6.3+dfsg-0ubuntu1
* New upstream release. Please see http://calibre.kovidgoyal.net/new_in_6/
  for the list of new features and changes.
* remove_postinstall.patch: Update for new version.
* build_debug.patch: Does not apply any more, disable for now. Might not be
  necessary any more.
* debian/copyright: Fix reference to versionless GPL.
* debian/rules: Drop obsolete dh_desktop call.
* debian/rules: Add workaround for weird Python 2.6 setuptools behaviour of
  putting compiled .so files into src/calibre/plugins/calibre/plugins
  instead of src/calibre/plugins.
* debian/rules: Drop hal fdi moving, new upstream version does not use hal
  any more. Drop hal dependency, too.
* debian/rules: Install udev rules into /lib/udev/rules.d.
* Add debian/calibre.preinst: Remove unmodified
  /etc/udev/rules.d/95-calibre.rules on upgrade.
* debian/control: Bump Python dependencies to 2.6, since upstream needs
  it now.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/env python
 
2
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
 
3
from __future__ import with_statement
 
4
 
 
5
__license__   = 'GPL v3'
 
6
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
 
7
__docformat__ = 'restructuredtext en'
 
8
 
 
9
import sys, cPickle
 
10
 
 
11
from PyQt4.Qt import QString, SIGNAL, QAbstractListModel, Qt, QVariant, QFont
 
12
 
 
13
from calibre.gui2 import ResizableDialog, NONE
 
14
from calibre.ebooks.conversion.config import GuiRecommendations, save_specifics, \
 
15
        load_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
 
22
 
 
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
 
28
 
 
29
class NoSupportedInputFormats(Exception):
 
30
    pass
 
31
 
 
32
def sort_formats_by_preference(formats, prefs):
 
33
    def fcmp(x, y):
 
34
        try:
 
35
            x = prefs.index(x.upper())
 
36
        except ValueError:
 
37
            x = sys.maxint
 
38
        try:
 
39
            y = prefs.index(y.upper())
 
40
        except ValueError:
 
41
            y = sys.maxint
 
42
        return cmp(x, y)
 
43
    return sorted(formats, cmp=fcmp)
 
44
 
 
45
class GroupModel(QAbstractListModel):
 
46
 
 
47
    def __init__(self, widgets):
 
48
        self.widgets = widgets
 
49
        QAbstractListModel.__init__(self)
 
50
 
 
51
    def rowCount(self, *args):
 
52
        return len(self.widgets)
 
53
 
 
54
    def data(self, index, role):
 
55
        try:
 
56
            widget = self.widgets[index.row()]
 
57
        except:
 
58
            return NONE
 
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:
 
64
            f = QFont()
 
65
            f.setBold(True)
 
66
            return QVariant(f)
 
67
        return NONE
 
68
 
 
69
class Config(ResizableDialog, Ui_Dialog):
 
70
    '''
 
71
    Configuration dialog for single book conversion. If accepted, has the
 
72
    following important attributes
 
73
 
 
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.
 
81
    '''
 
82
 
 
83
    def __init__(self, parent, db, book_id,
 
84
            preferred_input_format=None, preferred_output_format=None):
 
85
        ResizableDialog.__init__(self, parent)
 
86
 
 
87
        if preferred_input_format is None and db is not None:
 
88
            recs = load_specifics(db, book_id)
 
89
            if recs:
 
90
                preferred_input_format = recs.get('gui_preferred_input_format',
 
91
                        None)
 
92
 
 
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
 
96
        self.setup_pipeline()
 
97
 
 
98
        self.connect(self.input_formats, SIGNAL('currentIndexChanged(QString)'),
 
99
                self.setup_pipeline)
 
100
        self.connect(self.output_formats, SIGNAL('currentIndexChanged(QString)'),
 
101
                self.setup_pipeline)
 
102
        self.connect(self.groups, SIGNAL('activated(QModelIndex)'),
 
103
                self.show_pane)
 
104
        self.connect(self.groups, SIGNAL('clicked(QModelIndex)'),
 
105
                self.show_pane)
 
106
        self.connect(self.groups, SIGNAL('entered(QModelIndex)'),
 
107
                self.show_group_help)
 
108
        self.groups.setMouseTracking(True)
 
109
 
 
110
    @property
 
111
    def input_format(self):
 
112
        return unicode(self.input_formats.currentText()).lower()
 
113
 
 
114
    @property
 
115
    def output_format(self):
 
116
        return unicode(self.output_formats.currentText()).lower()
 
117
 
 
118
 
 
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,
 
124
                index_is_id=True)
 
125
        self.input_path = input_path
 
126
        output_path = 'dummy.'+output_format
 
127
        log = Log()
 
128
        log.outputs = []
 
129
        self.plumber = Plumber(input_path, output_path, log)
 
130
 
 
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)
 
134
 
 
135
 
 
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)
 
142
 
 
143
        output_widget = None
 
144
        name = self.plumber.output_plugin.name.lower().replace(' ', '_')
 
145
        try:
 
146
            output_widget = __import__('calibre.gui2.convert.'+name,
 
147
                        fromlist=[1])
 
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)
 
152
        except ImportError:
 
153
            pass
 
154
        input_widget = None
 
155
        name = self.plumber.input_plugin.name.lower().replace(' ', '_')
 
156
        try:
 
157
            input_widget = __import__('calibre.gui2.convert.'+name,
 
158
                        fromlist=[1])
 
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)
 
163
        except ImportError:
 
164
            pass
 
165
 
 
166
        while True:
 
167
            c = self.stack.currentWidget()
 
168
            if not c: break
 
169
            self.stack.removeWidget(c)
 
170
 
 
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)
 
176
        for w in widgets:
 
177
            self.stack.addWidget(w)
 
178
            self.connect(w, SIGNAL('set_help(PyQt_PyObject)'),
 
179
                    self.help.setPlainText)
 
180
 
 
181
        self._groups_model = GroupModel(widgets)
 
182
        self.groups.setModel(self._groups_model)
 
183
 
 
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)
 
187
 
 
188
 
 
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()])
 
199
        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
 
214
            input_formats])))
 
215
        self.output_formats.addItems(list(map(QString, [x.upper() for x in
 
216
            output_formats])))
 
217
        self.input_formats.setCurrentIndex(input_formats.index(preferred_input_format))
 
218
        self.output_formats.setCurrentIndex(output_formats.index(preferred_output_format))
 
219
 
 
220
    def show_pane(self, index):
 
221
        self.stack.setCurrentIndex(index.row())
 
222
 
 
223
    def accept(self):
 
224
        recs = GuiRecommendations()
 
225
        for w in self._groups_model.widgets:
 
226
            if not w.pre_commit_check():
 
227
                return
 
228
            x = w.commit(save_defaults=False)
 
229
            recs.update(x)
 
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)
 
236
 
 
237
    @property
 
238
    def recommendations(self):
 
239
        recs = [(k, v, OptionRecommendation.HIGH) for k, v in
 
240
                self._recommendations.items()]
 
241
        return cPickle.dumps(recs, -1)
 
242
 
 
243
    def show_group_help(self, index):
 
244
        widget = self._groups_model.widgets[index.row()]
 
245
        self.help.setPlainText(widget.HELP)
 
246
 
 
247
 
 
248
if __name__ == '__main__':
 
249
    from calibre.library.database2 import LibraryDatabase2
 
250
    from calibre.gui2 import images_rc, Application
 
251
    images_rc
 
252
    a  = Application([])
 
253
    db = LibraryDatabase2('/home/kovid/documents/library')
 
254
    d  = Config(None, db, 594)
 
255
    d.show()
 
256
    a.exec_()
 
257