eric6/WebBrowser/Network/QtHelpSchemeHandler.py

changeset 6942
2602857055c5
parent 6645
ad476851d7e0
child 6989
8b8cadf8d7e9
child 7229
53054eb5b15a
equal deleted inserted replaced
6941:f99d60d6b59b 6942:2602857055c5
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2009 - 2019 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, QMutex, \
16 QMutexLocker
17 from PyQt5.QtWebEngineCore import QWebEngineUrlSchemeHandler, \
18 QWebEngineUrlRequestJob
19
20 QtDocPath = "qthelp://org.qt-project."
21
22 ExtensionMap = {
23 ".bmp": "image/bmp",
24 ".css": "text/css",
25 ".gif": "image/gif",
26 ".html": "text/html",
27 ".htm": "text/html",
28 ".ico": "image/x-icon",
29 ".jpeg": "image/jpeg",
30 ".jpg": "image/jpeg",
31 ".js": "application/x-javascript",
32 ".mng": "video/x-mng",
33 ".pbm": "image/x-portable-bitmap",
34 ".pgm": "image/x-portable-graymap",
35 ".pdf": "application/pdf",
36 ".png": "image/png",
37 ".ppm": "image/x-portable-pixmap",
38 ".rss": "application/rss+xml",
39 ".svg": "image/svg+xml",
40 ".svgz": "image/svg+xml",
41 ".text": "text/plain",
42 ".tif": "image/tiff",
43 ".tiff": "image/tiff",
44 ".txt": "text/plain",
45 ".xbm": "image/x-xbitmap",
46 ".xml": "text/xml",
47 ".xpm": "image/x-xpm",
48 ".xsl": "text/xsl",
49 ".xhtml": "application/xhtml+xml",
50 ".wml": "text/vnd.wap.wml",
51 ".wmlc": "application/vnd.wap.wmlc",
52 }
53
54
55 class QtHelpSchemeHandler(QWebEngineUrlSchemeHandler):
56 """
57 Class implementing a scheme handler for the qthelp: scheme.
58 """
59 def __init__(self, engine, parent=None):
60 """
61 Constructor
62
63 @param engine reference to the help engine
64 @type QHelpEngine
65 @param parent reference to the parent object
66 @type QObject
67 """
68 super(QtHelpSchemeHandler, self).__init__(parent)
69
70 self.__engine = engine
71
72 self.__replies = []
73
74 def requestStarted(self, job):
75 """
76 Public method handling the URL request.
77
78 @param job URL request job
79 @type QWebEngineUrlRequestJob
80 """
81 if job.requestUrl().scheme() == "qthelp":
82 reply = QtHelpSchemeReply(job, self.__engine)
83 reply.closed.connect(lambda: self.__replyClosed(reply))
84 self.__replies.append(reply)
85 job.reply(reply.mimeType(), reply)
86 else:
87 job.fail(QWebEngineUrlRequestJob.UrlInvalid)
88
89 def __replyClosed(self, reply):
90 """
91 Private slot handling the closed signal of a reply.
92
93 @param reply reference to the network reply
94 @type QtHelpSchemeReply
95 """
96 if reply in self.__replies:
97 self.__replies.remove(reply)
98
99
100 class QtHelpSchemeReply(QIODevice):
101 """
102 Class implementing a reply for a requested qthelp: page.
103
104 @signal closed emitted to signal that the web engine has read
105 the data
106 """
107 closed = pyqtSignal()
108
109 def __init__(self, job, engine, parent=None):
110 """
111 Constructor
112
113 @param job reference to the URL request
114 @type QWebEngineUrlRequestJob
115 @param engine reference to the help engine
116 @type QHelpEngine
117 @param parent reference to the parent object
118 @type QObject
119 """
120 super(QtHelpSchemeReply, self).__init__(parent)
121
122 self.__job = job
123 self.__engine = engine
124 self.__mutex = QMutex()
125
126 self.__buffer = QBuffer()
127
128 # determine mimetype
129 url = self.__job.requestUrl()
130 strUrl = url.toString()
131
132 # For some reason the url to load maybe wrong (passed from web engine)
133 # though the css file and the references inside should work that way.
134 # One possible problem might be that the css is loaded at the same
135 # level as the html, thus a path inside the css like
136 # (../images/foo.png) might cd out of the virtual folder
137 if not self.__engine.findFile(url).isValid():
138 if strUrl.startswith(QtDocPath):
139 newUrl = self.__job.requestUrl()
140 if not newUrl.path().startswith("/qdoc/"):
141 newUrl.setPath("/qdoc" + newUrl.path())
142 url = newUrl
143 strUrl = url.toString()
144
145 self.__mimeType = mimetypes.guess_type(strUrl)[0]
146 if self.__mimeType is None:
147 # do our own (limited) guessing
148 self.__mimeType = self.__mimeFromUrl(url)
149
150 self.__loadQtHelpPage(url)
151
152 def __loadQtHelpPage(self, url):
153 """
154 Private method to load the requested QtHelp page.
155
156 @param url URL of the requested page
157 @type QUrl
158 """
159 if self.__engine.findFile(url).isValid():
160 data = self.__engine.fileData(url)
161 else:
162 data = QByteArray(self.tr(
163 """<html>"""
164 """<head><title>Error 404...</title></head>"""
165 """<body><div align="center"><br><br>"""
166 """<h1>The page could not be found</h1><br>"""
167 """<h3>'{0}'</h3></div></body>"""
168 """</html>""").format(url.toString())
169 .encode("utf-8"))
170
171 lock = QMutexLocker(self.__mutex)
172 self.__buffer.setData(data)
173 self.__buffer.open(QIODevice.ReadOnly)
174 self.open(QIODevice.ReadOnly)
175 lock.unlock()
176
177 self.readyRead.emit()
178
179 def bytesAvailable(self):
180 """
181 Public method to get the number of available bytes.
182
183 @return number of available bytes
184 @rtype int
185 """
186 lock = QMutexLocker(self.__mutex) # __IGNORE_WARNING__
187 return self.__buffer.bytesAvailable()
188
189 def readData(self, maxlen):
190 """
191 Public method to retrieve data from the reply object.
192
193 @param maxlen maximum number of bytes to read (integer)
194 @return string containing the data (bytes)
195 """
196 lock = QMutexLocker(self.__mutex) # __IGNORE_WARNING__
197 return self.__buffer.read(maxlen)
198
199 def close(self):
200 """
201 Public method used to cloase the reply.
202 """
203 super(QtHelpSchemeReply, self).close()
204 self.closed.emit()
205
206 def __mimeFromUrl(self, url):
207 """
208 Private method to guess the mime type given an URL.
209
210 @param url URL to guess the mime type from (QUrl)
211 @return mime type for the given URL (string)
212 """
213 path = url.path()
214 ext = os.path.splitext(path)[1].lower()
215 if ext in ExtensionMap:
216 return ExtensionMap[ext]
217 else:
218 return "application/octet-stream"
219
220 def mimeType(self):
221 """
222 Public method to get the reply mime type.
223
224 @return mime type of the reply
225 @rtype bytes
226 """
227 return self.__mimeType.encode("utf-8")

eric ide

mercurial