From 50089cfa3f5ed6a7559d402c0098606307c40833 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 8 Aug 2012 16:14:46 +0530 Subject: [PATCH] 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. --- src/calibre/gui2/viewer/documentview.py | 11 +++ src/calibre/gui2/viewer/image_popup.py | 110 ++++++++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 src/calibre/gui2/viewer/image_popup.py diff --git a/src/calibre/gui2/viewer/documentview.py b/src/calibre/gui2/viewer/documentview.py index 0bae46d717..eb5680b7b9 100644 --- a/src/calibre/gui2/viewer/documentview.py +++ b/src/calibre/gui2/viewer/documentview.py @@ -21,6 +21,7 @@ from calibre.gui2.viewer.keys import SHORTCUTS from calibre.gui2.viewer.javascript import JavaScriptLoader from calibre.gui2.viewer.position import PagePosition 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.constants import isxp # }}} @@ -470,6 +471,9 @@ class DocumentView(QWebView): # {{{ self.dictionary_action.setShortcut(Qt.CTRL+Qt.Key_L) self.dictionary_action.triggered.connect(self.lookup) 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')), _('&Search for next occurrence'), self) self.search_action.setShortcut(Qt.CTRL+Qt.Key_S) @@ -554,6 +558,11 @@ class DocumentView(QWebView): # {{{ self.manager.selection_changed(unicode(self.document.selectedText())) 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() for action in self.unimplemented_actions: menu.removeAction(action) @@ -561,6 +570,8 @@ class DocumentView(QWebView): # {{{ if text: menu.insertAction(list(menu.actions())[0], self.dictionary_action) menu.insertAction(list(menu.actions())[0], self.search_action) + if not img.isNull(): + menu.addAction(self.view_image_action) menu.addSeparator() menu.addAction(self.goto_location_action) if self.document.in_fullscreen_mode and self.manager is not None: diff --git a/src/calibre/gui2/viewer/image_popup.py b/src/calibre/gui2/viewer/image_popup.py new file mode 100644 index 0000000000..1317650ad8 --- /dev/null +++ b/src/calibre/gui2/viewer/image_popup.py @@ -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 ' +__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) +