|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2016 - 2021 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module containing a web site icon storage object. |
|
8 """ |
|
9 |
|
10 import json |
|
11 import os |
|
12 import contextlib |
|
13 |
|
14 from PyQt5.QtCore import ( |
|
15 pyqtSignal, QObject, QByteArray, QBuffer, QIODevice, QUrl |
|
16 ) |
|
17 from PyQt5.QtGui import QIcon, QPixmap, QImage |
|
18 from PyQt5.QtWidgets import QDialog |
|
19 |
|
20 from Utilities.AutoSaver import AutoSaver |
|
21 |
|
22 import UI.PixmapCache |
|
23 |
|
24 |
|
25 class WebIconProvider(QObject): |
|
26 """ |
|
27 Class implementing a web site icon storage. |
|
28 |
|
29 @signal changed() emitted to indicate a change of the icons database |
|
30 """ |
|
31 changed = pyqtSignal() |
|
32 |
|
33 def __init__(self, parent=None): |
|
34 """ |
|
35 Constructor |
|
36 |
|
37 @param parent reference to the parent object (QObject) |
|
38 """ |
|
39 super().__init__(parent) |
|
40 |
|
41 self.__encoding = "iso-8859-1" |
|
42 self.__iconsFileName = "web_site_icons.json" |
|
43 self.__iconDatabasePath = "" # saving of icons disabled |
|
44 |
|
45 self.__iconsDB = {} |
|
46 self.__loaded = False |
|
47 |
|
48 self.__saveTimer = AutoSaver(self, self.save) |
|
49 |
|
50 self.changed.connect(self.__saveTimer.changeOccurred) |
|
51 |
|
52 def setIconDatabasePath(self, path): |
|
53 """ |
|
54 Public method to set the path for the web site icons store. |
|
55 |
|
56 @param path path to store the icons file to |
|
57 @type str |
|
58 """ |
|
59 if path != self.__iconDatabasePath: |
|
60 self.close() |
|
61 |
|
62 self.__iconDatabasePath = path |
|
63 |
|
64 def iconDatabasePath(self): |
|
65 """ |
|
66 Public method o get the path for the web site icons store. |
|
67 |
|
68 @return path to store the icons file to |
|
69 @rtype str |
|
70 """ |
|
71 return self.__iconDatabasePath |
|
72 |
|
73 def close(self): |
|
74 """ |
|
75 Public method to close the web icon provider. |
|
76 """ |
|
77 self.__saveTimer.saveIfNeccessary() |
|
78 self.__loaded = False |
|
79 self.__iconsDB = {} |
|
80 |
|
81 def load(self): |
|
82 """ |
|
83 Public method to load the web site icons. |
|
84 """ |
|
85 if self.__loaded: |
|
86 return |
|
87 |
|
88 if self.__iconDatabasePath: |
|
89 filename = os.path.join(self.__iconDatabasePath, |
|
90 self.__iconsFileName) |
|
91 try: |
|
92 with open(filename, "r") as f: |
|
93 db = json.load(f) |
|
94 except OSError: |
|
95 # ignore silentyl |
|
96 db = {} |
|
97 |
|
98 self.__iconsDB = {} |
|
99 for url, data in db.items(): |
|
100 self.__iconsDB[url] = QIcon(QPixmap.fromImage(QImage.fromData( |
|
101 QByteArray(data.encode(self.__encoding))))) |
|
102 |
|
103 self.__loaded = True |
|
104 |
|
105 def save(self): |
|
106 """ |
|
107 Public method to save the web site icons. |
|
108 """ |
|
109 if not self.__loaded: |
|
110 return |
|
111 |
|
112 from WebBrowser.WebBrowserWindow import WebBrowserWindow |
|
113 if not WebBrowserWindow.isPrivate() and bool(self.__iconDatabasePath): |
|
114 db = {} |
|
115 for url, icon in self.__iconsDB.items(): |
|
116 ba = QByteArray() |
|
117 buffer = QBuffer(ba) |
|
118 buffer.open(QIODevice.OpenModeFlag.WriteOnly) |
|
119 icon.pixmap(32).toImage().save(buffer, "PNG") |
|
120 db[url] = bytes(buffer.data()).decode(self.__encoding) |
|
121 |
|
122 filename = os.path.join(self.__iconDatabasePath, |
|
123 self.__iconsFileName) |
|
124 with contextlib.suppress(OSError), open(filename, "w") as f: |
|
125 json.dump(db, f) |
|
126 |
|
127 def saveIcon(self, view): |
|
128 """ |
|
129 Public method to save a web site icon. |
|
130 |
|
131 @param view reference to the view object |
|
132 @type WebBrowserView |
|
133 """ |
|
134 scheme = view.url().scheme() |
|
135 if scheme in ["eric", "about", "qthelp", "file", "abp", "ftp"]: |
|
136 return |
|
137 |
|
138 self.load() |
|
139 |
|
140 if view.mainWindow().isPrivate(): |
|
141 return |
|
142 |
|
143 urlStr = self.__urlToString(view.url()) |
|
144 self.__iconsDB[urlStr] = view.icon() |
|
145 |
|
146 self.changed.emit() |
|
147 |
|
148 def __urlToString(self, url): |
|
149 """ |
|
150 Private method to convert an URL to a string. |
|
151 |
|
152 @param url URL to be converted |
|
153 @type QUrl |
|
154 @return string representation of the URL |
|
155 @rtype str |
|
156 """ |
|
157 return url.toString( |
|
158 QUrl.ComponentFormattingOption.PrettyDecoded | |
|
159 QUrl.UrlFormattingOption.RemoveUserInfo | |
|
160 QUrl.UrlFormattingOption.RemoveFragment | |
|
161 QUrl.UrlFormattingOption.RemovePath |
|
162 ) |
|
163 |
|
164 def iconForUrl(self, url): |
|
165 """ |
|
166 Public method to get an icon for an URL. |
|
167 |
|
168 @param url URL to get icon for |
|
169 @type QUrl |
|
170 @return icon for the URL |
|
171 @rtype QIcon |
|
172 """ |
|
173 scheme2iconName = { |
|
174 "eric": "ericWeb", |
|
175 "about": "ericWeb", |
|
176 "qthelp": "qthelp", |
|
177 "file": "fileMisc", |
|
178 "abp": "adBlockPlus", |
|
179 "ftp": "network-server", |
|
180 } |
|
181 |
|
182 scheme = url.scheme() |
|
183 iconName = scheme2iconName.get(scheme) |
|
184 if iconName: |
|
185 return UI.PixmapCache.getIcon(iconName) |
|
186 |
|
187 self.load() |
|
188 |
|
189 urlStr = self.__urlToString(url) |
|
190 if urlStr in self.__iconsDB: |
|
191 return self.__iconsDB[urlStr] |
|
192 else: |
|
193 for iconUrlStr in self.__iconsDB: |
|
194 if iconUrlStr.startswith(urlStr): |
|
195 return self.__iconsDB[iconUrlStr] |
|
196 |
|
197 # try replacing http scheme with https scheme |
|
198 url = QUrl(url) |
|
199 if url.scheme() == "http": |
|
200 url.setScheme("https") |
|
201 urlStr = self.__urlToString(url) |
|
202 if urlStr in self.__iconsDB: |
|
203 return self.__iconsDB[urlStr] |
|
204 else: |
|
205 for iconUrlStr in self.__iconsDB: |
|
206 if iconUrlStr.startswith(urlStr): |
|
207 return self.__iconsDB[iconUrlStr] |
|
208 |
|
209 if scheme == "https": |
|
210 return UI.PixmapCache.getIcon("securityHigh32") |
|
211 else: |
|
212 return UI.PixmapCache.getIcon("defaultIcon") |
|
213 |
|
214 def clear(self): |
|
215 """ |
|
216 Public method to clear the icons cache. |
|
217 """ |
|
218 self.load() |
|
219 self.__iconsDB = {} |
|
220 self.changed.emit() |
|
221 self.__saveTimer.saveIfNeccessary() |
|
222 |
|
223 def showWebIconDialog(self): |
|
224 """ |
|
225 Public method to show a dialog to manage the Favicons. |
|
226 """ |
|
227 self.load() |
|
228 |
|
229 from .WebIconDialog import WebIconDialog |
|
230 dlg = WebIconDialog(self.__iconsDB) |
|
231 if dlg.exec() == QDialog.DialogCode.Accepted: |
|
232 changed = False |
|
233 urls = dlg.getUrls() |
|
234 for url in list(self.__iconsDB.keys())[:]: |
|
235 if url not in urls: |
|
236 del self.__iconsDB[url] |
|
237 changed = True |
|
238 if changed: |
|
239 self.changed.emit() |
|
240 |
|
241 |
|
242 _WebIconProvider = None |
|
243 |
|
244 |
|
245 def instance(): |
|
246 """ |
|
247 Global function to get a reference to the web icon provider and create it, |
|
248 if it hasn't been yet. |
|
249 |
|
250 @return reference to the web icon provider object |
|
251 @rtype WebIconProvider |
|
252 """ |
|
253 global _WebIconProvider |
|
254 |
|
255 if _WebIconProvider is None: |
|
256 _WebIconProvider = WebIconProvider() |
|
257 |
|
258 return _WebIconProvider |