|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing a scheme access handler for QtHelp. |
|
8 """ |
|
9 |
|
10 from __future__ import unicode_literals |
|
11 |
|
12 import mimetypes |
|
13 import os |
|
14 |
|
15 from PyQt5.QtCore import pyqtSignal, QByteArray, QIODevice, QBuffer |
|
16 from PyQt5.QtWebEngineCore import QWebEngineUrlSchemeHandler, \ |
|
17 QWebEngineUrlRequestJob |
|
18 |
|
19 ##from .SchemeAccessHandler import SchemeAccessHandler |
|
20 ## |
|
21 ##from .NetworkReply import NetworkReply |
|
22 ## |
|
23 QtDocPath = "qthelp://org.qt-project." |
|
24 |
|
25 ExtensionMap = { |
|
26 ".bmp": "image/bmp", |
|
27 ".css": "text/css", |
|
28 ".gif": "image/gif", |
|
29 ".html": "text/html", |
|
30 ".htm": "text/html", |
|
31 ".ico": "image/x-icon", |
|
32 ".jpeg": "image/jpeg", |
|
33 ".jpg": "image/jpeg", |
|
34 ".js": "application/x-javascript", |
|
35 ".mng": "video/x-mng", |
|
36 ".pbm": "image/x-portable-bitmap", |
|
37 ".pgm": "image/x-portable-graymap", |
|
38 ".pdf": "application/pdf", |
|
39 ".png": "image/png", |
|
40 ".ppm": "image/x-portable-pixmap", |
|
41 ".rss": "application/rss+xml", |
|
42 ".svg": "image/svg+xml", |
|
43 ".svgz": "image/svg+xml", |
|
44 ".text": "text/plain", |
|
45 ".tif": "image/tiff", |
|
46 ".tiff": "image/tiff", |
|
47 ".txt": "text/plain", |
|
48 ".xbm": "image/x-xbitmap", |
|
49 ".xml": "text/xml", |
|
50 ".xpm": "image/x-xpm", |
|
51 ".xsl": "text/xsl", |
|
52 ".xhtml": "application/xhtml+xml", |
|
53 ".wml": "text/vnd.wap.wml", |
|
54 ".wmlc": "application/vnd.wap.wmlc", |
|
55 } |
|
56 |
|
57 |
|
58 class QtHelpSchemeHandler(QWebEngineUrlSchemeHandler): |
|
59 """ |
|
60 Class implementing a scheme handler for the qthelp: scheme. |
|
61 """ |
|
62 def __init__(self, engine, parent=None): |
|
63 """ |
|
64 Constructor |
|
65 |
|
66 @param engine reference to the help engine |
|
67 @type QHelpEngine |
|
68 @param parent reference to the parent object |
|
69 @type QObject |
|
70 """ |
|
71 super(QtHelpSchemeHandler, self).__init__(parent) |
|
72 |
|
73 self.__engine = engine |
|
74 |
|
75 self.__replies = [] |
|
76 |
|
77 def requestStarted(self, job): |
|
78 """ |
|
79 Public method handling the URL request. |
|
80 |
|
81 @param job URL request job |
|
82 @type QWebEngineUrlRequestJob |
|
83 """ |
|
84 if job.requestUrl().scheme() == "qthelp": |
|
85 reply = QtHelpSchemeReply(job, self.__engine) |
|
86 reply.closed.connect(self.__replyClosed) |
|
87 self.__replies.append(reply) |
|
88 job.reply(reply.mimeType(), reply) |
|
89 else: |
|
90 job.fail(QWebEngineUrlRequestJob.UrlInvalid) |
|
91 |
|
92 def __replyClosed(self): |
|
93 """ |
|
94 Private slot handling the closed signal of a reply. |
|
95 """ |
|
96 object = self.sender() |
|
97 if object and object in self.__replies: |
|
98 self.__replies.remove(object) |
|
99 |
|
100 |
|
101 class QtHelpSchemeReply(QIODevice): |
|
102 """ |
|
103 Class implementing a reply for a requested qthelp: page. |
|
104 |
|
105 @signal closed emitted to signal that the web engine has read |
|
106 the data |
|
107 """ |
|
108 closed = pyqtSignal() |
|
109 |
|
110 def __init__(self, job, engine, parent=None): |
|
111 """ |
|
112 Constructor |
|
113 |
|
114 @param job reference to the URL request |
|
115 @type QWebEngineUrlRequestJob |
|
116 @param engine reference to the help engine |
|
117 @type QHelpEngine |
|
118 @param parent reference to the parent object |
|
119 @type QObject |
|
120 """ |
|
121 super(QtHelpSchemeReply, self).__init__(parent) |
|
122 |
|
123 url = job.requestUrl() |
|
124 strUrl = url.toString() |
|
125 |
|
126 self.__buffer = QBuffer() |
|
127 |
|
128 # For some reason the url to load maybe wrong (passed from web engine) |
|
129 # though the css file and the references inside should work that way. |
|
130 # One possible problem might be that the css is loaded at the same |
|
131 # level as the html, thus a path inside the css like |
|
132 # (../images/foo.png) might cd out of the virtual folder |
|
133 if not engine.findFile(url).isValid(): |
|
134 if strUrl.startswith(QtDocPath): |
|
135 newUrl = job.requestUrl() |
|
136 if not newUrl.path().startswith("/qdoc/"): |
|
137 newUrl.setPath("/qdoc" + newUrl.path()) |
|
138 url = newUrl |
|
139 strUrl = url.toString() |
|
140 |
|
141 self.__mimeType = mimetypes.guess_type(strUrl)[0] |
|
142 if self.__mimeType is None: |
|
143 # do our own (limited) guessing |
|
144 self.__mimeType = self.__mimeFromUrl(url) |
|
145 |
|
146 if engine.findFile(url).isValid(): |
|
147 data = engine.fileData(url) |
|
148 else: |
|
149 data = QByteArray(self.tr( |
|
150 """<html>""" |
|
151 """<head><title>Error 404...</title></head>""" |
|
152 """<body><div align="center"><br><br>""" |
|
153 """<h1>The page could not be found</h1><br>""" |
|
154 """<h3>'{0}'</h3></div></body>""" |
|
155 """</html>""").format(strUrl) |
|
156 .encode("utf-8")) |
|
157 |
|
158 self.__buffer.setData(data) |
|
159 self.__buffer.open(QIODevice.ReadOnly) |
|
160 self.open(QIODevice.ReadOnly) |
|
161 |
|
162 def bytesAvailable(self): |
|
163 """ |
|
164 Public method to get the number of available bytes. |
|
165 |
|
166 @return number of available bytes |
|
167 @rtype int |
|
168 """ |
|
169 return self.__buffer.bytesAvailable() |
|
170 |
|
171 def readData(self, maxlen): |
|
172 """ |
|
173 Public method to retrieve data from the reply object. |
|
174 |
|
175 @param maxlen maximum number of bytes to read (integer) |
|
176 @return string containing the data (bytes) |
|
177 """ |
|
178 return self.__buffer.read(maxlen) |
|
179 |
|
180 def close(self): |
|
181 """ |
|
182 Public method used to cloase the reply. |
|
183 """ |
|
184 super(QtHelpSchemeReply, self).close() |
|
185 self.closed.emit() |
|
186 |
|
187 def __mimeFromUrl(self, url): |
|
188 """ |
|
189 Private method to guess the mime type given an URL. |
|
190 |
|
191 @param url URL to guess the mime type from (QUrl) |
|
192 @return mime type for the given URL (string) |
|
193 """ |
|
194 path = url.path() |
|
195 ext = os.path.splitext(path)[1].lower() |
|
196 if ext in ExtensionMap: |
|
197 return ExtensionMap[ext] |
|
198 else: |
|
199 return "application/octet-stream" |
|
200 |
|
201 def mimeType(self): |
|
202 """ |
|
203 Public method to get the reply mime type. |
|
204 |
|
205 @return mime type of the reply |
|
206 @rtype bytes |
|
207 """ |
|
208 return self.__mimeType.encode("utf-8") |
|
209 ## |
|
210 ## |
|
211 ## |
|
212 ## |
|
213 ## |
|
214 ##class QtHelpAccessHandler(SchemeAccessHandler): |
|
215 ## """ |
|
216 ## Class implementing a scheme access handler for QtHelp. |
|
217 ## """ |
|
218 ## def __init__(self, engine, parent=None): |
|
219 ## """ |
|
220 ## Constructor |
|
221 ## |
|
222 ## @param engine reference to the help engine (QHelpEngine) |
|
223 ## @param parent reference to the parent object (QObject) |
|
224 ## """ |
|
225 ## SchemeAccessHandler.__init__(self, parent) |
|
226 ## |
|
227 ## self.__engine = engine |
|
228 ## |
|
229 ## def __mimeFromUrl(self, url): |
|
230 ## """ |
|
231 ## Private method to guess the mime type given an URL. |
|
232 ## |
|
233 ## @param url URL to guess the mime type from (QUrl) |
|
234 ## @return mime type for the given URL (string) |
|
235 ## """ |
|
236 ## path = url.path() |
|
237 ## ext = os.path.splitext(path)[1].lower() |
|
238 ## if ext in ExtensionMap: |
|
239 ## return ExtensionMap[ext] |
|
240 ## else: |
|
241 ## return "application/octet-stream" |
|
242 ## |
|
243 ## def createRequest(self, op, request, outgoingData=None): |
|
244 ## """ |
|
245 ## Public method to create a request. |
|
246 ## |
|
247 ## @param op the operation to be performed |
|
248 ## (QNetworkAccessManager.Operation) |
|
249 ## @param request reference to the request object (QNetworkRequest) |
|
250 ## @param outgoingData reference to an IODevice containing data to be sent |
|
251 ## (QIODevice) |
|
252 ## @return reference to the created reply object (QNetworkReply) |
|
253 ## """ |
|
254 ## url = request.url() |
|
255 ## strUrl = url.toString() |
|
256 ## |
|
257 ## # For some reason the url to load is already wrong (passed from webkit) |
|
258 ## # though the css file and the references inside should work that way. |
|
259 ## # One possible problem might be that the css is loaded at the same |
|
260 ## # level as the html, thus a path inside the css like |
|
261 ## # (../images/foo.png) might cd out of the virtual folder |
|
262 ## if not self.__engine.findFile(url).isValid(): |
|
263 ## if strUrl.startswith(QtDocPath): |
|
264 ## newUrl = request.url() |
|
265 ## if not newUrl.path().startswith("/qdoc/"): |
|
266 ## newUrl.setPath("qdoc" + newUrl.path()) |
|
267 ## url = newUrl |
|
268 ## strUrl = url.toString() |
|
269 ## |
|
270 ## mimeType = mimetypes.guess_type(strUrl)[0] |
|
271 ## if mimeType is None: |
|
272 ## # do our own (limited) guessing |
|
273 ## mimeType = self.__mimeFromUrl(url) |
|
274 ## |
|
275 ## if self.__engine.findFile(url).isValid(): |
|
276 ## data = self.__engine.fileData(url) |
|
277 ## else: |
|
278 ## data = QByteArray(self.tr( |
|
279 ## """<title>Error 404...</title>""" |
|
280 ## """<div align="center"><br><br>""" |
|
281 ## """<h1>The page could not be found</h1><br>""" |
|
282 ## """<h3>'{0}'</h3></div>""").format(strUrl).encode("utf-8")) |
|
283 ## return NetworkReply(request, data, mimeType, self.parent()) |