Sat, 01 Dec 2018 11:44:34 +0100
Merged with default branch to prepare a new release.
# -*- coding: utf-8 -*- # Copyright (c) 2009 - 2018 Detlev Offenbach <detlev@die-offenbachs.de> # """ Module implementing a scheme access handler for QtHelp. """ from __future__ import unicode_literals import mimetypes import os from PyQt5.QtCore import pyqtSignal, QByteArray, QIODevice, QBuffer, QMutex, \ QMutexLocker from PyQt5.QtWebEngineCore import QWebEngineUrlSchemeHandler, \ QWebEngineUrlRequestJob QtDocPath = "qthelp://org.qt-project." ExtensionMap = { ".bmp": "image/bmp", ".css": "text/css", ".gif": "image/gif", ".html": "text/html", ".htm": "text/html", ".ico": "image/x-icon", ".jpeg": "image/jpeg", ".jpg": "image/jpeg", ".js": "application/x-javascript", ".mng": "video/x-mng", ".pbm": "image/x-portable-bitmap", ".pgm": "image/x-portable-graymap", ".pdf": "application/pdf", ".png": "image/png", ".ppm": "image/x-portable-pixmap", ".rss": "application/rss+xml", ".svg": "image/svg+xml", ".svgz": "image/svg+xml", ".text": "text/plain", ".tif": "image/tiff", ".tiff": "image/tiff", ".txt": "text/plain", ".xbm": "image/x-xbitmap", ".xml": "text/xml", ".xpm": "image/x-xpm", ".xsl": "text/xsl", ".xhtml": "application/xhtml+xml", ".wml": "text/vnd.wap.wml", ".wmlc": "application/vnd.wap.wmlc", } class QtHelpSchemeHandler(QWebEngineUrlSchemeHandler): """ Class implementing a scheme handler for the qthelp: scheme. """ def __init__(self, engine, parent=None): """ Constructor @param engine reference to the help engine @type QHelpEngine @param parent reference to the parent object @type QObject """ super(QtHelpSchemeHandler, self).__init__(parent) self.__engine = engine self.__replies = [] def requestStarted(self, job): """ Public method handling the URL request. @param job URL request job @type QWebEngineUrlRequestJob """ if job.requestUrl().scheme() == "qthelp": reply = QtHelpSchemeReply(job, self.__engine) reply.closed.connect(lambda: self.__replyClosed(reply)) self.__replies.append(reply) job.reply(reply.mimeType(), reply) else: job.fail(QWebEngineUrlRequestJob.UrlInvalid) def __replyClosed(self, reply): """ Private slot handling the closed signal of a reply. @param reply reference to the network reply @type QtHelpSchemeReply """ if reply in self.__replies: self.__replies.remove(reply) class QtHelpSchemeReply(QIODevice): """ Class implementing a reply for a requested qthelp: page. @signal closed emitted to signal that the web engine has read the data """ closed = pyqtSignal() def __init__(self, job, engine, parent=None): """ Constructor @param job reference to the URL request @type QWebEngineUrlRequestJob @param engine reference to the help engine @type QHelpEngine @param parent reference to the parent object @type QObject """ super(QtHelpSchemeReply, self).__init__(parent) self.__job = job self.__engine = engine self.__mutex = QMutex() self.__buffer = QBuffer() # determine mimetype url = self.__job.requestUrl() strUrl = url.toString() # For some reason the url to load maybe wrong (passed from web engine) # though the css file and the references inside should work that way. # One possible problem might be that the css is loaded at the same # level as the html, thus a path inside the css like # (../images/foo.png) might cd out of the virtual folder if not self.__engine.findFile(url).isValid(): if strUrl.startswith(QtDocPath): newUrl = self.__job.requestUrl() if not newUrl.path().startswith("/qdoc/"): newUrl.setPath("/qdoc" + newUrl.path()) url = newUrl strUrl = url.toString() self.__mimeType = mimetypes.guess_type(strUrl)[0] if self.__mimeType is None: # do our own (limited) guessing self.__mimeType = self.__mimeFromUrl(url) self.__loadQtHelpPage(url) def __loadQtHelpPage(self, url): """ Private method to load the requested QtHelp page. @param url URL of the requested page @type QUrl """ if self.__engine.findFile(url).isValid(): data = self.__engine.fileData(url) else: data = QByteArray(self.tr( """<html>""" """<head><title>Error 404...</title></head>""" """<body><div align="center"><br><br>""" """<h1>The page could not be found</h1><br>""" """<h3>'{0}'</h3></div></body>""" """</html>""").format(url.toString()) .encode("utf-8")) lock = QMutexLocker(self.__mutex) self.__buffer.setData(data) self.__buffer.open(QIODevice.ReadOnly) self.open(QIODevice.ReadOnly) lock.unlock() self.readyRead.emit() def bytesAvailable(self): """ Public method to get the number of available bytes. @return number of available bytes @rtype int """ lock = QMutexLocker(self.__mutex) # __IGNORE_WARNING__ return self.__buffer.bytesAvailable() def readData(self, maxlen): """ Public method to retrieve data from the reply object. @param maxlen maximum number of bytes to read (integer) @return string containing the data (bytes) """ lock = QMutexLocker(self.__mutex) # __IGNORE_WARNING__ return self.__buffer.read(maxlen) def close(self): """ Public method used to cloase the reply. """ super(QtHelpSchemeReply, self).close() self.closed.emit() def __mimeFromUrl(self, url): """ Private method to guess the mime type given an URL. @param url URL to guess the mime type from (QUrl) @return mime type for the given URL (string) """ path = url.path() ext = os.path.splitext(path)[1].lower() if ext in ExtensionMap: return ExtensionMap[ext] else: return "application/octet-stream" def mimeType(self): """ Public method to get the reply mime type. @return mime type of the reply @rtype bytes """ return self.__mimeType.encode("utf-8")