mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Tweak Book: Allow tweaking of KF8 MOBI files
This commit is contained in:
parent
61aa60ab07
commit
bb014a56bb
@ -490,12 +490,6 @@ save_original_format = True
|
||||
# how many should be shown, here.
|
||||
gui_view_history_size = 15
|
||||
|
||||
#: When using the 'Tweak Book' action, which format to prefer
|
||||
# When tweaking a book that has multiple formats, calibre picks one
|
||||
# automatically. By default EPUB is preferred to HTMLZ. If you would like to
|
||||
# prefer HTMLZ to EPUB for tweaking, change this to 'htmlz'
|
||||
tweak_book_prefer = 'epub'
|
||||
|
||||
#: Change the font size of book details in the interface
|
||||
# Change the font size at which book details are rendered in the side panel and
|
||||
# comments are rendered in the metadata edit dialog. Set it to a positive or
|
||||
|
@ -52,7 +52,10 @@ def explode(path, dest, question=lambda x:True):
|
||||
kf8_type = header.kf8_type
|
||||
|
||||
if kf8_type is None:
|
||||
raise BadFormat('This MOBI file does not contain a KF8 format book')
|
||||
raise BadFormat(_('This MOBI file does not contain a KF8 format '
|
||||
'book. KF8 is the new format from Amazon. calibre can '
|
||||
'only tweak MOBI files that contain KF8 books. Older '
|
||||
'MOBI files without KF8 are not tweakable.'))
|
||||
|
||||
if kf8_type == 'joint':
|
||||
if not question(_('This MOBI file contains both KF8 and '
|
||||
|
@ -5,70 +5,307 @@ __license__ = 'GPL v3'
|
||||
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
import os
|
||||
import os, weakref, shutil
|
||||
|
||||
from calibre.gui2 import error_dialog
|
||||
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
|
||||
from calibre.gui2.actions import InterfaceAction
|
||||
from calibre.gui2.dialogs.tweak_epub import TweakEpub
|
||||
from calibre.utils.config import tweaks
|
||||
from calibre.ptempfile import (PersistentTemporaryDirectory,
|
||||
PersistentTemporaryFile)
|
||||
from calibre.utils.config import prefs
|
||||
|
||||
class TweakBook(QDialog):
|
||||
|
||||
def __init__(self, parent, book_id, fmts, db):
|
||||
QDialog.__init__(self, parent)
|
||||
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(_('Tweak Book') + ' - ' + db.title(book_id,
|
||||
index_is_id=True))
|
||||
|
||||
button = self.fmt_choice_buttons[0]
|
||||
of = prefs['output_format'].upper()
|
||||
for x in self.fmt_choice_buttons:
|
||||
if unicode(x.text()) == of:
|
||||
button = x
|
||||
break
|
||||
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 tweak:'), self)
|
||||
self._fl = fl = QHBoxLayout()
|
||||
self.fmt_choice_box.setLayout(self._fl)
|
||||
self.fmt_choice_buttons = [QRadioButton(x, self) for x 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 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...'))
|
||||
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'), 'trim.png',
|
||||
_('Make small changes to ePub or HTMLZ format books'),
|
||||
_('Make small changes to ePub, HTMLZ or AZW3 format books'),
|
||||
_('T'))
|
||||
dont_add_to = frozenset(['context-menu-device'])
|
||||
action_type = 'current'
|
||||
|
||||
def genesis(self):
|
||||
self.qaction.triggered.connect(self.edit_epub_in_situ)
|
||||
self.qaction.triggered.connect(self.tweak_book)
|
||||
|
||||
def edit_epub_in_situ(self, *args):
|
||||
def tweak_book(self):
|
||||
row = self.gui.library_view.currentIndex()
|
||||
if not row.isValid():
|
||||
return error_dialog(self.gui, _('Cannot tweak Book'),
|
||||
_('No book selected'), show=True)
|
||||
|
||||
book_id = self.gui.library_view.model().id(row)
|
||||
|
||||
# Confirm 'EPUB' in formats
|
||||
try:
|
||||
path_to_epub = self.gui.library_view.model().db.format(
|
||||
book_id, 'EPUB', index_is_id=True, as_path=True)
|
||||
except:
|
||||
path_to_epub = None
|
||||
|
||||
# Confirm 'HTMLZ' in formats
|
||||
try:
|
||||
path_to_htmlz = self.gui.library_view.model().db.format(
|
||||
book_id, 'HTMLZ', index_is_id=True, as_path=True)
|
||||
except:
|
||||
path_to_htmlz = None
|
||||
|
||||
if not path_to_epub and not path_to_htmlz:
|
||||
return error_dialog(self.gui, _('Cannot tweak Book'),
|
||||
_('The book must be in ePub or HTMLZ format to tweak.'
|
||||
'\n\nFirst convert the book to ePub or HTMLZ.'),
|
||||
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 Tweak Book'),
|
||||
_('The book must be in ePub, HTMLZ or AZW3 formats to tweak.'
|
||||
'\n\nFirst convert the book to one of these formats.'),
|
||||
show=True)
|
||||
|
||||
# Launch modal dialog waiting for user to tweak or cancel
|
||||
if tweaks['tweak_book_prefer'] == 'htmlz':
|
||||
path_to_book = path_to_htmlz or path_to_epub
|
||||
else:
|
||||
path_to_book = path_to_epub or path_to_htmlz
|
||||
|
||||
dlg = TweakEpub(self.gui, path_to_book)
|
||||
if dlg.exec_() == dlg.Accepted:
|
||||
self.update_db(book_id, dlg._output)
|
||||
dlg = TweakBook(self.gui, book_id, tweakable_fmts, db)
|
||||
dlg.exec_()
|
||||
dlg.cleanup()
|
||||
os.remove(path_to_book)
|
||||
|
||||
def update_db(self, book_id, rebuilt):
|
||||
'''
|
||||
Update the calibre db with the tweaked epub
|
||||
'''
|
||||
fmt = os.path.splitext(rebuilt)[1][1:].upper()
|
||||
self.gui.library_view.model().db.add_format(book_id, fmt,
|
||||
open(rebuilt, 'rb'), index_is_id=True)
|
||||
|
||||
|
@ -1,130 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
from __future__ import with_statement
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
import os, shutil
|
||||
from itertools import repeat, izip
|
||||
from calibre.utils.zipfile import ZipFile, ZIP_DEFLATED, ZIP_STORED
|
||||
|
||||
from PyQt4.Qt import QDialog
|
||||
|
||||
from calibre.constants import isosx
|
||||
from calibre.gui2 import open_local_file, error_dialog
|
||||
from calibre.gui2.dialogs.tweak_epub_ui import Ui_Dialog
|
||||
from calibre.libunzip import extract as zipextract
|
||||
from calibre.ptempfile import (PersistentTemporaryDirectory,
|
||||
PersistentTemporaryFile)
|
||||
|
||||
class TweakEpub(QDialog, Ui_Dialog):
|
||||
'''
|
||||
Display controls for tweaking ePubs
|
||||
|
||||
'''
|
||||
|
||||
def __init__(self, parent, epub):
|
||||
QDialog.__init__(self, parent)
|
||||
|
||||
self._epub = epub
|
||||
self._exploded = None
|
||||
self._output = None
|
||||
self.ishtmlz = epub.lower().endswith('.htmlz')
|
||||
self.rebuilt_name = 'rebuilt.' + ('htmlz' if self.ishtmlz else 'epub')
|
||||
|
||||
# Run the dialog setup generated from tweak_epub.ui
|
||||
self.setupUi(self)
|
||||
for x, props in [(self, ['windowTitle']), (self.label, ['text'])]+\
|
||||
list(izip([self.cancel_button, self.explode_button,
|
||||
self.rebuild_button, self.preview_button],
|
||||
repeat(['text', 'statusTip', 'toolTip']))):
|
||||
for prop in props:
|
||||
val = unicode(getattr(x, prop)())
|
||||
val = val.format('HTMLZ' if self.ishtmlz else 'ePub')
|
||||
prop = 'set' + prop[0].upper() + prop[1:]
|
||||
getattr(x, prop)(val)
|
||||
|
||||
self.cancel_button.clicked.connect(self.reject)
|
||||
self.explode_button.clicked.connect(self.explode)
|
||||
self.rebuild_button.clicked.connect(self.rebuild)
|
||||
self.preview_button.clicked.connect(self.preview)
|
||||
|
||||
# Position update dialog overlaying top left of app window
|
||||
parent_loc = parent.pos()
|
||||
self.move(parent_loc.x(),parent_loc.y())
|
||||
|
||||
self.gui = parent
|
||||
self._preview_files = []
|
||||
|
||||
def cleanup(self):
|
||||
if isosx:
|
||||
try:
|
||||
import appscript
|
||||
self.finder = appscript.app('Finder')
|
||||
self.finder.Finder_windows[os.path.basename(self._exploded)].close()
|
||||
except:
|
||||
# appscript fails to load on 10.4
|
||||
pass
|
||||
|
||||
# Delete directory containing exploded ePub
|
||||
if self._exploded is not None:
|
||||
shutil.rmtree(self._exploded, ignore_errors=True)
|
||||
for x in self._preview_files:
|
||||
try:
|
||||
os.remove(x)
|
||||
except:
|
||||
pass
|
||||
|
||||
def display_exploded(self):
|
||||
'''
|
||||
Generic subprocess launch of native file browser
|
||||
User can use right-click to 'Open with ...'
|
||||
'''
|
||||
open_local_file(self._exploded)
|
||||
|
||||
def explode(self, *args):
|
||||
if self._exploded is None:
|
||||
self._exploded = PersistentTemporaryDirectory("_exploded", prefix='')
|
||||
zipextract(self._epub, self._exploded)
|
||||
self.display_exploded()
|
||||
self.rebuild_button.setEnabled(True)
|
||||
self.explode_button.setEnabled(False)
|
||||
|
||||
def do_rebuild(self, src):
|
||||
with ZipFile(src, 'w', compression=ZIP_DEFLATED) as zf:
|
||||
# Write mimetype
|
||||
mt = os.path.join(self._exploded, 'mimetype')
|
||||
if os.path.exists(mt):
|
||||
zf.write(mt, 'mimetype', compress_type=ZIP_STORED)
|
||||
# Write everything else
|
||||
exclude_files = ['.DS_Store','mimetype','iTunesMetadata.plist',self.rebuilt_name]
|
||||
for root, dirs, files in os.walk(self._exploded):
|
||||
for fn in files:
|
||||
if fn in exclude_files:
|
||||
continue
|
||||
absfn = os.path.join(root, fn)
|
||||
zfn = os.path.relpath(absfn,
|
||||
self._exploded).replace(os.sep, '/')
|
||||
zf.write(absfn, zfn)
|
||||
|
||||
def preview(self):
|
||||
if not self._exploded:
|
||||
msg = _('You must first explode the %s before previewing.')
|
||||
msg = msg%('HTMLZ' if self.ishtmlz else 'ePub')
|
||||
return error_dialog(self, _('Cannot preview'), msg, show=True)
|
||||
|
||||
tf = PersistentTemporaryFile('.htmlz' if self.ishtmlz else '.epub')
|
||||
tf.close()
|
||||
self._preview_files.append(tf.name)
|
||||
|
||||
self.do_rebuild(tf.name)
|
||||
|
||||
self.gui.iactions['View']._view_file(tf.name)
|
||||
|
||||
def rebuild(self, *args):
|
||||
self._output = os.path.join(self._exploded, self.rebuilt_name)
|
||||
self.do_rebuild(self._output)
|
||||
return QDialog.accept(self)
|
||||
|
@ -1,107 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Dialog</class>
|
||||
<widget class="QDialog" name="Dialog">
|
||||
<property name="windowModality">
|
||||
<enum>Qt::NonModal</enum>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>382</width>
|
||||
<height>265</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Tweak {0}</string>
|
||||
</property>
|
||||
<property name="sizeGripEnabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="modal">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string><p>Explode the {0} to display contents in a file browser window. To tweak individual files, right-click, then 'Open with...' your editor of choice. When tweaks are complete, close the file browser window <b>and the editor windows you used to edit files in the ePub</b>.</p><p>Rebuild the ePub, updating your calibre library.</p></string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QPushButton" name="explode_button">
|
||||
<property name="toolTip">
|
||||
<string>Display contents of exploded {0}</string>
|
||||
</property>
|
||||
<property name="statusTip">
|
||||
<string>Display contents of exploded {0}</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Explode {0}</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../../resources/images.qrc">
|
||||
<normaloff>:/images/wizard.png</normaloff>:/images/wizard.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QPushButton" name="cancel_button">
|
||||
<property name="toolTip">
|
||||
<string>Discard changes</string>
|
||||
</property>
|
||||
<property name="statusTip">
|
||||
<string>Discard changes</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Cancel</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../../resources/images.qrc">
|
||||
<normaloff>:/images/window-close.png</normaloff>:/images/window-close.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QPushButton" name="rebuild_button">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Rebuild {0} from exploded contents</string>
|
||||
</property>
|
||||
<property name="statusTip">
|
||||
<string>Rebuild {0} from exploded contents</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Rebuild {0}</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../../resources/images.qrc">
|
||||
<normaloff>:/images/exec.png</normaloff>:/images/exec.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QPushButton" name="preview_button">
|
||||
<property name="text">
|
||||
<string>&Preview {0}</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../../resources/images.qrc">
|
||||
<normaloff>:/images/view.png</normaloff>:/images/view.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="../../../../resources/images.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
Loading…
x
Reference in New Issue
Block a user