Helpviewer/OpenSearch/OpenSearchEngine.py

changeset 0
de9c2efb9d02
child 12
1d8dd9706f46
equal deleted inserted replaced
-1:000000000000 0:de9c2efb9d02
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2009 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing the open search engine.
8 """
9
10 import re
11 import json
12
13 from PyQt4.QtCore import *
14 from PyQt4.QtGui import *
15 from PyQt4.QtNetwork import QNetworkRequest, QNetworkAccessManager
16
17 from UI.Info import Program
18
19 import Preferences
20
21 class OpenSearchEngine(QObject):
22 """
23 Class implementing the open search engine.
24
25 @signal imageChanged() emitted after the icon has been changed
26 @signal suggestions(const QStringList&) emitted after the suggestions have
27 been received
28 """
29 loc = Preferences.getUILanguage()
30 if loc == "System":
31 loc = QLocale.system().name()
32 if loc is None:
33 _language = "en"
34 elif loc == "C":
35 _language = ""
36 else:
37 _language = loc[:2]
38
39 def __init__(self, parent = None):
40 """
41 Constructor
42
43 @param parent reference to the parent object (QObject)
44 """
45 QObject.__init__(self, parent)
46
47 self.__suggestionsReply = None
48 self.__networkAccessManager = None
49 self._name = ""
50 self._description = ""
51 self._searchUrlTemplate = ""
52 self._suggestionsUrlTemplate = ""
53 self._searchParameters = [] # list of two tuples
54 self._suggestionsParameters = [] # list of two tuples
55 self._imageUrl = ""
56 self.__image = QImage()
57 self.__iconMoved = False
58 self.__searchMethod = "get"
59 self.__suggestionsMethod = "get"
60 self.__requestMethods = {
61 "get" : QNetworkAccessManager.GetOperation,
62 "post" : QNetworkAccessManager.PostOperation,
63 }
64
65 @classmethod
66 def parseTemplate(cls, searchTerm, searchTemplate):
67 """
68 Class method to parse a search template.
69
70 @param searchTerm term to search for (string)
71 @param searchTemplate template to be parsed (string)
72 @return parsed template (string)
73 """
74 result = searchTemplate
75 result = result.replace("{count}", "20")
76 result = result.replace("{startIndex}", "0")
77 result = result.replace("{startPage}", "0")
78 result = result.replace("{language}", cls._language)
79 result = result.replace("{inputEncoding}", "UTF-8")
80 result = result.replace("{outputEncoding}", "UTF-8")
81 result = result.replace("{searchTerms}",
82 unicode(QUrl.toPercentEncoding(searchTerm)))
83 result = re.sub(r"""\{([^\}]*:|)source\??\}""", Program, result)
84
85 return result
86
87 @pyqtSlot(result = str)
88 def name(self):
89 """
90 Public method to get the name of the engine.
91
92 @return name of the engine (string)
93 """
94 return self._name
95
96 def setName(self, name):
97 """
98 Public method to set the engine name.
99
100 @param name name of the engine (string)
101 """
102 self._name = name
103
104 def description(self):
105 """
106 Public method to get the description of the engine.
107
108 @return description of the engine (string)
109 """
110 return self._description
111
112 def setDescription(self, description):
113 """
114 Public method to set the engine description.
115
116 @param description description of the engine (string)
117 """
118 self._description = description
119
120 def searchUrlTemplate(self):
121 """
122 Public method to get the search URL template of the engine.
123
124 @return search URL template of the engine (string)
125 """
126 return self._searchUrlTemplate
127
128 def setSearchUrlTemplate(self, searchUrlTemplate):
129 """
130 Public method to set the engine search URL template.
131
132 @param searchUrlTemplate search URL template of the engine (string)
133 """
134 self._searchUrlTemplate = searchUrlTemplate
135
136 def searchUrl(self, searchTerm):
137 """
138 Public method to get a URL ready for searching.
139
140 @param searchTerm term to search for (string)
141 @return URL (QUrl)
142 """
143 if not self._searchUrlTemplate:
144 return QUrl()
145
146 ret = QUrl.fromEncoded(self.parseTemplate(searchTerm, self._searchUrlTemplate))
147
148 if self.__searchMethod != "post":
149 for parameter in self._searchParameters:
150 ret.addQueryItem(parameter[0],
151 self.parseTemplate(searchTerm, parameter[1]))
152
153 return ret
154
155 def providesSuggestions(self):
156 """
157 Public method to check, if the engine provides suggestions.
158
159 @return flag indicating suggestions are provided (boolean)
160 """
161 return self._suggestionsUrlTemplate != ""
162
163 def suggestionsUrlTemplate(self):
164 """
165 Public method to get the search URL template of the engine.
166
167 @return search URL template of the engine (string)
168 """
169 return self._suggestionsUrlTemplate
170
171 def setSuggestionsUrlTemplate(self, suggestionsUrlTemplate):
172 """
173 Public method to set the engine suggestions URL template.
174
175 @param suggestionsUrlTemplate suggestions URL template of the
176 engine (string)
177 """
178 self._suggestionsUrlTemplate = suggestionsUrlTemplate
179
180 def suggestionsUrl(self, searchTerm):
181 """
182 Public method to get a URL ready for suggestions.
183
184 @param searchTerm term to search for (string)
185 @return URL (QUrl)
186 """
187 if not self._suggestionsUrlTemplate:
188 return QUrl()
189
190 ret = QUrl.fromEncoded(QByteArray(
191 self.parseTemplate(searchTerm, self._suggestionsUrlTemplate)))
192
193 if self.__searchMethod != "post":
194 for parameter in self._suggestionsParameters:
195 ret.addQueryItem(parameter[0],
196 self.parseTemplate(searchTerm, parameter[1]))
197
198 return ret
199
200 def searchParameters(self):
201 """
202 Public method to get the search parameters of the engine.
203
204 @return search parameters of the engine (list of two tuples)
205 """
206 return self._searchParameters[:]
207
208 def setSearchParameters(self, searchParameters):
209 """
210 Public method to set the engine search parameters.
211
212 @param searchParameters search parameters of the engine (list of two tuples)
213 """
214 self._searchParameters = searchParameters[:]
215
216 def suggestionsParameters(self):
217 """
218 Public method to get the suggestions parameters of the engine.
219
220 @return suggestions parameters of the engine (list of two tuples)
221 """
222 return self._suggestionsParameters[:]
223
224 def setSuggestionsParameters(self, suggestionsParameters):
225 """
226 Public method to set the engine suggestions parameters.
227
228 @param suggestionsParameters suggestions parameters of the
229 engine (list of two tuples)
230 """
231 self._suggestionsParameters = suggestionsParameters[:]
232
233 def searchMethod(self):
234 """
235 Public method to get the HTTP request method used to perform search requests.
236
237 @return HTTP request method (string)
238 """
239 return self.__searchMethod
240
241 def setSearchMethod(self, method):
242 """
243 Public method to set the HTTP request method used to perform search requests.
244
245 @param method HTTP request method (string)
246 """
247 requestMethod = method.lower()
248 if requestMethod not in self.__requestMethods:
249 return
250
251 self.__searchMethod = requestMethod
252
253 def suggestionsMethod(self):
254 """
255 Public method to get the HTTP request method used to perform suggestions requests.
256
257 @return HTTP request method (string)
258 """
259 return self.__suggestionsMethod
260
261 def setSuggestionsMethod(self, method):
262 """
263 Public method to set the HTTP request method used to perform suggestions requests.
264
265 @param method HTTP request method (string)
266 """
267 requestMethod = method.lower()
268 if requestMethod not in self.__requestMethods:
269 return
270
271 self.__suggestionsMethod = requestMethod
272
273 def imageUrl(self):
274 """
275 Public method to get the image URL of the engine.
276
277 @return image URL of the engine (string)
278 """
279 return self._imageUrl
280
281 def setImageUrl(self, imageUrl):
282 """
283 Public method to set the engine image URL.
284
285 @param description image URL of the engine (string)
286 """
287 self._imageUrl = imageUrl
288
289 def setImageUrlAndLoad(self, imageUrl):
290 """
291 Public method to set the engine image URL.
292
293 @param description image URL of the engine (string)
294 """
295 self.setImageUrl(imageUrl)
296 self.__iconMoved = False
297 self.loadImage()
298
299 def loadImage(self):
300 """
301 Public method to load the image of the engine.
302 """
303 if self.__networkAccessManager is None or not self._imageUrl:
304 return
305
306 reply = self.__networkAccessManager.get(
307 QNetworkRequest(QUrl.fromEncoded(self._imageUrl)))
308 self.connect(reply, SIGNAL("finished()"), self.__imageObtained)
309
310 def __imageObtained(self):
311 """
312 Private slot to receive the image of the engine.
313 """
314 reply = self.sender()
315 if reply is None:
316 return
317
318 response = reply.readAll()
319
320 reply.close()
321 reply.deleteLater()
322
323 if response.isEmpty():
324 return
325
326 if response.startsWith("<html>") or response.startsWith("HTML"):
327 self.__iconMoved = True
328 self.__image = QImage()
329 else:
330 self.__image.loadFromData(response)
331 self.emit(SIGNAL("imageChanged()"))
332
333 def image(self):
334 """
335 Public method to get the image of the engine.
336
337 @return image of the engine (QImage)
338 """
339 if not self.__iconMoved and self.__image.isNull():
340 self.loadImage()
341
342 return self.__image
343
344 def setImage(self, image):
345 """
346 Public method to set the image of the engine.
347
348 @param image image to be set (QImage)
349 """
350 if not self._imageUrl:
351 imageBuffer = QBuffer()
352 imageBuffer.open(QIODevice.ReadWrite)
353 if image.save(imageBuffer, "PNG"):
354 self._imageUrl = "data:image/png;base64,{0}"\
355 .format(unicode(imageBuffer.buffer().toBase64()))
356
357 self.__image = QImage(image)
358 self.emit(SIGNAL("imageChanged()"))
359
360 def isValid(self):
361 """
362 Public method to check, if the engine is valid.
363
364 @return flag indicating validity (boolean)
365 """
366 return self._name and self._searchUrlTemplate
367
368 def __eq__(self, other):
369 """
370 Public method implementing the == operator.
371
372 @param other reference to an open search engine (OpenSearchEngine)
373 @return flag indicating equality (boolean)
374 """
375 if not isinstance(other, OpenSearchEngine):
376 return NotImplemented
377
378 return self._name == other._name and \
379 self._description == other._description and \
380 self._imageUrl == other._imageUrl and \
381 self._searchUrlTemplate == other._searchUrlTemplate and \
382 self._suggestionsUrlTemplate ==other._suggestionsUrlTemplate and \
383 self._searchParameters == other._searchParameters and \
384 self._suggestionsParameters == other._suggestionsParameters
385
386 def __lt__(self, other):
387 """
388 Public method implementing the < operator.
389
390 @param other reference to an open search engine (OpenSearchEngine)
391 @return flag indicating less than (boolean)
392 """
393 if not isinstance(other, OpenSearchEngine):
394 return NotImplemented
395
396 return self._name < other._name
397
398 def requestSuggestions(self, searchTerm):
399 """
400 Public method to request suggestions.
401
402 @param searchTerm term to get suggestions for (string)
403 """
404 if not searchTerm or not self.providesSuggestions():
405 return
406
407 if self.__networkAccessManager is None:
408 return
409
410 if self.__suggestionsReply is not None:
411 self.__suggestionsReply.disconnect(self)
412 self.__suggestionsReply.abort()
413 self.__suggestionsReply = None
414
415 if self.__suggestionsMethod not in self.__requestMethods:
416 # ignore
417 return
418
419 if self.__suggestionsMethod == "get":
420 self.__suggestionsReply = self.networkAccessManager().get(
421 QNetworkRequest(self.suggestionsUrl(searchTerm)))
422 else:
423 parameters = []
424 for parameter in self._suggestionsParameters:
425 parameters.append(parameter[0] + "=" + parameter[1])
426 data = "&".join(parameters)
427 self.__suggestionsReply = self.networkAccessManager().post(
428 QNetworkRequest(self.suggestionsUrl(searchTerm)), data)
429 self.connect(self.__suggestionsReply, SIGNAL("finished()"),
430 self.__suggestionsObtained)
431
432 def __suggestionsObtained(self):
433 """
434 Private slot to receive the suggestions.
435 """
436 response = unicode(self.__suggestionsReply.readAll(), "utf-8")
437 response = response.strip()
438
439 self.__suggestionsReply.close()
440 self.__suggestionsReply = None
441
442 if len(response) == 0:
443 return
444
445 try:
446 result = json.loads(response)
447 except ValueError:
448 return
449
450 try:
451 suggestions = result[1]
452 except IndexError:
453 return
454
455 self.emit(SIGNAL("suggestions(const QStringList&)"), suggestions)
456
457 def networkAccessManager(self):
458 """
459 Public method to get a reference to the network access manager object.
460
461 @return reference to the network access manager object (QNetworkAccessManager)
462 """
463 return self.__networkAccessManager
464
465 def setNetworkAccessManager(self, networkAccessManager):
466 """
467 Public method to set the reference to the network access manager.
468
469 @param networkAccessManager reference to the network access manager
470 object (QNetworkAccessManager)
471 """
472 self.__networkAccessManager = networkAccessManager

eric ide

mercurial