UI for new Add Cover tool

This commit is contained in:
Kovid Goyal 2014-06-25 19:26:28 +05:30
parent 0841399d0b
commit 00a368909a
5 changed files with 178 additions and 29 deletions

View File

@ -58,6 +58,7 @@ d['editor_html_toolbar'] = ['fix-html-current', 'pretty-current', 'insert-image'
d['editor_format_toolbar'] = [('format-text-' + x) for x in (
'bold', 'italic', 'underline', 'strikethrough', 'subscript', 'superscript', 'color', 'background-color')]
d['spell_check_case_sensitive_search'] = False
d['add_cover_preserve_aspect_ratio'] = False
del d
ucase_map = {l:string.ascii_uppercase[i] for i, l in enumerate(string.ascii_lowercase)}

View File

@ -42,7 +42,7 @@ from calibre.gui2.tweak_book.search import validate_search_request, run_search
from calibre.gui2.tweak_book.spell import find_next as find_next_word, find_next_error
from calibre.gui2.tweak_book.widgets import (
RationalizeFolders, MultiSplit, ImportForeign, QuickOpen, InsertLink,
InsertSemantics, BusyCursor, InsertTag, FilterCSS)
InsertSemantics, BusyCursor, InsertTag, FilterCSS, AddCover)
_diff_dialogs = []
@ -353,27 +353,29 @@ class Boss(QObject):
d = NewFileDialog(self.gui)
if d.exec_() != d.Accepted:
return
self.add_savepoint(_('Before: Add file %s') % self.gui.elided_text(d.file_name))
self.do_add_file(d.file_name, d.file_data, using_template=d.using_template, edit_file=True)
def do_add_file(self, file_name, data, using_template=False, edit_file=False):
self.add_savepoint(_('Before: Add file %s') % self.gui.elided_text(file_name))
c = current_container()
data = d.file_data
if d.using_template:
if using_template:
data = data.replace(b'%CURSOR%', b'')
try:
c.add_file(d.file_name, data)
c.add_file(file_name, data)
except:
self.rewind_savepoint()
raise
self.gui.file_list.build(c)
self.gui.file_list.select_name(d.file_name)
self.gui.file_list.select_name(file_name)
if c.opf_name in editors:
editors[c.opf_name].replace_data(c.raw_data(c.opf_name))
mt = c.mime_map[d.file_name]
syntax = syntax_from_mime(d.file_name, mt)
if syntax:
if d.using_template:
self.edit_file(d.file_name, syntax, use_template=d.file_data.decode('utf-8'))
mt = c.mime_map[file_name]
syntax = syntax_from_mime(file_name, mt)
if syntax and edit_file:
if using_template:
self.edit_file(file_name, syntax, use_template=data.decode('utf-8'))
else:
self.edit_file(d.file_name, syntax)
self.edit_file(file_name, syntax)
self.set_modified()
def add_files(self):
@ -406,6 +408,15 @@ class Boss(QObject):
editors[c.opf_name].replace_data(c.raw_data(c.opf_name))
self.set_modified()
def add_cover(self):
d = AddCover(current_container(), self.gui)
d.import_requested.connect(self.do_add_file)
try:
if d.exec_() == d.Accepted:
pass
finally:
d.import_requested.disconnect()
def edit_toc(self):
if current_container() is None:
return error_dialog(self.gui, _('No book opened'), _(

View File

@ -685,7 +685,7 @@ class FileList(QTreeWidget):
class NewFileDialog(QDialog): # {{{
def __init__(self, initial_choice='html', parent=None):
def __init__(self, parent=None):
QDialog.__init__(self, parent)
self.l = l = QVBoxLayout()
self.setLayout(l)
@ -715,6 +715,7 @@ class NewFileDialog(QDialog): # {{{
self.file_data = b''
self.using_template = False
self.setMinimumWidth(350)
def show_error(self, msg):
self.err_label.setText('<p style="color:red">' + msg)
@ -723,15 +724,19 @@ class NewFileDialog(QDialog): # {{{
def import_file(self):
path = choose_files(self, 'tweak-book-new-resource-file', _('Choose file'), select_only_single_file=True)
if path:
path = path[0]
with open(path, 'rb') as f:
self.file_data = f.read()
name = os.path.basename(path)
fmap = get_recommended_folders(current_container(), (name,))
if fmap[name]:
name = '/'.join((fmap[name], name))
self.name.setText(name)
self.la.setText(_('Choose a name for the imported file'))
self.do_import_file(path[0])
def do_import_file(self, path, hide_button=False):
with open(path, 'rb') as f:
self.file_data = f.read()
name = os.path.basename(path)
fmap = get_recommended_folders(current_container(), (name,))
if fmap[name]:
name = '/'.join((fmap[name], name))
self.name.setText(name)
self.la.setText(_('Choose a name for the imported file'))
if hide_button:
self.imp_button.setVisible(False)
@property
def name_is_ok(self):

View File

@ -357,6 +357,7 @@ class Main(MainWindow):
self.action_filter_css = treg('filter.png', _('&Filter style information'), self.boss.filter_css, 'filter-css', (),
_('Filter style information'))
self.action_manage_fonts = treg('font.png', _('Manage &fonts'), self.boss.manage_fonts, 'manage-fonts', (), _('Manage fonts in the book'))
self.action_add_cover = treg('default_cover.png', _('Add &cover'), self.boss.add_cover, 'add-cover', (), _('Add a cover to the book'))
def ereg(icon, text, target, sid, keys, description):
return reg(icon, text, partial(self.boss.editor_action, target), sid, keys, description)
@ -496,6 +497,7 @@ class Main(MainWindow):
e.addAction(self.action_fix_html_all)
e.addAction(self.action_pretty_all)
e.addAction(self.action_rationalize_folders)
e.addAction(self.action_add_cover)
e.addAction(self.action_set_semantics)
e.addAction(self.action_filter_css)
e.addAction(self.action_spell_check_book)

View File

@ -6,7 +6,7 @@ from __future__ import (unicode_literals, division, absolute_import,
__license__ = 'GPL v3'
__copyright__ = '2014, Kovid Goyal <kovid at kovidgoyal.net>'
import os
import os, textwrap
from itertools import izip
from collections import OrderedDict
@ -15,12 +15,13 @@ from PyQt4.Qt import (
QFormLayout, QHBoxLayout, QToolButton, QIcon, QApplication, Qt, QWidget,
QPoint, QSizePolicy, QPainter, QStaticText, pyqtSignal, QTextOption,
QAbstractListModel, QModelIndex, QVariant, QStyledItemDelegate, QStyle,
QListView, QTextDocument, QSize, QComboBox, QFrame, QCursor, QCheckBox)
QListView, QTextDocument, QSize, QComboBox, QFrame, QCursor, QCheckBox,
QSplitter, QPixmap, QRect)
from calibre import prepare_string_for_xml
from calibre.ebooks.oeb.polish.utils import lead_text
from calibre.gui2 import error_dialog, choose_files, choose_save_file, NONE, info_dialog
from calibre.gui2.tweak_book import tprefs
from calibre import prepare_string_for_xml, human_readable
from calibre.ebooks.oeb.polish.utils import lead_text, guess_type
from calibre.gui2 import error_dialog, choose_files, choose_save_file, NONE, info_dialog, choose_images
from calibre.gui2.tweak_book import tprefs, current_container
from calibre.utils.icu import primary_sort_key, sort_key, primary_contains
from calibre.utils.matcher import get_char, Matcher
from calibre.gui2.complete2 import EditWithComplete
@ -569,6 +570,12 @@ class NamesModel(QAbstractListModel):
if text == name:
return i
def name_for_index(self, index):
try:
return self.items[index.row()][0]
except IndexError:
pass
def create_filterable_names_list(names, filter_text=None, parent=None, model=NamesModel):
nl = QListView(parent)
nl.m = m = model(names, parent=nl)
@ -992,7 +999,130 @@ class FilterCSS(Dialog): # {{{
# }}}
# Add Cover {{{
class CoverView(QWidget):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
self.current_pixmap_size = QSize(0, 0)
self.pixmap = QPixmap()
self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
def set_pixmap(self, data):
self.pixmap.loadFromData(data)
self.current_pixmap_size = self.pixmap.size()
self.update()
def paintEvent(self, event):
if self.pixmap.isNull():
return
canvas_size = self.rect()
width = self.current_pixmap_size.width()
extrax = canvas_size.width() - width
if extrax < 0:
extrax = 0
x = int(extrax/2.)
height = self.current_pixmap_size.height()
extray = canvas_size.height() - height
if extray < 0:
extray = 0
y = int(extray/2.)
target = QRect(x, y, min(canvas_size.width(), width), min(canvas_size.height(), height))
p = QPainter(self)
p.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform)
p.drawPixmap(target, self.pixmap.scaled(target.size(),
Qt.KeepAspectRatio, Qt.SmoothTransformation))
p.end()
def sizeHint(self):
return QSize(300, 400)
class AddCover(Dialog):
import_requested = pyqtSignal(object, object)
def __init__(self, container, parent=None):
self.container = container
Dialog.__init__(self, _('Add a cover'), 'add-cover-wizard', parent)
@property
def image_names(self):
img_types = {guess_type('a.'+x) for x in ('png', 'jpeg', 'gif')}
for name, mt in self.container.mime_map.iteritems():
if mt.lower() in img_types:
yield name
def setup_ui(self):
self.l = l = QVBoxLayout(self)
self.setLayout(l)
self.names, self.names_filter = create_filterable_names_list(sorted(self.image_names, key=sort_key), filter_text=_('Filter the list of images'))
self.cover_view = CoverView(self)
l.addWidget(self.names_filter)
self.splitter = s = QSplitter(self)
l.addWidget(s)
s.addWidget(self.names)
s.addWidget(self.cover_view)
self.h = h = QHBoxLayout()
self.preserve = p = QCheckBox(_('Preserve aspect ratio'))
p.setToolTip(textwrap.fill(_('If enabled the cover image you select will be embedded'
' into the book in such a way that when viewed, its aspect'
' ratio (ratio of width to height) will be preserved.'
' This will mean blank spaces around the image if the screen'
' the book is being viewed on has an aspect ratio different'
' to the image.')))
p.setChecked(tprefs['add_cover_preserve_aspect_ratio'])
p.setVisible(self.container.book_type != 'azw3')
p.stateChanged.connect(lambda s:tprefs.set('add_cover_preserve_aspect_ratio', s == Qt.Checked))
self.info_label = il = QLabel('\xa0')
h.addWidget(p), h.addStretch(1), h.addWidget(il)
l.addLayout(h)
l.addWidget(self.bb)
b = self.bb.addButton(_('Import &image'), self.bb.ActionRole)
b.clicked.connect(self.import_image)
b.setIcon(QIcon(I('document_open.png')))
self.names.setFocus(Qt.OtherFocusReason)
self.names.selectionModel().currentChanged.connect(self.current_image_changed)
def current_image_changed(self):
self.info_label.setText('')
name = self.names.model().name_for_index(self.names.currentIndex())
if name is not None:
data = self.container.raw_data(name, decode=False)
self.cover_view.set_pixmap(data)
self.info_label.setText('{0}x{1}px | {2}'.format(
self.cover_view.pixmap.width(), self.cover_view.pixmap.height(), human_readable(len(data))))
def import_image(self):
ans = choose_images(self, 'add-cover-choose-image', _('Choose a cover image'), formats=(
'jpg', 'jpeg', 'png', 'gif'))
if ans:
from calibre.gui2.tweak_book.file_list import NewFileDialog
d = NewFileDialog(self)
d.do_import_file(ans[0], hide_button=True)
if d.exec_() == d.Accepted:
self.import_requested.emit(d.file_name, d.file_data)
self.container = current_container()
self.names_filter.clear()
self.names.model().set_names(sorted(self.image_names, key=sort_key))
i = self.names.model().find_name(d.file_name)
self.names.setCurrentIndex(self.names.model().index(i))
self.current_image_changed()
@classmethod
def test(cls):
import sys
from calibre.ebooks.oeb.polish.container import get_container
c = get_container(sys.argv[-1], tweak_mode=True)
d = cls(c)
if d.exec_() == d.Accepted:
pass
# }}}
if __name__ == '__main__':
app = QApplication([])
FilterCSS.test()
AddCover.test()