def setUrl(self, url): """ Reimplemented to make sure that when we return, the DOM is ready to use. Based on the local event loop approach described here: http://doc.qt.digia.com/qq/qq27-responsive-guis.html#waitinginalocaleventloop """ event_loop = QtCore.QEventLoop() self._loaded = False def on_load(): self._loaded = True event_loop.quit() self._page.loadFinished.connect(on_load) super(ProxyQWebView, self).setUrl(QtCore.QUrl(url)) if not self._loaded: event_loop.exec_()
class ProxyQWebPage(QtWebKit.QWebPage): """ Overridden to open external links in a web browser. Source: http://www.expobrain.net/2012/03/01/open-urls-in-external-browser-by-javascript-in-webkit/ """ def acceptNavigationRequest(self, frame, request, type): # Checking this is same as checking if the client side <a> tag making # the HTTP request had target="_blank" as an attribute. if frame is None: import webbrowser webbrowser.open_new(request.url().toString()) return False else: return super(ProxyQWebPage, self).acceptNavigationRequest( frame, request, type ) def createWindow(self, *args, **kwargs): return ProxyQWebPage() if __name__ == '__main__': app = QtGui.QApplication([]) w = ProxyQWebView() w.show() w.raise_() w.load(QtCore.QUrl('http://www.google.com/')) app.exec_()
class ProxyReplyWorker(QtCore.QThread): """ Worker thread to fetch urls for QNetworkProxy. """ # Signals to forward to ProxyReply metaDataChanged = QtCore.Signal() readyRead = QtCore.Signal() finished = QtCore.Signal() OPERATIONS = {QtNetwork.QNetworkAccessManager.GetOperation: 'GET', QtNetwork.QNetworkAccessManager.PostOperation: 'POST',} def __init__(self, reply, parent=None): super(ProxyReplyWorker, self).__init__(parent) self.reply = reply self.metaDataChanged.connect(self.reply.metaDataChanged) self.readyRead.connect(self.reply.readyRead) self.finished.connect(self.reply.finished) ########################################################################### # QThread interface. ########################################################################### def run(self): """ handles the request by acting as a WSGI forwarding server. """ reply = self.reply url = reply.url() req = reply.request() # WSGI environ variables env = { 'REQUEST_METHOD': self.OPERATIONS[reply.operation()], 'SCRIPT_NAME': '', 'PATH_INFO': url.path(), 'SERVER_NAME': url.host(), 'SERVER_PORT': '80', 'SERVER_PROTOCOL': 'HTTP/1.1', 'QUERY_STRING': str(url.encodedQuery()), 'wsgi.version': (1, 0), 'wsgi.url_scheme': url.scheme(), 'wsgi.input': StringIO(reply.req_data), 'wsgi.errors': sys.stderr, 'wsgi.multithread': False, 'wsgi.multiprocess': True, 'wsgi.run_once': False, } # Set WSGI HTTP request headers for head_name in req.rawHeaderList(): env_name = 'HTTP_' + head_name.data().replace('-','_').upper() head_val = req.rawHeader(head_name) env[env_name] = head_val.data() try: local_buf = [] local_buf_len = 0 for read in reply.handler(env, self._start_response): if reply.aborted: return local_buf.append(str(read)) local_buf_len += len(read) if local_buf_len >= 8192: # Do not write to buffer on every read, app is slowed down # due to lock contention with reply._buflock: reply.buffer += ''.join(local_buf) local_buf = [] local_buf_len = 0 self.readyRead.emit() with reply._buflock: reply.buffer += ''.join(local_buf) except Exception as e: if reply.aborted: return reply.setAttribute( QtNetwork.QNetworkRequest.HttpStatusCodeAttribute, 500 ) reply.setAttribute( QtNetwork.QNetworkRequest.HttpReasonPhraseAttribute, 'Internal Error' ) with reply._buflock: reply.buffer += 'WSGI Proxy "Server" Error.\n' + str(e) finally: self.readyRead.emit() self.finished.emit() ########################################################################### # Private interface. ########################################################################### def _start_response(self, status, response_headers): """ WSGI start_response callable. """ code, reason = status.split(' ', 1) self.reply.setAttribute( QtNetwork.QNetworkRequest.HttpStatusCodeAttribute, int(code) ) self.reply.setAttribute( QtNetwork.QNetworkRequest.HttpReasonPhraseAttribute, reason ) for name, value in response_headers: self.reply.setRawHeader(name, str(value)) self.metaDataChanged.emit()