|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2011 - 2016 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing a dialog to show some information about a site. |
|
8 """ |
|
9 |
|
10 from __future__ import unicode_literals |
|
11 |
|
12 from PyQt5.QtCore import pyqtSlot, QUrl, Qt |
|
13 from PyQt5.QtGui import QPixmap, QImage |
|
14 from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply |
|
15 from PyQt5.QtWidgets import QDialog, QTreeWidgetItem, QGraphicsScene, QMenu, \ |
|
16 QApplication, QGraphicsPixmapItem |
|
17 |
|
18 from E5Gui import E5MessageBox, E5FileDialog |
|
19 |
|
20 from .Ui_SiteInfoDialog import Ui_SiteInfoDialog |
|
21 |
|
22 from ..Tools import Scripts, WebBrowserTools |
|
23 |
|
24 import UI.PixmapCache |
|
25 |
|
26 |
|
27 class SiteInfoDialog(QDialog, Ui_SiteInfoDialog): |
|
28 """ |
|
29 Class implementing a dialog to show some information about a site. |
|
30 """ |
|
31 okStyle = "QLabel { color : white; background-color : green; }" |
|
32 nokStyle = "QLabel { color : white; background-color : red; }" |
|
33 |
|
34 def __init__(self, browser, parent=None): |
|
35 """ |
|
36 Constructor |
|
37 |
|
38 @param browser reference to the browser window (HelpBrowser) |
|
39 @param parent reference to the parent widget (QWidget) |
|
40 """ |
|
41 super(SiteInfoDialog, self).__init__(parent) |
|
42 self.setupUi(self) |
|
43 self.setWindowFlags(Qt.Window) |
|
44 |
|
45 # put icons |
|
46 self.tabWidget.setTabIcon( |
|
47 0, UI.PixmapCache.getIcon("siteinfo-general.png")) |
|
48 self.tabWidget.setTabIcon( |
|
49 1, UI.PixmapCache.getIcon("siteinfo-media.png")) |
|
50 |
|
51 self.__imageReply = None |
|
52 |
|
53 self.__baseUrl = browser.url() |
|
54 title = browser.title() |
|
55 |
|
56 # populate General tab |
|
57 self.heading.setText("<b>{0}</b>".format(title)) |
|
58 self.siteAddressLabel.setText(self.__baseUrl.toString()) |
|
59 if self.__baseUrl.scheme() in ["https"]: |
|
60 self.securityLabel.setStyleSheet(SiteInfoDialog.okStyle) |
|
61 self.securityLabel.setText('<b>Connection is encrypted.</b>') |
|
62 else: |
|
63 self.securityLabel.setStyleSheet(SiteInfoDialog.nokStyle) |
|
64 self.securityLabel.setText('<b>Connection is not encrypted.</b>') |
|
65 browser.page().runJavaScript( |
|
66 "document.charset", |
|
67 lambda res: self.encodingLabel.setText(res)) |
|
68 |
|
69 # populate Meta tags |
|
70 browser.page().runJavaScript(Scripts.getAllMetaAttributes(), |
|
71 self.__processMetaAttributes) |
|
72 |
|
73 # populate Media tab |
|
74 browser.page().runJavaScript(Scripts.getAllImages(), |
|
75 self.__processImageTags) |
|
76 |
|
77 def __processImageTags(self, res): |
|
78 """ |
|
79 Private method to process the image tags. |
|
80 |
|
81 @param res result of the JavaScript script |
|
82 @type list of dict |
|
83 """ |
|
84 for img in res: |
|
85 src = img["src"] |
|
86 alt = img["alt"] |
|
87 if not alt: |
|
88 if src.find("/") == -1: |
|
89 alt = src |
|
90 else: |
|
91 pos = src.rfind("/") |
|
92 alt = src[pos + 1:] |
|
93 |
|
94 if not src or not alt: |
|
95 continue |
|
96 |
|
97 QTreeWidgetItem(self.imagesTree, [alt, src]) |
|
98 |
|
99 for col in range(self.imagesTree.columnCount()): |
|
100 self.imagesTree.resizeColumnToContents(col) |
|
101 if self.imagesTree.columnWidth(0) > 300: |
|
102 self.imagesTree.setColumnWidth(0, 300) |
|
103 self.imagesTree.setCurrentItem(self.imagesTree.topLevelItem(0)) |
|
104 self.imagesTree.setContextMenuPolicy(Qt.CustomContextMenu) |
|
105 self.imagesTree.customContextMenuRequested.connect( |
|
106 self.__imagesTreeContextMenuRequested) |
|
107 |
|
108 def __processMetaAttributes(self, res): |
|
109 """ |
|
110 Private method to process the meta attributes. |
|
111 |
|
112 @param res result of the JavaScript script |
|
113 @type list of dict |
|
114 """ |
|
115 for meta in res: |
|
116 content = meta["content"] |
|
117 name = meta["name"] |
|
118 if not name: |
|
119 name = meta["httpequiv"] |
|
120 |
|
121 if not name or not content: |
|
122 continue |
|
123 |
|
124 if meta["charset"]: |
|
125 self.encodingLabel.setText(meta["charset"]) |
|
126 if "charset=" in content: |
|
127 self.encodingLabel.setText( |
|
128 content[content.index("charset=") + 8:]) |
|
129 |
|
130 QTreeWidgetItem(self.tagsTree, [name, content]) |
|
131 for col in range(self.tagsTree.columnCount()): |
|
132 self.tagsTree.resizeColumnToContents(col) |
|
133 |
|
134 @pyqtSlot(QTreeWidgetItem, QTreeWidgetItem) |
|
135 def on_imagesTree_currentItemChanged(self, current, previous): |
|
136 """ |
|
137 Private slot to show a preview of the selected image. |
|
138 |
|
139 @param current current image entry (QTreeWidgetItem) |
|
140 @param previous old current entry (QTreeWidgetItem) |
|
141 """ |
|
142 if current is None: |
|
143 return |
|
144 |
|
145 imageUrl = QUrl(current.text(1)) |
|
146 if imageUrl.isRelative(): |
|
147 imageUrl = self.__baseUrl.resolved(imageUrl) |
|
148 |
|
149 pixmap = QPixmap() |
|
150 loading = False |
|
151 |
|
152 if imageUrl.scheme() == "data": |
|
153 encodedUrl = current.text(1).encode("utf-8") |
|
154 imageData = encodedUrl[encodedUrl.find(",") + 1:] |
|
155 pixmap = WebBrowserTools.pixmapFromByteArray(imageData) |
|
156 elif imageUrl.scheme() == "file": |
|
157 pixmap = QPixmap(imageUrl.toLocalFile()) |
|
158 elif imageUrl.scheme() == "qrc": |
|
159 pixmap = QPixmap(imageUrl.toString()[3:]) |
|
160 else: |
|
161 if self.__imageReply is not None: |
|
162 self.__imageReply.deleteLater() |
|
163 self.__imageReply = None |
|
164 |
|
165 from WebBrowser.WebBrowserWindow import WebBrowserWindow |
|
166 self.__imageReply = WebBrowserWindow.networkManager().get( |
|
167 QNetworkRequest(imageUrl)) |
|
168 self.__imageReply.finished.connect(self.__imageReplyFinished) |
|
169 loading = True |
|
170 self.__showLoadingText() |
|
171 |
|
172 if not loading: |
|
173 self.__showPixmap(pixmap) |
|
174 |
|
175 @pyqtSlot() |
|
176 def __imageReplyFinished(self): |
|
177 """ |
|
178 Private slot handling the loading of an image. |
|
179 """ |
|
180 if self.__imageReply.error() != QNetworkReply.NoError: |
|
181 return |
|
182 |
|
183 data = self.__imageReply.readAll() |
|
184 self.__showPixmap(QPixmap.fromImage(QImage.fromData(data))) |
|
185 |
|
186 def __showPixmap(self, pixmap): |
|
187 """ |
|
188 Private method to show a pixmap in the preview pane. |
|
189 |
|
190 @param pixmap pixmap to be shown |
|
191 @type QPixmap |
|
192 """ |
|
193 scene = QGraphicsScene(self.imagePreview) |
|
194 if pixmap.isNull(): |
|
195 scene.addText(self.tr("Preview not available.")) |
|
196 else: |
|
197 scene.addPixmap(pixmap) |
|
198 self.imagePreview.setScene(scene) |
|
199 |
|
200 def __showLoadingText(self): |
|
201 """ |
|
202 Private method to show some text while loading an image. |
|
203 """ |
|
204 scene = QGraphicsScene(self.imagePreview) |
|
205 scene.addText(self.tr("Loading...")) |
|
206 self.imagePreview.setScene(scene) |
|
207 |
|
208 def __imagesTreeContextMenuRequested(self, pos): |
|
209 """ |
|
210 Private slot to show a context menu for the images list. |
|
211 |
|
212 @param pos position for the menu (QPoint) |
|
213 """ |
|
214 itm = self.imagesTree.itemAt(pos) |
|
215 if itm is None: |
|
216 return |
|
217 |
|
218 menu = QMenu() |
|
219 menu.addAction( |
|
220 self.tr("Copy Image Location to Clipboard"), |
|
221 self.__copyAction).setData(itm.text(1)) |
|
222 menu.addAction( |
|
223 self.tr("Copy Image Name to Clipboard"), |
|
224 self.__copyAction).setData(itm.text(0)) |
|
225 menu.addSeparator() |
|
226 menu.addAction( |
|
227 self.tr("Save Image"), |
|
228 self.__saveImage).setData(self.imagesTree.indexOfTopLevelItem(itm)) |
|
229 menu.exec_(self.imagesTree.viewport().mapToGlobal(pos)) |
|
230 |
|
231 def __copyAction(self): |
|
232 """ |
|
233 Private slot to copy the image URL or the image name to the clipboard. |
|
234 """ |
|
235 act = self.sender() |
|
236 QApplication.clipboard().setText(act.data()) |
|
237 |
|
238 def __saveImage(self): |
|
239 """ |
|
240 Private slot to save the selected image to disk. |
|
241 """ |
|
242 act = self.sender() |
|
243 index = act.data() |
|
244 itm = self.imagesTree.topLevelItem(index) |
|
245 if itm is None: |
|
246 return |
|
247 |
|
248 if not self.imagePreview.scene() or \ |
|
249 len(self.imagePreview.scene().items()) == 0: |
|
250 return |
|
251 |
|
252 pixmapItem = self.imagePreview.scene().items()[0] |
|
253 if not isinstance(pixmapItem, QGraphicsPixmapItem): |
|
254 return |
|
255 |
|
256 if pixmapItem.pixmap().isNull(): |
|
257 E5MessageBox.warning( |
|
258 self, |
|
259 self.tr("Save Image"), |
|
260 self.tr( |
|
261 """<p>This preview is not available.</p>""")) |
|
262 return |
|
263 |
|
264 imageFileName = WebBrowserTools.getFileNameFromUrl(QUrl(itm.text(1))) |
|
265 index = imageFileName.rfind(".") |
|
266 if index != -1: |
|
267 imageFileName = imageFileName[:index] + ".png" |
|
268 |
|
269 filename = E5FileDialog.getSaveFileName( |
|
270 self, |
|
271 self.tr("Save Image"), |
|
272 imageFileName, |
|
273 self.tr("All Files (*)"), |
|
274 E5FileDialog.Options(E5FileDialog.DontConfirmOverwrite)) |
|
275 |
|
276 if not filename: |
|
277 return |
|
278 |
|
279 if not pixmapItem.pixmap().save(filename, "PNG"): |
|
280 E5MessageBox.critical( |
|
281 self, |
|
282 self.tr("Save Image"), |
|
283 self.tr( |
|
284 """<p>Cannot write to file <b>{0}</b>.</p>""") |
|
285 .format(filename)) |
|
286 return |