Integrate the new Tweak Book tool into the main calibre gui

The old Tweak Book tool has become "Unpack Book"
This commit is contained in:
Kovid Goyal 2013-12-01 14:35:24 +05:30
parent 3447684c5e
commit df1c8b7e56
7 changed files with 425 additions and 281 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

View File

@ -907,6 +907,11 @@ class ActionTweakEpub(InterfaceActionBase):
actual_plugin = 'calibre.gui2.actions.tweak_epub:TweakEpubAction'
description = _('Make small tweaks to epub or htmlz files in your calibre library')
class ActionUnpackBook(InterfaceActionBase):
name = 'Unpack Book'
actual_plugin = 'calibre.gui2.actions.unpack_book:UnpackBookAction'
description = _('Make small changes to epub or htmlz files in your calibre library')
class ActionNextMatch(InterfaceActionBase):
name = 'Next Match'
actual_plugin = 'calibre.gui2.actions.next_match:NextMatchAction'
@ -957,7 +962,7 @@ plugins += [ActionAdd, ActionFetchAnnotations, ActionGenerateCatalog,
ActionShowBookDetails,ActionRestart, ActionOpenFolder, ActionConnectShare,
ActionSendToDevice, ActionHelp, ActionPreferences, ActionSimilarBooks,
ActionAddToLibrary, ActionEditCollections, ActionMatchBooks, ActionChooseLibrary,
ActionCopyToLibrary, ActionTweakEpub, ActionNextMatch, ActionStore,
ActionCopyToLibrary, ActionTweakEpub, ActionUnpackBook, ActionNextMatch, ActionStore,
ActionPluginUpdater, ActionPickRandom, ActionEditToC, ActionSortBy,
ActionMarkBooks]

View File

@ -671,8 +671,8 @@ class Cache(object):
'''
Return absolute path to the ebook file of format `format`
Currently used only in calibredb list, the viewer and the catalogs (via
get_data_as_dict()).
Currently used only in calibredb list, the viewer, tweak book and the
catalogs (via get_data_as_dict()).
Apart from the viewer, I don't believe any of the others do any file
I/O with the results of this call.

View File

@ -5,290 +5,56 @@ __license__ = 'GPL v3'
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import os, weakref, shutil
import time
from functools import partial
from PyQt4.Qt import (QDialog, QVBoxLayout, QHBoxLayout, QRadioButton, QFrame,
QPushButton, QLabel, QGroupBox, QGridLayout, QIcon, QSize, QTimer)
from PyQt4.Qt import QTimer, QDialog, QDialogButtonBox, QCheckBox, QVBoxLayout, QLabel, Qt
from calibre import as_unicode
from calibre.constants import isosx
from calibre.gui2 import error_dialog, question_dialog, open_local_file, gprefs
from calibre.gui2 import error_dialog
from calibre.gui2.actions import InterfaceAction
from calibre.ptempfile import (PersistentTemporaryDirectory,
PersistentTemporaryFile)
from calibre.utils.config import prefs, tweaks
class TweakBook(QDialog):
class Choose(QDialog):
def __init__(self, parent, book_id, fmts, db):
def __init__(self, fmts, parent=None):
QDialog.__init__(self, parent)
self.setWindowIcon(QIcon(I('tweak.png')))
self.book_id, self.fmts, self.db_ref = book_id, fmts, weakref.ref(db)
self._exploded = None
self._cleanup_dirs = []
self._cleanup_files = []
self.l = l = QVBoxLayout(self)
self.setLayout(l)
self.setWindowTitle(_('Choose format to tweak'))
self.setup_ui()
self.setWindowTitle(_('Tweak Book') + ' - ' + db.title(book_id,
index_is_id=True))
self.la = la = QLabel(_(
'This book mas multiple formats that can be tweaked. Choose the format you want to tweak.'))
l.addWidget(la)
button = self.fmt_choice_buttons[0]
button_map = {unicode(x.text()):x for x in self.fmt_choice_buttons}
of = prefs['output_format'].upper()
df = tweaks.get('default_tweak_format', None)
lf = gprefs.get('last_tweak_format', None)
if df and df.lower() == 'remember' and lf in button_map:
button = button_map[lf]
elif df and df.upper() in button_map:
button = button_map[df.upper()]
elif of in button_map:
button = button_map[of]
button.setChecked(True)
self.rem = QCheckBox(_('Always ask when more than one format is available'))
self.rem.setChecked(True)
l.addWidget(self.rem)
self.init_state()
for button in self.fmt_choice_buttons:
button.toggled.connect(self.init_state)
self.bb = bb = QDialogButtonBox(self)
l.addWidget(bb)
bb.accepted.connect(self.accept)
bb.rejected.connect(self.reject)
self.buts = buts = []
for fmt in fmts:
b = bb.addButton(fmt.upper(), bb.AcceptRole)
b.clicked.connect(partial(self.chosen, fmt))
buts.append(b)
def init_state(self, *args):
self._exploded = None
self.preview_button.setEnabled(False)
self.rebuild_button.setEnabled(False)
self.explode_button.setEnabled(True)
self.fmt = None
self.resize(self.sizeHint())
def setup_ui(self): # {{{
self._g = g = QHBoxLayout(self)
self.setLayout(g)
self._l = l = QVBoxLayout()
g.addLayout(l)
def chosen(self, fmt):
self.fmt = fmt
fmts = sorted(x.upper() for x in self.fmts)
self.fmt_choice_box = QGroupBox(_('Choose the format to tweak:'), self)
self._fl = fl = QHBoxLayout()
self.fmt_choice_box.setLayout(self._fl)
self.fmt_choice_buttons = [QRadioButton(y, self) for y in fmts]
for x in self.fmt_choice_buttons:
fl.addWidget(x, stretch=10 if x is self.fmt_choice_buttons[-1] else
0)
l.addWidget(self.fmt_choice_box)
self.fmt_choice_box.setVisible(len(fmts) > 1)
def accept(self):
from calibre.gui2.tweak_book import tprefs
tprefs['choose_tweak_fmt'] = self.rem.isChecked()
QDialog.accept(self)
self.help_label = QLabel(_('''\
<h2>About Tweak Book</h2>
<p>Tweak Book allows you to fine tune the appearance of an ebook by
making small changes to its internals. In order to use Tweak Book,
you need to know a little bit about HTML and CSS, technologies that
are used in ebooks. Follow the steps:</p>
<br>
<ol>
<li>Click "Explode Book": This will "explode" the book into its
individual internal components.<br></li>
<li>Right click on any individual file and select "Open with..." to
edit it in your favorite text editor.<br></li>
<li>When you are done Tweaking: <b>close the file browser window
and the editor windows you used to make your tweaks</b>. Then click
the "Rebuild Book" button, to update the book in your calibre
library.</li>
</ol>'''))
self.help_label.setWordWrap(True)
self._fr = QFrame()
self._fr.setFrameShape(QFrame.VLine)
g.addWidget(self._fr)
g.addWidget(self.help_label)
self._b = b = QGridLayout()
left, top, right, bottom = b.getContentsMargins()
top += top
b.setContentsMargins(left, top, right, bottom)
l.addLayout(b, stretch=10)
self.explode_button = QPushButton(QIcon(I('wizard.png')), _('&Explode Book'))
self.preview_button = QPushButton(QIcon(I('view.png')), _('&Preview Book'))
self.cancel_button = QPushButton(QIcon(I('window-close.png')), _('&Cancel'))
self.rebuild_button = QPushButton(QIcon(I('exec.png')), _('&Rebuild Book'))
self.explode_button.setToolTip(
_('Explode the book to edit its components'))
self.preview_button.setToolTip(
_('Preview the result of your tweaks'))
self.cancel_button.setToolTip(
_('Abort without saving any changes'))
self.rebuild_button.setToolTip(
_('Save your changes and update the book in the calibre library'))
a = b.addWidget
a(self.explode_button, 0, 0, 1, 1)
a(self.preview_button, 0, 1, 1, 1)
a(self.cancel_button, 1, 0, 1, 1)
a(self.rebuild_button, 1, 1, 1, 1)
for x in ('explode', 'preview', 'cancel', 'rebuild'):
getattr(self, x+'_button').clicked.connect(getattr(self, x))
self.msg = QLabel('dummy', self)
self.msg.setVisible(False)
self.msg.setStyleSheet('''
QLabel {
text-align: center;
background-color: white;
color: black;
border-width: 1px;
border-style: solid;
border-radius: 20px;
font-size: x-large;
font-weight: bold;
}
''')
self.resize(self.sizeHint() + QSize(40, 10))
# }}}
def show_msg(self, msg):
self.msg.setText(msg)
self.msg.resize(self.size() - QSize(50, 25))
self.msg.move((self.width() - self.msg.width())//2,
(self.height() - self.msg.height())//2)
self.msg.setVisible(True)
def hide_msg(self):
self.msg.setVisible(False)
def explode(self):
self.show_msg(_('Exploding, please wait...'))
if len(self.fmt_choice_buttons) > 1:
gprefs.set('last_tweak_format', self.current_format.upper())
QTimer.singleShot(5, self.do_explode)
def ask_question(self, msg):
return question_dialog(self, _('Are you sure?'), msg)
def do_explode(self):
from calibre.ebooks.tweak import get_tools, Error, WorkerError
tdir = PersistentTemporaryDirectory('_tweak_explode')
self._cleanup_dirs.append(tdir)
det_msg = None
try:
src = self.db.format(self.book_id, self.current_format,
index_is_id=True, as_path=True)
self._cleanup_files.append(src)
exploder = get_tools(self.current_format)[0]
opf = exploder(src, tdir, question=self.ask_question)
except WorkerError as e:
det_msg = e.orig_tb
except Error as e:
return error_dialog(self, _('Failed to unpack'),
(_('Could not explode the %s file.')%self.current_format) + ' '
+ as_unicode(e), show=True)
except:
import traceback
det_msg = traceback.format_exc()
finally:
self.hide_msg()
if det_msg is not None:
return error_dialog(self, _('Failed to unpack'),
_('Could not explode the %s file. Click "Show Details" for '
'more information.')%self.current_format, det_msg=det_msg,
show=True)
if opf is None:
# The question was answered with No
return
self._exploded = tdir
self.explode_button.setEnabled(False)
self.preview_button.setEnabled(True)
self.rebuild_button.setEnabled(True)
open_local_file(tdir)
def rebuild_it(self):
from calibre.ebooks.tweak import get_tools, WorkerError
src_dir = self._exploded
det_msg = None
of = PersistentTemporaryFile('_tweak_rebuild.'+self.current_format.lower())
of.close()
of = of.name
self._cleanup_files.append(of)
try:
rebuilder = get_tools(self.current_format)[1]
rebuilder(src_dir, of)
except WorkerError as e:
det_msg = e.orig_tb
except:
import traceback
det_msg = traceback.format_exc()
finally:
self.hide_msg()
if det_msg is not None:
error_dialog(self, _('Failed to rebuild file'),
_('Failed to rebuild %s. For more information, click '
'"Show details".')%self.current_format,
det_msg=det_msg, show=True)
return None
return of
def preview(self):
self.show_msg(_('Rebuilding, please wait...'))
QTimer.singleShot(5, self.do_preview)
def do_preview(self):
rebuilt = self.rebuild_it()
if rebuilt is not None:
self.parent().iactions['View']._view_file(rebuilt)
def rebuild(self):
self.show_msg(_('Rebuilding, please wait...'))
QTimer.singleShot(5, self.do_rebuild)
def do_rebuild(self):
rebuilt = self.rebuild_it()
if rebuilt is not None:
fmt = os.path.splitext(rebuilt)[1][1:].upper()
with open(rebuilt, 'rb') as f:
self.db.add_format(self.book_id, fmt, f, index_is_id=True)
self.accept()
def cancel(self):
self.reject()
def cleanup(self):
if isosx and self._exploded:
try:
import appscript
self.finder = appscript.app('Finder')
self.finder.Finder_windows[os.path.basename(self._exploded)].close()
except:
pass
for f in self._cleanup_files:
try:
os.remove(f)
except:
pass
for d in self._cleanup_dirs:
try:
shutil.rmtree(d)
except:
pass
@property
def db(self):
return self.db_ref()
@property
def current_format(self):
for b in self.fmt_choice_buttons:
if b.isChecked():
return unicode(b.text())
class TweakEpubAction(InterfaceAction):
name = 'Tweak ePub'
action_spec = (_('Tweak Book'), 'tweak.png',
_('Make small changes to ePub, HTMLZ or AZW3 format books'),
_('T'))
action_spec = (_('Tweak Book'), 'tweak.png', _('Edit eBooks'), _('T'))
dont_add_to = frozenset(['context-menu-device'])
action_type = 'current'
@ -324,25 +90,46 @@ class TweakEpubAction(InterfaceAction):
def tweak_book(self):
row = self.gui.library_view.currentIndex()
if not row.isValid():
return error_dialog(self.gui, _('Cannot tweak Book'),
return error_dialog(self.gui, _('Cannot Tweak Book'),
_('No book selected'), show=True)
book_id = self.gui.library_view.model().id(row)
self.do_tweak(book_id)
def do_tweak(self, book_id):
from calibre.ebooks.oeb.polish.main import SUPPORTED
db = self.gui.library_view.model().db
fmts = db.formats(book_id, index_is_id=True) or ''
fmts = [x.lower().strip() for x in fmts.split(',')]
tweakable_fmts = set(fmts).intersection({'epub', 'htmlz', 'azw3',
'mobi', 'azw'})
fmts = [x.upper().strip() for x in fmts.split(',')]
tweakable_fmts = set(fmts).intersection(SUPPORTED)
if not tweakable_fmts:
return error_dialog(self.gui, _('Cannot Tweak Book'),
_('The book must be in ePub, HTMLZ or AZW3 formats to tweak.'
'\n\nFirst convert the book to one of these formats.'),
_('The book must be in the %s formats to tweak.'
'\n\nFirst convert the book to one of these formats.') % (_(' or '.join(SUPPORTED))),
show=True)
dlg = TweakBook(self.gui, book_id, tweakable_fmts, db)
dlg.exec_()
dlg.cleanup()
if len(tweakable_fmts) > 1:
from calibre.gui2.tweak_book import tprefs
if tprefs['choose_tweak_fmt']:
d = Choose(sorted(tweakable_fmts, key=tprefs.defaults['tweak_fmt_order'].index), self.gui)
if d.exec_() != d.Accepted:
return
tweakable_fmts = {d.fmt}
else:
fmts = [f for f in tprefs['tweak_fmt_order'] if f in tweakable_fmts]
if not fmts:
fmts = [f for f in tprefs.defaults['tweak_fmt_order'] if f in tweakable_fmts]
tweakable_fmts = {fmts[0]}
fmt = tuple(tweakable_fmts)[0]
path = db.new_api.format_abspath(book_id, fmt)
if path is None:
return error_dialog(self.gui, _('File missing'), _(
'The %s format is missing from the calibre library. You should run'
' library maintenance.') % fmt, show=True)
tweak = 'ebook-tweak'
self.gui.setCursor(Qt.BusyCursor)
try:
self.gui.job_manager.launch_gui_app(tweak, kwargs=dict(args=[tweak, path]))
time.sleep(2)
finally:
self.gui.unsetCursor()

View File

@ -0,0 +1,348 @@
#!/usr/bin/env python
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
__license__ = 'GPL v3'
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import os, weakref, shutil
from PyQt4.Qt import (QDialog, QVBoxLayout, QHBoxLayout, QRadioButton, QFrame,
QPushButton, QLabel, QGroupBox, QGridLayout, QIcon, QSize, QTimer)
from calibre import as_unicode
from calibre.constants import isosx
from calibre.gui2 import error_dialog, question_dialog, open_local_file, gprefs
from calibre.gui2.actions import InterfaceAction
from calibre.ptempfile import (PersistentTemporaryDirectory,
PersistentTemporaryFile)
from calibre.utils.config import prefs, tweaks
class UnpackBook(QDialog):
def __init__(self, parent, book_id, fmts, db):
QDialog.__init__(self, parent)
self.setWindowIcon(QIcon(I('unpack-book.png')))
self.book_id, self.fmts, self.db_ref = book_id, fmts, weakref.ref(db)
self._exploded = None
self._cleanup_dirs = []
self._cleanup_files = []
self.setup_ui()
self.setWindowTitle(_('Unpack Book') + ' - ' + db.title(book_id,
index_is_id=True))
button = self.fmt_choice_buttons[0]
button_map = {unicode(x.text()):x for x in self.fmt_choice_buttons}
of = prefs['output_format'].upper()
df = tweaks.get('default_tweak_format', None)
lf = gprefs.get('last_tweak_format', None)
if df and df.lower() == 'remember' and lf in button_map:
button = button_map[lf]
elif df and df.upper() in button_map:
button = button_map[df.upper()]
elif of in button_map:
button = button_map[of]
button.setChecked(True)
self.init_state()
for button in self.fmt_choice_buttons:
button.toggled.connect(self.init_state)
def init_state(self, *args):
self._exploded = None
self.preview_button.setEnabled(False)
self.rebuild_button.setEnabled(False)
self.explode_button.setEnabled(True)
def setup_ui(self): # {{{
self._g = g = QHBoxLayout(self)
self.setLayout(g)
self._l = l = QVBoxLayout()
g.addLayout(l)
fmts = sorted(x.upper() for x in self.fmts)
self.fmt_choice_box = QGroupBox(_('Choose the format to unpack:'), self)
self._fl = fl = QHBoxLayout()
self.fmt_choice_box.setLayout(self._fl)
self.fmt_choice_buttons = [QRadioButton(y, self) for y in fmts]
for x in self.fmt_choice_buttons:
fl.addWidget(x, stretch=10 if x is self.fmt_choice_buttons[-1] else
0)
l.addWidget(self.fmt_choice_box)
self.fmt_choice_box.setVisible(len(fmts) > 1)
self.help_label = QLabel(_('''\
<h2>About Unpack Book</h2>
<p>Unpack Book allows you to fine tune the appearance of an ebook by
making small changes to its internals. In order to use Unpack Book,
you need to know a little bit about HTML and CSS, technologies that
are used in ebooks. Follow the steps:</p>
<br>
<ol>
<li>Click "Explode Book": This will "explode" the book into its
individual internal components.<br></li>
<li>Right click on any individual file and select "Open with..." to
edit it in your favorite text editor.<br></li>
<li>When you are done: <b>close the file browser window
and the editor windows you used to make your tweaks</b>. Then click
the "Rebuild Book" button, to update the book in your calibre
library.</li>
</ol>'''))
self.help_label.setWordWrap(True)
self._fr = QFrame()
self._fr.setFrameShape(QFrame.VLine)
g.addWidget(self._fr)
g.addWidget(self.help_label)
self._b = b = QGridLayout()
left, top, right, bottom = b.getContentsMargins()
top += top
b.setContentsMargins(left, top, right, bottom)
l.addLayout(b, stretch=10)
self.explode_button = QPushButton(QIcon(I('wizard.png')), _('&Explode Book'))
self.preview_button = QPushButton(QIcon(I('view.png')), _('&Preview Book'))
self.cancel_button = QPushButton(QIcon(I('window-close.png')), _('&Cancel'))
self.rebuild_button = QPushButton(QIcon(I('exec.png')), _('&Rebuild Book'))
self.explode_button.setToolTip(
_('Explode the book to edit its components'))
self.preview_button.setToolTip(
_('Preview the result of your changes'))
self.cancel_button.setToolTip(
_('Abort without saving any changes'))
self.rebuild_button.setToolTip(
_('Save your changes and update the book in the calibre library'))
a = b.addWidget
a(self.explode_button, 0, 0, 1, 1)
a(self.preview_button, 0, 1, 1, 1)
a(self.cancel_button, 1, 0, 1, 1)
a(self.rebuild_button, 1, 1, 1, 1)
for x in ('explode', 'preview', 'cancel', 'rebuild'):
getattr(self, x+'_button').clicked.connect(getattr(self, x))
self.msg = QLabel('dummy', self)
self.msg.setVisible(False)
self.msg.setStyleSheet('''
QLabel {
text-align: center;
background-color: white;
color: black;
border-width: 1px;
border-style: solid;
border-radius: 20px;
font-size: x-large;
font-weight: bold;
}
''')
self.resize(self.sizeHint() + QSize(40, 10))
# }}}
def show_msg(self, msg):
self.msg.setText(msg)
self.msg.resize(self.size() - QSize(50, 25))
self.msg.move((self.width() - self.msg.width())//2,
(self.height() - self.msg.height())//2)
self.msg.setVisible(True)
def hide_msg(self):
self.msg.setVisible(False)
def explode(self):
self.show_msg(_('Exploding, please wait...'))
if len(self.fmt_choice_buttons) > 1:
gprefs.set('last_tweak_format', self.current_format.upper())
QTimer.singleShot(5, self.do_explode)
def ask_question(self, msg):
return question_dialog(self, _('Are you sure?'), msg)
def do_explode(self):
from calibre.ebooks.tweak import get_tools, Error, WorkerError
tdir = PersistentTemporaryDirectory('_tweak_explode')
self._cleanup_dirs.append(tdir)
det_msg = None
try:
src = self.db.format(self.book_id, self.current_format,
index_is_id=True, as_path=True)
self._cleanup_files.append(src)
exploder = get_tools(self.current_format)[0]
opf = exploder(src, tdir, question=self.ask_question)
except WorkerError as e:
det_msg = e.orig_tb
except Error as e:
return error_dialog(self, _('Failed to unpack'),
(_('Could not explode the %s file.')%self.current_format) + ' '
+ as_unicode(e), show=True)
except:
import traceback
det_msg = traceback.format_exc()
finally:
self.hide_msg()
if det_msg is not None:
return error_dialog(self, _('Failed to unpack'),
_('Could not explode the %s file. Click "Show Details" for '
'more information.')%self.current_format, det_msg=det_msg,
show=True)
if opf is None:
# The question was answered with No
return
self._exploded = tdir
self.explode_button.setEnabled(False)
self.preview_button.setEnabled(True)
self.rebuild_button.setEnabled(True)
open_local_file(tdir)
def rebuild_it(self):
from calibre.ebooks.tweak import get_tools, WorkerError
src_dir = self._exploded
det_msg = None
of = PersistentTemporaryFile('_tweak_rebuild.'+self.current_format.lower())
of.close()
of = of.name
self._cleanup_files.append(of)
try:
rebuilder = get_tools(self.current_format)[1]
rebuilder(src_dir, of)
except WorkerError as e:
det_msg = e.orig_tb
except:
import traceback
det_msg = traceback.format_exc()
finally:
self.hide_msg()
if det_msg is not None:
error_dialog(self, _('Failed to rebuild file'),
_('Failed to rebuild %s. For more information, click '
'"Show details".')%self.current_format,
det_msg=det_msg, show=True)
return None
return of
def preview(self):
self.show_msg(_('Rebuilding, please wait...'))
QTimer.singleShot(5, self.do_preview)
def do_preview(self):
rebuilt = self.rebuild_it()
if rebuilt is not None:
self.parent().iactions['View']._view_file(rebuilt)
def rebuild(self):
self.show_msg(_('Rebuilding, please wait...'))
QTimer.singleShot(5, self.do_rebuild)
def do_rebuild(self):
rebuilt = self.rebuild_it()
if rebuilt is not None:
fmt = os.path.splitext(rebuilt)[1][1:].upper()
with open(rebuilt, 'rb') as f:
self.db.add_format(self.book_id, fmt, f, index_is_id=True)
self.accept()
def cancel(self):
self.reject()
def cleanup(self):
if isosx and self._exploded:
try:
import appscript
self.finder = appscript.app('Finder')
self.finder.Finder_windows[os.path.basename(self._exploded)].close()
except:
pass
for f in self._cleanup_files:
try:
os.remove(f)
except:
pass
for d in self._cleanup_dirs:
try:
shutil.rmtree(d)
except:
pass
@property
def db(self):
return self.db_ref()
@property
def current_format(self):
for b in self.fmt_choice_buttons:
if b.isChecked():
return unicode(b.text())
class UnpackBookAction(InterfaceAction):
name = 'Unpack Book'
action_spec = (_('Unpack Book'), 'unpack-book.png',
_('Unpack books in the EPUB, AZW3, HTMLZ formats into their individual components'), None)
dont_add_to = frozenset(['context-menu-device'])
action_type = 'current'
accepts_drops = True
def accept_enter_event(self, event, mime_data):
if mime_data.hasFormat("application/calibre+from_library"):
return True
return False
def accept_drag_move_event(self, event, mime_data):
if mime_data.hasFormat("application/calibre+from_library"):
return True
return False
def drop_event(self, event, mime_data):
mime = 'application/calibre+from_library'
if mime_data.hasFormat(mime):
self.dropped_ids = tuple(map(int, str(mime_data.data(mime)).split()))
QTimer.singleShot(1, self.do_drop)
return True
return False
def do_drop(self):
book_ids = self.dropped_ids
del self.dropped_ids
if book_ids:
self.do_tweak(book_ids[0])
def genesis(self):
self.qaction.triggered.connect(self.tweak_book)
def tweak_book(self):
row = self.gui.library_view.currentIndex()
if not row.isValid():
return error_dialog(self.gui, _('Cannot unpack Book'),
_('No book selected'), show=True)
book_id = self.gui.library_view.model().id(row)
self.do_tweak(book_id)
def do_tweak(self, book_id):
db = self.gui.library_view.model().db
fmts = db.formats(book_id, index_is_id=True) or ''
fmts = [x.lower().strip() for x in fmts.split(',')]
tweakable_fmts = set(fmts).intersection({'epub', 'htmlz', 'azw3',
'mobi', 'azw'})
if not tweakable_fmts:
return error_dialog(self.gui, _('Cannot unpack Book'),
_('The book must be in ePub, HTMLZ or AZW3 formats to unpack.'
'\n\nFirst convert the book to one of these formats.'),
show=True)
dlg = UnpackBook(self.gui, book_id, tweakable_fmts, db)
dlg.exec_()
dlg.cleanup()

View File

@ -14,6 +14,8 @@ tprefs.defaults['editor_font_family'] = None
tprefs.defaults['editor_font_size'] = 12
tprefs.defaults['editor_line_wrap'] = True
tprefs.defaults['preview_refresh_time'] = 2
tprefs.defaults['choose_tweak_fmt'] = True
tprefs.defaults['tweak_fmt_order'] = ['EPUB', 'AZW3']
_current_container = None

View File

@ -25,6 +25,9 @@ PARALLEL_FUNCS = {
'ebook-viewer' :
('calibre.gui2.viewer.main', 'main', None),
'ebook-tweak' :
('calibre.gui2.tweak_book.main', 'main', None),
'render_pages' :
('calibre.ebooks.comic.input', 'render_pages', 'notification'),
@ -197,6 +200,5 @@ def main():
return 0
if __name__ == '__main__':
sys.exit(main())