--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/WebBrowser/SiteInfo/SiteInfoDialog.py Sun Feb 28 15:23:42 2016 +0100 @@ -0,0 +1,286 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2011 - 2016 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing a dialog to show some information about a site. +""" + +from __future__ import unicode_literals + +from PyQt5.QtCore import pyqtSlot, QUrl, Qt +from PyQt5.QtGui import QPixmap, QImage +from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply +from PyQt5.QtWidgets import QDialog, QTreeWidgetItem, QGraphicsScene, QMenu, \ + QApplication, QGraphicsPixmapItem + +from E5Gui import E5MessageBox, E5FileDialog + +from .Ui_SiteInfoDialog import Ui_SiteInfoDialog + +from ..Tools import Scripts, WebBrowserTools + +import UI.PixmapCache + + +class SiteInfoDialog(QDialog, Ui_SiteInfoDialog): + """ + Class implementing a dialog to show some information about a site. + """ + okStyle = "QLabel { color : white; background-color : green; }" + nokStyle = "QLabel { color : white; background-color : red; }" + + def __init__(self, browser, parent=None): + """ + Constructor + + @param browser reference to the browser window (HelpBrowser) + @param parent reference to the parent widget (QWidget) + """ + super(SiteInfoDialog, self).__init__(parent) + self.setupUi(self) + self.setWindowFlags(Qt.Window) + + # put icons + self.tabWidget.setTabIcon( + 0, UI.PixmapCache.getIcon("siteinfo-general.png")) + self.tabWidget.setTabIcon( + 1, UI.PixmapCache.getIcon("siteinfo-media.png")) + + self.__imageReply = None + + self.__baseUrl = browser.url() + title = browser.title() + + # populate General tab + self.heading.setText("<b>{0}</b>".format(title)) + self.siteAddressLabel.setText(self.__baseUrl.toString()) + if self.__baseUrl.scheme() in ["https"]: + self.securityLabel.setStyleSheet(SiteInfoDialog.okStyle) + self.securityLabel.setText('<b>Connection is encrypted.</b>') + else: + self.securityLabel.setStyleSheet(SiteInfoDialog.nokStyle) + self.securityLabel.setText('<b>Connection is not encrypted.</b>') + browser.page().runJavaScript( + "document.charset", + lambda res: self.encodingLabel.setText(res)) + + # populate Meta tags + browser.page().runJavaScript(Scripts.getAllMetaAttributes(), + self.__processMetaAttributes) + + # populate Media tab + browser.page().runJavaScript(Scripts.getAllImages(), + self.__processImageTags) + + def __processImageTags(self, res): + """ + Private method to process the image tags. + + @param res result of the JavaScript script + @type list of dict + """ + for img in res: + src = img["src"] + alt = img["alt"] + if not alt: + if src.find("/") == -1: + alt = src + else: + pos = src.rfind("/") + alt = src[pos + 1:] + + if not src or not alt: + continue + + QTreeWidgetItem(self.imagesTree, [alt, src]) + + for col in range(self.imagesTree.columnCount()): + self.imagesTree.resizeColumnToContents(col) + if self.imagesTree.columnWidth(0) > 300: + self.imagesTree.setColumnWidth(0, 300) + self.imagesTree.setCurrentItem(self.imagesTree.topLevelItem(0)) + self.imagesTree.setContextMenuPolicy(Qt.CustomContextMenu) + self.imagesTree.customContextMenuRequested.connect( + self.__imagesTreeContextMenuRequested) + + def __processMetaAttributes(self, res): + """ + Private method to process the meta attributes. + + @param res result of the JavaScript script + @type list of dict + """ + for meta in res: + content = meta["content"] + name = meta["name"] + if not name: + name = meta["httpequiv"] + + if not name or not content: + continue + + if meta["charset"]: + self.encodingLabel.setText(meta["charset"]) + if "charset=" in content: + self.encodingLabel.setText( + content[content.index("charset=") + 8:]) + + QTreeWidgetItem(self.tagsTree, [name, content]) + for col in range(self.tagsTree.columnCount()): + self.tagsTree.resizeColumnToContents(col) + + @pyqtSlot(QTreeWidgetItem, QTreeWidgetItem) + def on_imagesTree_currentItemChanged(self, current, previous): + """ + Private slot to show a preview of the selected image. + + @param current current image entry (QTreeWidgetItem) + @param previous old current entry (QTreeWidgetItem) + """ + if current is None: + return + + imageUrl = QUrl(current.text(1)) + if imageUrl.isRelative(): + imageUrl = self.__baseUrl.resolved(imageUrl) + + pixmap = QPixmap() + loading = False + + if imageUrl.scheme() == "data": + encodedUrl = current.text(1).encode("utf-8") + imageData = encodedUrl[encodedUrl.find(",") + 1:] + pixmap = WebBrowserTools.pixmapFromByteArray(imageData) + elif imageUrl.scheme() == "file": + pixmap = QPixmap(imageUrl.toLocalFile()) + elif imageUrl.scheme() == "qrc": + pixmap = QPixmap(imageUrl.toString()[3:]) + else: + if self.__imageReply is not None: + self.__imageReply.deleteLater() + self.__imageReply = None + + from WebBrowser.WebBrowserWindow import WebBrowserWindow + self.__imageReply = WebBrowserWindow.networkManager().get( + QNetworkRequest(imageUrl)) + self.__imageReply.finished.connect(self.__imageReplyFinished) + loading = True + self.__showLoadingText() + + if not loading: + self.__showPixmap(pixmap) + + @pyqtSlot() + def __imageReplyFinished(self): + """ + Private slot handling the loading of an image. + """ + if self.__imageReply.error() != QNetworkReply.NoError: + return + + data = self.__imageReply.readAll() + self.__showPixmap(QPixmap.fromImage(QImage.fromData(data))) + + def __showPixmap(self, pixmap): + """ + Private method to show a pixmap in the preview pane. + + @param pixmap pixmap to be shown + @type QPixmap + """ + scene = QGraphicsScene(self.imagePreview) + if pixmap.isNull(): + scene.addText(self.tr("Preview not available.")) + else: + scene.addPixmap(pixmap) + self.imagePreview.setScene(scene) + + def __showLoadingText(self): + """ + Private method to show some text while loading an image. + """ + scene = QGraphicsScene(self.imagePreview) + scene.addText(self.tr("Loading...")) + self.imagePreview.setScene(scene) + + def __imagesTreeContextMenuRequested(self, pos): + """ + Private slot to show a context menu for the images list. + + @param pos position for the menu (QPoint) + """ + itm = self.imagesTree.itemAt(pos) + if itm is None: + return + + menu = QMenu() + menu.addAction( + self.tr("Copy Image Location to Clipboard"), + self.__copyAction).setData(itm.text(1)) + menu.addAction( + self.tr("Copy Image Name to Clipboard"), + self.__copyAction).setData(itm.text(0)) + menu.addSeparator() + menu.addAction( + self.tr("Save Image"), + self.__saveImage).setData(self.imagesTree.indexOfTopLevelItem(itm)) + menu.exec_(self.imagesTree.viewport().mapToGlobal(pos)) + + def __copyAction(self): + """ + Private slot to copy the image URL or the image name to the clipboard. + """ + act = self.sender() + QApplication.clipboard().setText(act.data()) + + def __saveImage(self): + """ + Private slot to save the selected image to disk. + """ + act = self.sender() + index = act.data() + itm = self.imagesTree.topLevelItem(index) + if itm is None: + return + + if not self.imagePreview.scene() or \ + len(self.imagePreview.scene().items()) == 0: + return + + pixmapItem = self.imagePreview.scene().items()[0] + if not isinstance(pixmapItem, QGraphicsPixmapItem): + return + + if pixmapItem.pixmap().isNull(): + E5MessageBox.warning( + self, + self.tr("Save Image"), + self.tr( + """<p>This preview is not available.</p>""")) + return + + imageFileName = WebBrowserTools.getFileNameFromUrl(QUrl(itm.text(1))) + index = imageFileName.rfind(".") + if index != -1: + imageFileName = imageFileName[:index] + ".png" + + filename = E5FileDialog.getSaveFileName( + self, + self.tr("Save Image"), + imageFileName, + self.tr("All Files (*)"), + E5FileDialog.Options(E5FileDialog.DontConfirmOverwrite)) + + if not filename: + return + + if not pixmapItem.pixmap().save(filename, "PNG"): + E5MessageBox.critical( + self, + self.tr("Save Image"), + self.tr( + """<p>Cannot write to file <b>{0}</b>.</p>""") + .format(filename)) + return