Helpviewer/SpeedDial/SpeedDial.py

changeset 1670
6fd889391d2c
child 1699
10706f6ad9d2
equal deleted inserted replaced
1667:02ad912c8645 1670:6fd889391d2c
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2012 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing the speed dial.
8 """
9
10 import os
11
12 from PyQt4.QtCore import pyqtSignal, pyqtSlot, QObject, QCryptographicHash, QByteArray, \
13 QUrl, qWarning
14 from PyQt4.QtWebKit import QWebPage
15
16 from .PageThumbnailer import PageThumbnailer
17
18 import Preferences
19 import Utilities
20
21
22 class Page(object):
23 """
24 Class to hold the data for a speed dial page.
25 """
26 def __init__(self, url="", title="", broken=False):
27 """
28 Constructor
29
30 @param url URL of the page (string)
31 @param title title of the page (string)
32 """
33 self.url = url
34 self.title = title
35 self.broken = broken
36
37 def __eq__(self, other):
38 """
39 Public method implementing the equality operator.
40
41 @param other reference to the other page object (Page)
42 @return flag indicating equality (boolean)
43 """
44 return self.title == other.title and \
45 self.url == other.url
46
47
48 class SpeedDial(QObject):
49 """
50 Class implementing the speed dial.
51
52 @signal pagesChanged() emitted after the list of pages changed
53 """
54 pagesChanged = pyqtSignal()
55
56 def __init__(self, parent=None):
57 """
58 Constructor
59
60 @param parent reference to the parent object (QObject)
61 """
62 super().__init__(parent)
63
64 self.__regenerateScript = True
65
66 self.__webPages = []
67 self.__webFrames = []
68
69 self.__initialScript = ""
70 self.__thumbnailsDirectory = ""
71
72 self.__thumbnailers = []
73
74 self.__initialize()
75
76 self.pagesChanged.connect(self.__pagesChanged)
77
78 def addWebFrame(self, frame):
79 """
80 Public method to add a web frame.
81
82 @param frame reference to the frame to be added (QWebFrame)
83 """
84 if frame not in self.__webFrames:
85 self.__webFrames.append(frame)
86
87 def addPage(self, url, title):
88 """
89 Public method to add a page for the given data.
90
91 @param url URL of the page (QUrl)
92 @param title title of the page (string)
93 """
94 if url.isEmpty():
95 return
96
97 page = Page(url.toString(), title)
98 self.__webPages.append(page)
99
100 self.pagesChanged.emit()
101
102 def removePage(self, url):
103 """
104 Public method to remove a page.
105
106 @param url URL of the page (QUrl)
107 """
108 page = self.pageForUrl(url)
109 if not page.url:
110 return
111
112 self.removeImageForUrl(page.url)
113 self.__webPages.remove(page)
114
115 self.pagesChanged.emit()
116
117 def __imageFileName(self, url):
118 """
119 Private method to generate the image file name for a URL.
120
121 @param url URL to generate the file name from (string)
122 @return name of the image file (string)
123 """
124 return os.path.join(self.__thumbnailsDirectory,
125 str(QCryptographicHash.hash(QByteArray(url.encode("utf-8")),
126 QCryptographicHash.Md5).toHex(), encoding="utf-8") + ".png")
127
128 def initialScript(self):
129 """
130 Public method to get the 'initial' JavaScript script.
131
132 @return initial JavaScript script (string)
133 """
134 if self.__regenerateScript:
135 self.__regenerateScript = False
136 self.__initialScript = ""
137
138 for page in self.__webPages:
139 if page.broken:
140 imgSource = "qrc:icons/brokenPage.png"
141 else:
142 imgSource = self.__imageFileName(page.url)
143 if not os.path.exists(imgSource):
144 self.loadThumbnail(page.url)
145 imgSource = "qrc:icons/loading.gif"
146
147 if not page.url:
148 imgSource = ""
149 else:
150 imgSource = QUrl.fromLocalFile(imgSource).toString()
151
152 self.__initialScript += "addBox('{0}', '{1}', '{2}');\n".format(
153 page.url, page.title, imgSource)
154
155 return self.__initialScript
156
157 def __initialize(self):
158 """
159 Private method to initialize the speed dial.
160 """
161 allPages = Preferences.Prefs.settings.value("Help/SpeedDial/Pages", "")
162
163 if not allPages:
164 allPages = \
165 'url:"http://eric-ide.python-projects.org/"|title:"Eric Web Site";'\
166 'url:"http://www.riverbankcomputing.com/"|title:"PyQt4 Web Site";'\
167 'url:"http://qt.nokia.com/"|title:"Qt Web Site";'\
168 'url:"http://www.python.org"|title:"Python Language Website";'\
169 'url:"http://www.google.com"|title:"Google";'
170 self.changed(allPages)
171
172 self.__thumbnailsDirectory = os.path.join(
173 Utilities.getConfigDir(), "browser", "thumbnails")
174 # Create directory if it does not exist yet
175 if not os.path.exists(self.__thumbnailsDirectory):
176 os.makedirs(self.__thumbnailsDirectory)
177
178 def pageForUrl(self, url):
179 """
180 Public method to get the page for the given URL.
181
182 @param url URL to be searched for (QUrl)
183 @return page for the URL (Page)
184 """
185 urlString = url.toString()
186 for page in self.__webPages:
187 if page.url == urlString:
188 return page
189
190 return Page()
191
192 def urlForShortcut(self, key):
193 """
194 Public method to get the URL for the given shortcut key.
195
196 @param key shortcut key (integer)
197 @return URL for the key (QUrl)
198 """
199 if key < 0 or len(self.__webPages) <= key:
200 return QUrl()
201
202 return QUrl.fromEncoded(self.__webPages[key].url.encode("utf-8"))
203
204 @pyqtSlot(str)
205 def changed(self, allPages):
206 """
207 Public slot to react on changed pages.
208
209 @param allPages string giving all pages (string)
210 """
211 if not allPages:
212 return
213
214 entries = allPages.split('";')
215 self.__webPages = []
216
217 for entry in entries:
218 if not entry:
219 continue
220
221 tmp = entry.split('"|')
222 if len(tmp) == 2:
223 broken = False
224 elif len(tmp) == 3:
225 broken = "brokenPage" in tmp[2][5:]
226 else:
227 continue
228
229 page = Page(tmp[0][5:], tmp[1][7:], broken)
230 self.__webPages.append(page)
231
232 self.pagesChanged.emit()
233
234 @pyqtSlot(str)
235 @pyqtSlot(str, bool)
236 def loadThumbnail(self, url, loadTitle=False):
237 """
238 Public slot to load a thumbnail of the given URL.
239
240 @param url URL of the thumbnail (string)
241 @param loadTitle flag indicating to get the title for the thumbnail
242 from the site (boolean)
243 """
244 if not url:
245 return
246
247 thumbnailer = PageThumbnailer(self)
248 thumbnailer.setUrl(QUrl.fromEncoded(url.encode("utf-8")))
249 thumbnailer.setLoadTitle(loadTitle)
250 thumbnailer.thumbnailCreated.connect(self.__thumbnailCreated)
251 self.__thumbnailers.append(thumbnailer)
252
253 thumbnailer.start()
254
255 @pyqtSlot(str)
256 def removeImageForUrl(self, url):
257 """
258 Public slot to remove the image for a URL.
259
260 @param url URL to remove the image for (string)
261 """
262 fileName = self.__imageFileName(url)
263 if os.path.exists(fileName):
264 os.remove(fileName)
265
266 @pyqtSlot(str, result=str)
267 def urlFromUserInput(self, url):
268 """
269 Public slot to get the URL from user input.
270
271 @param url URL entered by the user (string)
272 @return sanitized URL (string)
273 """
274 return QUrl.fromUserInput(url).toString()
275
276 @pyqtSlot(int)
277 def setPagesInRow(self, count):
278 """
279 Public slot to set the number of pages per row.
280
281 @param count number of pages per row (integer)
282 """
283 Preferences.Prefs.settings.setValue("Help/SpeedDial/PagesPerRow", count)
284
285 def pagesInRow(self):
286 """
287 Public method to get the number of dials per row.
288
289 @return number of dials per row (integer)
290 """
291 return int(
292 Preferences.Prefs.settings.value("Help/SpeedDial/PagesPerRow", 4))
293
294 @pyqtSlot(int)
295 def setSdSize(self, size):
296 """
297 Public slot to set the size of the speed dial.
298
299 @param size size of the speed dial (integer)
300 """
301 Preferences.Prefs.settings.setValue("Help/SpeedDial/SpeedDialSize", size)
302
303 def sdSize(self):
304 """
305 Public method to get the speed dial size.
306
307 @return speed dial size (integer)
308 """
309 return int(
310 Preferences.Prefs.settings.value("Help/SpeedDial/SpeedDialSize", 231))
311
312 def __thumbnailCreated(self, image):
313 """
314 Private slot to handle the creation of a thumbnail image.
315
316 @param image thumbnail image (QPixmap)
317 """
318 thumbnailer = self.sender()
319 if not isinstance(thumbnailer, PageThumbnailer) or \
320 thumbnailer not in self.__thumbnailers:
321 return
322
323 loadTitle = thumbnailer.loadTitle()
324 title = thumbnailer.title()
325 url = thumbnailer.url().toString()
326 fileName = self.__imageFileName(url)
327
328 if image.isNull():
329 fileName = "qrc:icons/brokenPage.png"
330 title = self.trUtf8("Unable to load")
331 loadTitle = True
332 page = self.pageForUrl(thumbnailer.url())
333 page.broken = True
334 else:
335 if not image.save(fileName):
336 qWarning("SpeedDial.__thumbnailCreated: Cannot save thumbnail to {0}"
337 .format(fileName))
338
339 fileName = QUrl.fromLocalFile(fileName).toString()
340
341 self.__regenerateScript = True
342
343 for frame in self.__cleanFrames():
344 frame.evaluateJavaScript("setImageToUrl('{0}', '{1}');".format(
345 url, fileName))
346 if loadTitle:
347 frame.evaluateJavaScript("setTitleToUrl('{0}', '{1}');".format(
348 url, title))
349
350 thumbnailer.thumbnailCreated.disconnect(self.__thumbnailCreated)
351 self.__thumbnailers.remove(thumbnailer)
352
353 def __cleanFrames(self):
354 """
355 Private method to clean all frames.
356
357 @return list of speed dial frames (list of QWebFrame)
358 """
359 frames = []
360
361 for frame in self.__webFrames[:]:
362 if frame.url().toString() == "eric:speeddial":
363 frames.append(frame)
364 else:
365 self.__webFrames.remove(frame)
366
367 return frames
368
369 def __pagesChanged(self):
370 """
371 Private slot to react on a change of the pages configuration.
372 """
373 # step 1: save the list of pages
374 Preferences.Prefs.settings.setValue(
375 "Help/SpeedDial/Pages", self.__generateAllPages())
376
377 # step 2: update all speed dial pages
378 self.__regenerateScript = True
379 for frame in self.__cleanFrames():
380 frame.page().triggerAction(QWebPage.Reload)
381
382 def __generateAllPages(self):
383 """
384 Private method to generate s string with all pages managed by the speed dial.
385
386 @return string with all pages (string)
387 """
388 allPages = ""
389
390 for page in self.__webPages:
391 entry = 'url:"{0}"|title:"{1}";'.format(page.url, page.title)
392 allPages += entry
393
394 return allPages

eric ide

mercurial