eric6/WebBrowser/SiteInfo/SiteInfoDialog.py

Sat, 27 Feb 2021 12:08:23 +0100

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Sat, 27 Feb 2021 12:08:23 +0100
changeset 8138
169e65a6787c
parent 7937
181d1160f617
child 8143
2c730d5fd177
permissions
-rw-r--r--

Shell: added functionality to show a prompt when the main client process has exited (e.g. a script ended).

# -*- coding: utf-8 -*-

# Copyright (c) 2011 - 2021 Detlev Offenbach <detlev@die-offenbachs.de>
#

"""
Module implementing a dialog to show some information about a site.
"""

from PyQt5.QtCore import pyqtSlot, QUrl, Qt
from PyQt5.QtGui import QPixmap, QImage, QPainter, QColor, QBrush
from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply
from PyQt5.QtWidgets import (
    QDialog, QTreeWidgetItem, QGraphicsScene, QMenu, QApplication,
    QGraphicsPixmapItem
)
try:
    from PyQt5.QtNetwork import QSslCertificate     # __IGNORE_WARNING__
    SSL = True
except ImportError:
    SSL = False

from E5Gui import E5MessageBox, E5FileDialog

from .Ui_SiteInfoDialog import Ui_SiteInfoDialog

from ..Tools import Scripts, WebBrowserTools
from ..WebBrowserPage import WebBrowserPage

import UI.PixmapCache
import Preferences

from WebBrowser.WebBrowserWindow import WebBrowserWindow


class SiteInfoDialog(QDialog, Ui_SiteInfoDialog):
    """
    Class implementing a dialog to show some information about a site.
    """
    securityStyleFormat = "QLabel {{ background-color : {0}; }}"
    
    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"))
        self.tabWidget.setTabIcon(
            1, UI.PixmapCache.getIcon("siteinfo-media"))
        if SSL:
            self.tabWidget.setTabIcon(
                2, UI.PixmapCache.getIcon("siteinfo-security"))
        
        self.__imageReply = None
        
        self.__baseUrl = browser.url()
        title = browser.title()
        sslInfo = browser.page().getSslCertificateChain()
        
        #prepare background of image preview
        self.__imagePreviewStandardBackground = (
            self.imagePreview.backgroundBrush()
        )
        color1 = QColor(220, 220, 220)
        color2 = QColor(160, 160, 160)
        self.__tilePixmap = QPixmap(8, 8)
        self.__tilePixmap.fill(color1)
        tilePainter = QPainter(self.__tilePixmap)
        tilePainter.fillRect(0, 0, 4, 4, color2)
        tilePainter.fillRect(4, 4, 4, 4, color2)
        tilePainter.end()
        
        # populate General tab
        self.heading.setText("<b>{0}</b>".format(title))
        self.siteAddressLabel.setText(self.__baseUrl.toString())
        if self.__baseUrl.scheme() in ["https"]:
            if WebBrowserWindow.networkManager().isInsecureHost(
                self.__baseUrl.host()
            ):
                self.securityIconLabel.setPixmap(
                    UI.PixmapCache.getPixmap("securityMedium"))
                self.securityLabel.setStyleSheet(
                    SiteInfoDialog.securityStyleFormat.format(
                        Preferences.getWebBrowser("InsecureUrlColor").name()
                    )
                )
                self.securityLabel.setText(self.tr(
                    '<b>Connection is encrypted but may be insecure.</b>'))
            else:
                self.securityIconLabel.setPixmap(
                    UI.PixmapCache.getPixmap("securityHigh"))
                self.securityLabel.setStyleSheet(
                    SiteInfoDialog.securityStyleFormat.format(
                        Preferences.getWebBrowser("SecureUrlColor").name()
                    )
                )
                self.securityLabel.setText(
                    self.tr('<b>Connection is encrypted.</b>'))
        else:
            self.securityIconLabel.setPixmap(
                UI.PixmapCache.getPixmap("securityLow"))
            self.securityLabel.setText(
                self.tr('<b>Connection is not encrypted.</b>'))
        browser.page().runJavaScript(
            "document.charset", WebBrowserPage.SafeJsWorld,
            lambda res: self.encodingLabel.setText(res))
        
        # populate the Security tab
        if sslInfo:
            if SSL:
                self.sslWidget.showCertificateChain(sslInfo)
        self.tabWidget.setTabEnabled(2, SSL and bool(sslInfo))
        self.securityDetailsButton.setEnabled(SSL and bool(sslInfo))
        
        # populate Meta tags
        browser.page().runJavaScript(Scripts.getAllMetaAttributes(),
                                     WebBrowserPage.SafeJsWorld,
                                     self.__processMetaAttributes)
        
        # populate Media tab
        browser.page().runJavaScript(Scripts.getAllImages(),
                                     WebBrowserPage.SafeJsWorld,
                                     self.__processImageTags)
        
        self.tabWidget.setCurrentIndex(0)
    
    @pyqtSlot()
    def on_securityDetailsButton_clicked(self):
        """
        Private slot to show security details.
        """
        self.tabWidget.setCurrentIndex(
            self.tabWidget.indexOf(self.securityTab))
    
    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(b",") + 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():
            self.imagePreview.setBackgroundBrush(
                self.__imagePreviewStandardBackground)
            scene.addText(self.tr("Preview not available."))
        else:
            self.imagePreview.setBackgroundBrush(QBrush(self.__tilePixmap))
            scene.addPixmap(pixmap)
        self.imagePreview.setScene(scene)
    
    def __showLoadingText(self):
        """
        Private method to show some text while loading an image.
        """
        self.imagePreview.setBackgroundBrush(
            self.__imagePreviewStandardBackground)
        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()
        act1 = menu.addAction(self.tr("Copy Image Location to Clipboard"))
        act1.setData(itm.text(1))
        act1.triggered.connect(lambda: self.__copyAction(act1))
        act2 = menu.addAction(self.tr("Copy Image Name to Clipboard"))
        act2.setData(itm.text(0))
        act2.triggered.connect(lambda: self.__copyAction(act2))
        menu.addSeparator()
        act3 = menu.addAction(self.tr("Save Image"))
        act3.setData(self.imagesTree.indexOfTopLevelItem(itm))
        act3.triggered.connect(lambda: self.__saveImage(act3))
        menu.exec(self.imagesTree.viewport().mapToGlobal(pos))
    
    def __copyAction(self, act):
        """
        Private slot to copy the image URL or the image name to the clipboard.
        
        @param act reference to the action that triggered
        @type QAction
        """
        QApplication.clipboard().setText(act.data())
    
    def __saveImage(self, act):
        """
        Private slot to save the selected image to disk.
        
        @param act reference to the action that triggered
        @type QAction
        """
        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

eric ide

mercurial