E-book viewer: Allow viewing images in the book in a separate pop-up window by right clicking on the image. Useful if you want to keep some image, like a map to the side while reading the book.

This commit is contained in:
Kovid Goyal 2012-08-08 16:14:46 +05:30
parent f82db76113
commit 50089cfa3f
2 changed files with 121 additions and 0 deletions

View File

@ -21,6 +21,7 @@ from calibre.gui2.viewer.keys import SHORTCUTS
from calibre.gui2.viewer.javascript import JavaScriptLoader from calibre.gui2.viewer.javascript import JavaScriptLoader
from calibre.gui2.viewer.position import PagePosition from calibre.gui2.viewer.position import PagePosition
from calibre.gui2.viewer.config import config, ConfigDialog from calibre.gui2.viewer.config import config, ConfigDialog
from calibre.gui2.viewer.image_popup import ImagePopup
from calibre.ebooks.oeb.display.webview import load_html from calibre.ebooks.oeb.display.webview import load_html
from calibre.constants import isxp from calibre.constants import isxp
# }}} # }}}
@ -470,6 +471,9 @@ class DocumentView(QWebView): # {{{
self.dictionary_action.setShortcut(Qt.CTRL+Qt.Key_L) self.dictionary_action.setShortcut(Qt.CTRL+Qt.Key_L)
self.dictionary_action.triggered.connect(self.lookup) self.dictionary_action.triggered.connect(self.lookup)
self.addAction(self.dictionary_action) self.addAction(self.dictionary_action)
self.image_popup = ImagePopup(self)
self.view_image_action = QAction(_('View &image...'), self)
self.view_image_action.triggered.connect(self.image_popup)
self.search_action = QAction(QIcon(I('dictionary.png')), self.search_action = QAction(QIcon(I('dictionary.png')),
_('&Search for next occurrence'), self) _('&Search for next occurrence'), self)
self.search_action.setShortcut(Qt.CTRL+Qt.Key_S) self.search_action.setShortcut(Qt.CTRL+Qt.Key_S)
@ -554,6 +558,11 @@ class DocumentView(QWebView): # {{{
self.manager.selection_changed(unicode(self.document.selectedText())) self.manager.selection_changed(unicode(self.document.selectedText()))
def contextMenuEvent(self, ev): def contextMenuEvent(self, ev):
mf = self.document.mainFrame()
r = mf.hitTestContent(ev.pos())
img = r.pixmap()
self.image_popup.current_img = img
self.image_popup.current_url = r.imageUrl()
menu = self.document.createStandardContextMenu() menu = self.document.createStandardContextMenu()
for action in self.unimplemented_actions: for action in self.unimplemented_actions:
menu.removeAction(action) menu.removeAction(action)
@ -561,6 +570,8 @@ class DocumentView(QWebView): # {{{
if text: if text:
menu.insertAction(list(menu.actions())[0], self.dictionary_action) menu.insertAction(list(menu.actions())[0], self.dictionary_action)
menu.insertAction(list(menu.actions())[0], self.search_action) menu.insertAction(list(menu.actions())[0], self.search_action)
if not img.isNull():
menu.addAction(self.view_image_action)
menu.addSeparator() menu.addSeparator()
menu.addAction(self.goto_location_action) menu.addAction(self.goto_location_action)
if self.document.in_fullscreen_mode and self.manager is not None: if self.document.in_fullscreen_mode and self.manager is not None:

View File

@ -0,0 +1,110 @@
#!/usr/bin/env python
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:fdm=marker:ai
from __future__ import (unicode_literals, division, absolute_import,
print_function)
__license__ = 'GPL v3'
__copyright__ = '2012, Kovid Goyal <kovid at kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
from PyQt4.Qt import (QDialog, QPixmap, QUrl, QScrollArea, QLabel, QSizePolicy,
QDialogButtonBox, QVBoxLayout, QPalette, QApplication, QSize, QIcon, Qt)
from calibre.gui2 import choose_save_file
class ImageView(QDialog):
def __init__(self, parent, current_img, current_url):
QDialog.__init__(self)
dw = QApplication.instance().desktop()
self.avail_geom = dw.availableGeometry(parent)
self.current_img = current_img
self.current_url = current_url
self.factor = 1.0
self.label = l = QLabel()
l.setBackgroundRole(QPalette.Base);
l.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
l.setScaledContents(True)
self.scrollarea = sa = QScrollArea()
sa.setBackgroundRole(QPalette.Dark)
sa.setWidget(l)
self.bb = bb = QDialogButtonBox(QDialogButtonBox.Close)
bb.accepted.connect(self.accept)
bb.rejected.connect(self.reject)
self.zi_button = zi = bb.addButton(_('Zoom &in'), bb.ActionRole)
self.zo_button = zo = bb.addButton(_('Zoom &out'), bb.ActionRole)
self.save_button = so = bb.addButton(_('&Save as'), bb.ActionRole)
zi.setIcon(QIcon(I('plus.png')))
zo.setIcon(QIcon(I('minus.png')))
so.setIcon(QIcon(I('save.png')))
zi.clicked.connect(self.zoom_in)
zo.clicked.connect(self.zoom_out)
so.clicked.connect(self.save_image)
self.l = l = QVBoxLayout()
self.setLayout(l)
l.addWidget(sa)
l.addWidget(bb)
def zoom_in(self):
self.factor *= 1.25
self.adjust_image(1.25)
def zoom_out(self):
self.factor *= 0.8
self.adjust_image(0.8)
def save_image(self):
filters=[('Images', ['png', 'jpeg', 'jpg'])]
f = choose_save_file(self, 'viewer image view save dialog',
_('Choose a file to save to'), filters=filters,
all_files=False)
if f:
self.current_img.save(f)
def adjust_image(self, factor):
self.label.resize(self.factor * self.current_img.size())
self.zi_button.setEnabled(self.factor <= 3)
self.zo_button.setEnabled(self.factor >= 0.3333)
self.adjust_scrollbars(factor)
def adjust_scrollbars(self, factor):
for sb in (self.scrollarea.horizontalScrollBar(),
self.scrollarea.verticalScrollBar()):
sb.setValue(int(factor*sb.value()) + ((factor - 1) * sb.pageStep()/2))
def __call__(self):
geom = self.avail_geom
self.label.setPixmap(self.current_img)
self.label.adjustSize()
self.resize(QSize(int(geom.width()/2.5), geom.height()-50))
self.current_image_name = unicode(self.current_url.toString()).rpartition('/')[-1]
title = _('View Image: %s')%self.current_image_name
self.setWindowTitle(title)
self.show()
class ImagePopup(object):
def __init__(self, parent):
self.current_img = QPixmap()
self.current_url = QUrl()
self.parent = parent
self.dialogs = []
def __call__(self):
if self.current_img.isNull():
return
d = ImageView(self.parent, self.current_img, self.current_url)
self.dialogs.append(d)
d.finished.connect(self.cleanup, type=Qt.QueuedConnection)
d()
def cleanup(self):
for d in tuple(self.dialogs):
if not d.isVisible():
self.dialogs.remove(d)