src/eric7/WebBrowser/SiteInfo/SiteInfoDialog.py

branch
eric7
changeset 9221
bf71ee032bb4
parent 9209
b99e7fd55fd3
child 9413
80c06d472826
equal deleted inserted replaced
9220:e9e7eca7efee 9221:bf71ee032bb4
9 9
10 from PyQt6.QtCore import pyqtSlot, QUrl, Qt 10 from PyQt6.QtCore import pyqtSlot, QUrl, Qt
11 from PyQt6.QtGui import QPixmap, QImage, QPainter, QColor, QBrush 11 from PyQt6.QtGui import QPixmap, QImage, QPainter, QColor, QBrush
12 from PyQt6.QtNetwork import QNetworkRequest, QNetworkReply 12 from PyQt6.QtNetwork import QNetworkRequest, QNetworkReply
13 from PyQt6.QtWidgets import ( 13 from PyQt6.QtWidgets import (
14 QDialog, QTreeWidgetItem, QGraphicsScene, QMenu, QApplication, 14 QDialog,
15 QGraphicsPixmapItem 15 QTreeWidgetItem,
16 QGraphicsScene,
17 QMenu,
18 QApplication,
19 QGraphicsPixmapItem,
16 ) 20 )
21
17 try: 22 try:
18 from PyQt6.QtNetwork import QSslCertificate # __IGNORE_WARNING__ 23 from PyQt6.QtNetwork import QSslCertificate # __IGNORE_WARNING__
24
19 SSL = True 25 SSL = True
20 except ImportError: 26 except ImportError:
21 SSL = False 27 SSL = False
22 28
23 from EricWidgets import EricMessageBox, EricFileDialog 29 from EricWidgets import EricMessageBox, EricFileDialog
35 41
36 class SiteInfoDialog(QDialog, Ui_SiteInfoDialog): 42 class SiteInfoDialog(QDialog, Ui_SiteInfoDialog):
37 """ 43 """
38 Class implementing a dialog to show some information about a site. 44 Class implementing a dialog to show some information about a site.
39 """ 45 """
46
40 securityStyleFormat = "QLabel {{ background-color : {0}; }}" 47 securityStyleFormat = "QLabel {{ background-color : {0}; }}"
41 48
42 def __init__(self, browser, parent=None): 49 def __init__(self, browser, parent=None):
43 """ 50 """
44 Constructor 51 Constructor
45 52
46 @param browser reference to the browser window (HelpBrowser) 53 @param browser reference to the browser window (HelpBrowser)
47 @param parent reference to the parent widget (QWidget) 54 @param parent reference to the parent widget (QWidget)
48 """ 55 """
49 super().__init__(parent) 56 super().__init__(parent)
50 self.setupUi(self) 57 self.setupUi(self)
51 self.setWindowFlags(Qt.WindowType.Window) 58 self.setWindowFlags(Qt.WindowType.Window)
52 59
53 # put icons 60 # put icons
54 self.tabWidget.setTabIcon( 61 self.tabWidget.setTabIcon(0, UI.PixmapCache.getIcon("siteinfo-general"))
55 0, UI.PixmapCache.getIcon("siteinfo-general")) 62 self.tabWidget.setTabIcon(1, UI.PixmapCache.getIcon("siteinfo-media"))
56 self.tabWidget.setTabIcon(
57 1, UI.PixmapCache.getIcon("siteinfo-media"))
58 if SSL: 63 if SSL:
59 self.tabWidget.setTabIcon( 64 self.tabWidget.setTabIcon(2, UI.PixmapCache.getIcon("siteinfo-security"))
60 2, UI.PixmapCache.getIcon("siteinfo-security")) 65
61
62 self.__imageReply = None 66 self.__imageReply = None
63 67
64 self.__baseUrl = browser.url() 68 self.__baseUrl = browser.url()
65 title = browser.title() 69 title = browser.title()
66 sslInfo = browser.page().getSslCertificateChain() 70 sslInfo = browser.page().getSslCertificateChain()
67 71
68 #prepare background of image preview 72 # prepare background of image preview
69 self.__imagePreviewStandardBackground = ( 73 self.__imagePreviewStandardBackground = self.imagePreview.backgroundBrush()
70 self.imagePreview.backgroundBrush()
71 )
72 color1 = QColor(220, 220, 220) 74 color1 = QColor(220, 220, 220)
73 color2 = QColor(160, 160, 160) 75 color2 = QColor(160, 160, 160)
74 self.__tilePixmap = QPixmap(8, 8) 76 self.__tilePixmap = QPixmap(8, 8)
75 self.__tilePixmap.fill(color1) 77 self.__tilePixmap.fill(color1)
76 tilePainter = QPainter(self.__tilePixmap) 78 tilePainter = QPainter(self.__tilePixmap)
77 tilePainter.fillRect(0, 0, 4, 4, color2) 79 tilePainter.fillRect(0, 0, 4, 4, color2)
78 tilePainter.fillRect(4, 4, 4, 4, color2) 80 tilePainter.fillRect(4, 4, 4, 4, color2)
79 tilePainter.end() 81 tilePainter.end()
80 82
81 # populate General tab 83 # populate General tab
82 self.heading.setText("<b>{0}</b>".format(title)) 84 self.heading.setText("<b>{0}</b>".format(title))
83 self.siteAddressLabel.setText(self.__baseUrl.toString()) 85 self.siteAddressLabel.setText(self.__baseUrl.toString())
84 if self.__baseUrl.scheme() in ["https"]: 86 if self.__baseUrl.scheme() in ["https"]:
85 if WebBrowserWindow.networkManager().isInsecureHost( 87 if WebBrowserWindow.networkManager().isInsecureHost(self.__baseUrl.host()):
86 self.__baseUrl.host()
87 ):
88 self.securityIconLabel.setPixmap( 88 self.securityIconLabel.setPixmap(
89 UI.PixmapCache.getPixmap("securityMedium")) 89 UI.PixmapCache.getPixmap("securityMedium")
90 )
90 self.securityLabel.setStyleSheet( 91 self.securityLabel.setStyleSheet(
91 SiteInfoDialog.securityStyleFormat.format( 92 SiteInfoDialog.securityStyleFormat.format(
92 Preferences.getWebBrowser("InsecureUrlColor").name() 93 Preferences.getWebBrowser("InsecureUrlColor").name()
93 ) 94 )
94 ) 95 )
95 self.securityLabel.setText(self.tr( 96 self.securityLabel.setText(
96 '<b>Connection is encrypted but may be insecure.</b>')) 97 self.tr("<b>Connection is encrypted but may be insecure.</b>")
98 )
97 else: 99 else:
98 self.securityIconLabel.setPixmap( 100 self.securityIconLabel.setPixmap(
99 UI.PixmapCache.getPixmap("securityHigh")) 101 UI.PixmapCache.getPixmap("securityHigh")
102 )
100 self.securityLabel.setStyleSheet( 103 self.securityLabel.setStyleSheet(
101 SiteInfoDialog.securityStyleFormat.format( 104 SiteInfoDialog.securityStyleFormat.format(
102 Preferences.getWebBrowser("SecureUrlColor").name() 105 Preferences.getWebBrowser("SecureUrlColor").name()
103 ) 106 )
104 ) 107 )
105 self.securityLabel.setText( 108 self.securityLabel.setText(self.tr("<b>Connection is encrypted.</b>"))
106 self.tr('<b>Connection is encrypted.</b>'))
107 else: 109 else:
108 self.securityIconLabel.setPixmap( 110 self.securityIconLabel.setPixmap(UI.PixmapCache.getPixmap("securityLow"))
109 UI.PixmapCache.getPixmap("securityLow")) 111 self.securityLabel.setText(self.tr("<b>Connection is not encrypted.</b>"))
110 self.securityLabel.setText(
111 self.tr('<b>Connection is not encrypted.</b>'))
112 browser.page().runJavaScript( 112 browser.page().runJavaScript(
113 "document.charset", WebBrowserPage.SafeJsWorld, 113 "document.charset",
114 lambda res: self.encodingLabel.setText(res)) 114 WebBrowserPage.SafeJsWorld,
115 115 lambda res: self.encodingLabel.setText(res),
116 )
117
116 # populate the Security tab 118 # populate the Security tab
117 if sslInfo and SSL: 119 if sslInfo and SSL:
118 self.sslWidget.showCertificateChain(sslInfo) 120 self.sslWidget.showCertificateChain(sslInfo)
119 self.tabWidget.setTabEnabled(2, SSL and bool(sslInfo)) 121 self.tabWidget.setTabEnabled(2, SSL and bool(sslInfo))
120 self.securityDetailsButton.setEnabled(SSL and bool(sslInfo)) 122 self.securityDetailsButton.setEnabled(SSL and bool(sslInfo))
121 123
122 # populate Meta tags 124 # populate Meta tags
123 browser.page().runJavaScript(Scripts.getAllMetaAttributes(), 125 browser.page().runJavaScript(
124 WebBrowserPage.SafeJsWorld, 126 Scripts.getAllMetaAttributes(),
125 self.__processMetaAttributes) 127 WebBrowserPage.SafeJsWorld,
126 128 self.__processMetaAttributes,
129 )
130
127 # populate Media tab 131 # populate Media tab
128 browser.page().runJavaScript(Scripts.getAllImages(), 132 browser.page().runJavaScript(
129 WebBrowserPage.SafeJsWorld, 133 Scripts.getAllImages(), WebBrowserPage.SafeJsWorld, self.__processImageTags
130 self.__processImageTags) 134 )
131 135
132 self.tabWidget.setCurrentIndex(0) 136 self.tabWidget.setCurrentIndex(0)
133 137
134 @pyqtSlot() 138 @pyqtSlot()
135 def on_securityDetailsButton_clicked(self): 139 def on_securityDetailsButton_clicked(self):
136 """ 140 """
137 Private slot to show security details. 141 Private slot to show security details.
138 """ 142 """
139 self.tabWidget.setCurrentIndex( 143 self.tabWidget.setCurrentIndex(self.tabWidget.indexOf(self.securityTab))
140 self.tabWidget.indexOf(self.securityTab)) 144
141
142 def __processImageTags(self, res): 145 def __processImageTags(self, res):
143 """ 146 """
144 Private method to process the image tags. 147 Private method to process the image tags.
145 148
146 @param res result of the JavaScript script 149 @param res result of the JavaScript script
147 @type list of dict 150 @type list of dict
148 """ 151 """
149 for img in res: 152 for img in res:
150 src = img["src"] 153 src = img["src"]
152 if not alt: 155 if not alt:
153 if src.find("/") == -1: 156 if src.find("/") == -1:
154 alt = src 157 alt = src
155 else: 158 else:
156 pos = src.rfind("/") 159 pos = src.rfind("/")
157 alt = src[pos + 1:] 160 alt = src[pos + 1 :]
158 161
159 if not src or not alt: 162 if not src or not alt:
160 continue 163 continue
161 164
162 QTreeWidgetItem(self.imagesTree, [alt, src]) 165 QTreeWidgetItem(self.imagesTree, [alt, src])
163 166
164 for col in range(self.imagesTree.columnCount()): 167 for col in range(self.imagesTree.columnCount()):
165 self.imagesTree.resizeColumnToContents(col) 168 self.imagesTree.resizeColumnToContents(col)
166 if self.imagesTree.columnWidth(0) > 300: 169 if self.imagesTree.columnWidth(0) > 300:
167 self.imagesTree.setColumnWidth(0, 300) 170 self.imagesTree.setColumnWidth(0, 300)
168 self.imagesTree.setCurrentItem(self.imagesTree.topLevelItem(0)) 171 self.imagesTree.setCurrentItem(self.imagesTree.topLevelItem(0))
169 self.imagesTree.setContextMenuPolicy( 172 self.imagesTree.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
170 Qt.ContextMenuPolicy.CustomContextMenu)
171 self.imagesTree.customContextMenuRequested.connect( 173 self.imagesTree.customContextMenuRequested.connect(
172 self.__imagesTreeContextMenuRequested) 174 self.__imagesTreeContextMenuRequested
173 175 )
176
174 def __processMetaAttributes(self, res): 177 def __processMetaAttributes(self, res):
175 """ 178 """
176 Private method to process the meta attributes. 179 Private method to process the meta attributes.
177 180
178 @param res result of the JavaScript script 181 @param res result of the JavaScript script
179 @type list of dict 182 @type list of dict
180 """ 183 """
181 for meta in res: 184 for meta in res:
182 content = meta["content"] 185 content = meta["content"]
183 name = meta["name"] 186 name = meta["name"]
184 if not name: 187 if not name:
185 name = meta["httpequiv"] 188 name = meta["httpequiv"]
186 189
187 if not name or not content: 190 if not name or not content:
188 continue 191 continue
189 192
190 if meta["charset"]: 193 if meta["charset"]:
191 self.encodingLabel.setText(meta["charset"]) 194 self.encodingLabel.setText(meta["charset"])
192 if "charset=" in content: 195 if "charset=" in content:
193 self.encodingLabel.setText( 196 self.encodingLabel.setText(content[content.index("charset=") + 8 :])
194 content[content.index("charset=") + 8:]) 197
195
196 QTreeWidgetItem(self.tagsTree, [name, content]) 198 QTreeWidgetItem(self.tagsTree, [name, content])
197 for col in range(self.tagsTree.columnCount()): 199 for col in range(self.tagsTree.columnCount()):
198 self.tagsTree.resizeColumnToContents(col) 200 self.tagsTree.resizeColumnToContents(col)
199 201
200 @pyqtSlot(QTreeWidgetItem, QTreeWidgetItem) 202 @pyqtSlot(QTreeWidgetItem, QTreeWidgetItem)
201 def on_imagesTree_currentItemChanged(self, current, previous): 203 def on_imagesTree_currentItemChanged(self, current, previous):
202 """ 204 """
203 Private slot to show a preview of the selected image. 205 Private slot to show a preview of the selected image.
204 206
205 @param current current image entry (QTreeWidgetItem) 207 @param current current image entry (QTreeWidgetItem)
206 @param previous old current entry (QTreeWidgetItem) 208 @param previous old current entry (QTreeWidgetItem)
207 """ 209 """
208 if current is None: 210 if current is None:
209 return 211 return
210 212
211 imageUrl = QUrl(current.text(1)) 213 imageUrl = QUrl(current.text(1))
212 if imageUrl.isRelative(): 214 if imageUrl.isRelative():
213 imageUrl = self.__baseUrl.resolved(imageUrl) 215 imageUrl = self.__baseUrl.resolved(imageUrl)
214 216
215 pixmap = QPixmap() 217 pixmap = QPixmap()
216 loading = False 218 loading = False
217 219
218 if imageUrl.scheme() == "data": 220 if imageUrl.scheme() == "data":
219 encodedUrl = current.text(1).encode("utf-8") 221 encodedUrl = current.text(1).encode("utf-8")
220 imageData = encodedUrl[encodedUrl.find(b",") + 1:] 222 imageData = encodedUrl[encodedUrl.find(b",") + 1 :]
221 pixmap = WebBrowserTools.pixmapFromByteArray(imageData) 223 pixmap = WebBrowserTools.pixmapFromByteArray(imageData)
222 elif imageUrl.scheme() == "file": 224 elif imageUrl.scheme() == "file":
223 pixmap = QPixmap(imageUrl.toLocalFile()) 225 pixmap = QPixmap(imageUrl.toLocalFile())
224 elif imageUrl.scheme() == "qrc": 226 elif imageUrl.scheme() == "qrc":
225 pixmap = QPixmap(imageUrl.toString()[3:]) 227 pixmap = QPixmap(imageUrl.toString()[3:])
226 else: 228 else:
227 if self.__imageReply is not None: 229 if self.__imageReply is not None:
228 self.__imageReply.deleteLater() 230 self.__imageReply.deleteLater()
229 self.__imageReply = None 231 self.__imageReply = None
230 232
231 from WebBrowser.WebBrowserWindow import WebBrowserWindow 233 from WebBrowser.WebBrowserWindow import WebBrowserWindow
234
232 self.__imageReply = WebBrowserWindow.networkManager().get( 235 self.__imageReply = WebBrowserWindow.networkManager().get(
233 QNetworkRequest(imageUrl)) 236 QNetworkRequest(imageUrl)
237 )
234 self.__imageReply.finished.connect(self.__imageReplyFinished) 238 self.__imageReply.finished.connect(self.__imageReplyFinished)
235 loading = True 239 loading = True
236 self.__showLoadingText() 240 self.__showLoadingText()
237 241
238 if not loading: 242 if not loading:
239 self.__showPixmap(pixmap) 243 self.__showPixmap(pixmap)
240 244
241 @pyqtSlot() 245 @pyqtSlot()
242 def __imageReplyFinished(self): 246 def __imageReplyFinished(self):
243 """ 247 """
244 Private slot handling the loading of an image. 248 Private slot handling the loading of an image.
245 """ 249 """
246 if self.__imageReply.error() != QNetworkReply.NetworkError.NoError: 250 if self.__imageReply.error() != QNetworkReply.NetworkError.NoError:
247 return 251 return
248 252
249 data = self.__imageReply.readAll() 253 data = self.__imageReply.readAll()
250 self.__showPixmap(QPixmap.fromImage(QImage.fromData(data))) 254 self.__showPixmap(QPixmap.fromImage(QImage.fromData(data)))
251 255
252 def __showPixmap(self, pixmap): 256 def __showPixmap(self, pixmap):
253 """ 257 """
254 Private method to show a pixmap in the preview pane. 258 Private method to show a pixmap in the preview pane.
255 259
256 @param pixmap pixmap to be shown 260 @param pixmap pixmap to be shown
257 @type QPixmap 261 @type QPixmap
258 """ 262 """
259 scene = QGraphicsScene(self.imagePreview) 263 scene = QGraphicsScene(self.imagePreview)
260 if pixmap.isNull(): 264 if pixmap.isNull():
261 self.imagePreview.setBackgroundBrush( 265 self.imagePreview.setBackgroundBrush(self.__imagePreviewStandardBackground)
262 self.__imagePreviewStandardBackground)
263 scene.addText(self.tr("Preview not available.")) 266 scene.addText(self.tr("Preview not available."))
264 else: 267 else:
265 self.imagePreview.setBackgroundBrush(QBrush(self.__tilePixmap)) 268 self.imagePreview.setBackgroundBrush(QBrush(self.__tilePixmap))
266 scene.addPixmap(pixmap) 269 scene.addPixmap(pixmap)
267 self.imagePreview.setScene(scene) 270 self.imagePreview.setScene(scene)
268 271
269 def __showLoadingText(self): 272 def __showLoadingText(self):
270 """ 273 """
271 Private method to show some text while loading an image. 274 Private method to show some text while loading an image.
272 """ 275 """
273 self.imagePreview.setBackgroundBrush( 276 self.imagePreview.setBackgroundBrush(self.__imagePreviewStandardBackground)
274 self.__imagePreviewStandardBackground)
275 scene = QGraphicsScene(self.imagePreview) 277 scene = QGraphicsScene(self.imagePreview)
276 scene.addText(self.tr("Loading...")) 278 scene.addText(self.tr("Loading..."))
277 self.imagePreview.setScene(scene) 279 self.imagePreview.setScene(scene)
278 280
279 def __imagesTreeContextMenuRequested(self, pos): 281 def __imagesTreeContextMenuRequested(self, pos):
280 """ 282 """
281 Private slot to show a context menu for the images list. 283 Private slot to show a context menu for the images list.
282 284
283 @param pos position for the menu (QPoint) 285 @param pos position for the menu (QPoint)
284 """ 286 """
285 itm = self.imagesTree.itemAt(pos) 287 itm = self.imagesTree.itemAt(pos)
286 if itm is None: 288 if itm is None:
287 return 289 return
288 290
289 menu = QMenu() 291 menu = QMenu()
290 act1 = menu.addAction(self.tr("Copy Image Location to Clipboard")) 292 act1 = menu.addAction(self.tr("Copy Image Location to Clipboard"))
291 act1.setData(itm.text(1)) 293 act1.setData(itm.text(1))
292 act1.triggered.connect(lambda: self.__copyAction(act1)) 294 act1.triggered.connect(lambda: self.__copyAction(act1))
293 act2 = menu.addAction(self.tr("Copy Image Name to Clipboard")) 295 act2 = menu.addAction(self.tr("Copy Image Name to Clipboard"))
296 menu.addSeparator() 298 menu.addSeparator()
297 act3 = menu.addAction(self.tr("Save Image")) 299 act3 = menu.addAction(self.tr("Save Image"))
298 act3.setData(self.imagesTree.indexOfTopLevelItem(itm)) 300 act3.setData(self.imagesTree.indexOfTopLevelItem(itm))
299 act3.triggered.connect(lambda: self.__saveImage(act3)) 301 act3.triggered.connect(lambda: self.__saveImage(act3))
300 menu.exec(self.imagesTree.viewport().mapToGlobal(pos)) 302 menu.exec(self.imagesTree.viewport().mapToGlobal(pos))
301 303
302 def __copyAction(self, act): 304 def __copyAction(self, act):
303 """ 305 """
304 Private slot to copy the image URL or the image name to the clipboard. 306 Private slot to copy the image URL or the image name to the clipboard.
305 307
306 @param act reference to the action that triggered 308 @param act reference to the action that triggered
307 @type QAction 309 @type QAction
308 """ 310 """
309 QApplication.clipboard().setText(act.data()) 311 QApplication.clipboard().setText(act.data())
310 312
311 def __saveImage(self, act): 313 def __saveImage(self, act):
312 """ 314 """
313 Private slot to save the selected image to disk. 315 Private slot to save the selected image to disk.
314 316
315 @param act reference to the action that triggered 317 @param act reference to the action that triggered
316 @type QAction 318 @type QAction
317 """ 319 """
318 index = act.data() 320 index = act.data()
319 itm = self.imagesTree.topLevelItem(index) 321 itm = self.imagesTree.topLevelItem(index)
320 if itm is None: 322 if itm is None:
321 return 323 return
322 324
323 if ( 325 if not self.imagePreview.scene() or len(self.imagePreview.scene().items()) == 0:
324 not self.imagePreview.scene() or 326 return
325 len(self.imagePreview.scene().items()) == 0 327
326 ):
327 return
328
329 pixmapItem = self.imagePreview.scene().items()[0] 328 pixmapItem = self.imagePreview.scene().items()[0]
330 if not isinstance(pixmapItem, QGraphicsPixmapItem): 329 if not isinstance(pixmapItem, QGraphicsPixmapItem):
331 return 330 return
332 331
333 if pixmapItem.pixmap().isNull(): 332 if pixmapItem.pixmap().isNull():
334 EricMessageBox.warning( 333 EricMessageBox.warning(
335 self, 334 self,
336 self.tr("Save Image"), 335 self.tr("Save Image"),
337 self.tr( 336 self.tr("""<p>This preview is not available.</p>"""),
338 """<p>This preview is not available.</p>""")) 337 )
339 return 338 return
340 339
341 imageFileName = WebBrowserTools.getFileNameFromUrl(QUrl(itm.text(1))) 340 imageFileName = WebBrowserTools.getFileNameFromUrl(QUrl(itm.text(1)))
342 index = imageFileName.rfind(".") 341 index = imageFileName.rfind(".")
343 if index != -1: 342 if index != -1:
344 imageFileName = imageFileName[:index] + ".png" 343 imageFileName = imageFileName[:index] + ".png"
345 344
346 filename = EricFileDialog.getSaveFileName( 345 filename = EricFileDialog.getSaveFileName(
347 self, 346 self,
348 self.tr("Save Image"), 347 self.tr("Save Image"),
349 imageFileName, 348 imageFileName,
350 self.tr("All Files (*)"), 349 self.tr("All Files (*)"),
351 EricFileDialog.DontConfirmOverwrite) 350 EricFileDialog.DontConfirmOverwrite,
352 351 )
352
353 if not filename: 353 if not filename:
354 return 354 return
355 355
356 if not pixmapItem.pixmap().save(filename, "PNG"): 356 if not pixmapItem.pixmap().save(filename, "PNG"):
357 EricMessageBox.critical( 357 EricMessageBox.critical(
358 self, 358 self,
359 self.tr("Save Image"), 359 self.tr("Save Image"),
360 self.tr( 360 self.tr("""<p>Cannot write to file <b>{0}</b>.</p>""").format(filename),
361 """<p>Cannot write to file <b>{0}</b>.</p>""") 361 )
362 .format(filename)) 362 return
363 return

eric ide

mercurial