Reputation: 492
I am using a QWebview to display html generated by the same program. Now the html can have references to other resources, e.g. <FRAME src=...>
in a fram set. When the browser would start downloading that resource, I must intercept that request and suply the content myself, since there's no webserver involved. Where are the hooks that I may use to catch up the requested url and supply the generated content?
to create the browser widget:
self.browser = QWebView()
self.layout.addWidget(self.browser)
to load the frame set:
self.browser.setHtml(ret.text)
Now what I would expect to find is some signal and then
self.browser.requestURI.connect(myhandler)
But I don't see anything alike it. What is the better approach here?
EDIT:
The major problem seems to be using setHtml. Thus, all loading mechanisms appear to be bypassed. With load() in combination with a QNetworkAccessManager I had better results (see below). Now the response-object is offered to my content manager, however, I sofar failed to write anything to the response object (or to instantiate a fresh one). It can be opened passing an access mode parameter. Then the READ-ONLY error disappears, but still write returns -1.
I rephrase the title of this question accordingly.
from PyQt5.Qt import * # @UnusedWildImport
class ContentManager(object):
def handleRequest(self, request, response):
# response.writeData(bytearray("hello new year", "utf-8")) #THIS WORKS NOT
return response
class NAM(QNetworkAccessManager):
def __init__(self, contentManager):
super().__init__()
self.contentManager = contentManager
def createRequest(self, operation, request, device):
response = super().createRequest(operation, request, device)
return self.contentManager.handleRequest(request, response)
class Browser(QWidget):
def __init__(self):
super().__init__()
def open(self, url):
self.browser.load(QUrl(url))
def build(self, contentManager):
layout = QVBoxLayout(self)
view = QWebView()
page = view.page(); view.setPage(page)
page.setNetworkAccessManager(NAM(contentManager))
layout.addWidget(view)
self.browser = view
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
w = Browser()
w.build(ContentManager())
w.open("index.html")
w.show()
app.exec_()
Upvotes: 0
Views: 646
Reputation: 18136
You need to implement a custom QNetworkReply
, in general it is possible to serve whatever you want to 'inject' into webview (html, text, images).
I am not sure about local files and frames, but when you are using a 'fake domain' that has to be resolved by QNetworkAccessManager
it will work.
The following very simple example works using Python 3.6.3 and PyQt 5.10.0:
from PyQt5.Qt import * # @UnusedWildImport
import signal, os, sys
signal.signal(signal.SIGINT, signal.SIG_DFL)
class ContentHandler(object):
def __init__(self, url):
self.url = url
def __call__(self):
print ('ContentHandler >>', self.url)
path, basename = os.path.split(self.url)
if basename == 'index.html':
return b"hello new year", 'text/html'
class DownloadReply(QNetworkReply):
def __init__(self, parent, operation, request):
super(DownloadReply, self).__init__(parent)
self.setRequest(request)
self.setOperation(operation)
self.setUrl(request.url())
self.bytes_read = 0
self.content = b''
# give webkit time to connect to the finished and readyRead signals
QTimer.singleShot(200, self.load_content)
def load_content(self):
self.content, self.data = ContentHandler(str(self.url().toString()))()
self.offset = 0
self.open(QIODevice.ReadOnly | QIODevice.Unbuffered)
self.setHeader(QNetworkRequest.ContentTypeHeader, QVariant(self.data))
self.setHeader(QNetworkRequest.ContentLengthHeader, QVariant(len(self.content)))
self.readyRead.emit()
self.finished.emit()
def abort(self):
pass
def isSequential(self):
return True
def bytesAvailable(self):
ba = len(self.content) - self.bytes_read + super(DownloadReply, self).bytesAvailable()
return ba
def readData(self, size):
if self.bytes_read >= len(self.content):
return None
data = self.content[self.bytes_read:self.bytes_read + size]
self.bytes_read += len(data)
return data
def manager(self):
return self.parent()
class NetworkAccessManager(QNetworkAccessManager):
def __init__(self, parent=None):
super(NetworkAccessManager, self).__init__(parent=parent)
def createRequest(self, operation, request, device):
if str(request.url().host()).lower() == "myfakedom.ain":
print ('request:', request.url().host())
return DownloadReply(self, self.GetOperation, request)
return super(NetworkAccessManager, self).createRequest(operation, request, device)
if __name__ == '__main__':
app = QApplication(sys.argv)
webView = QWebView()
webView.settings().setAttribute(QWebSettings.PluginsEnabled, True)
webView.settings().setAttribute(QWebSettings.DeveloperExtrasEnabled, True)
webView.settings().setAttribute(QWebSettings.AutoLoadImages, True)
webView.settings().setAttribute(QWebSettings.JavascriptEnabled, True)
webInspector = QWebInspector()
nam = NetworkAccessManager()
webView.page().setNetworkAccessManager(nam)
webView.load(QUrl('http://myFakeDom.ain/index.html'))
webInspector.setPage(webView.page())
window = QMainWindow()
window.setCentralWidget(webView)
window.setFixedSize(1200, 840)
window.setWindowTitle('Test')
window.show()
sys.exit(app.exec_())
Upvotes: 3