eric7/WebBrowser/Network/QtHelpSchemeHandler.py

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

eric ide

mercurial