How to load python stick app into gtk webview (webkit)
I am currently working on an application that requires displaying math expressions (from latex) and needs to have some kind of native gui (even if it just uses gtk and then displays the html in webkit).
I did some research and figured that an easy way to do this is to use webkit to load the webpage and use a JavaScript library like MathJax to display the math. Some other reasons why I decided to do it this way compared to other solutions, I had quite a lot of experience in developing web applications in python (albeit some time ago), lack of experience with native guis and the portability that it provided.
For the web application framework, I chose to use the flask as it is the most familiar to me.
The problem is that this application should have its own GUI using gtk (even if it just displays html with webkit) and also preferably shouldn't have an HTTP server that is connected to some socket.
So my question is, instead of starting a flask server, there is a way to do something like this:
import gtk
import webkit
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return "<h1>Hello World!</h1>"
if __name__ == '__main__':
window = gtk.Window()
webview = webkit.WebView()
webview.load_string(
app.load_from_uri('/'),
"text/html",
"utf-8",
'/'
)
window.add(webview)
window.show_all()
Where app.load_from_uri('/')
used as an example of a way to load a web page for a given uri of a Flask application. But since this is just an example, how could it be done app.load_from_uri('/')
in real code?
Also there is still some way to override when the user clicks on a link so that he does something like this:
def link_clicked(uri):
webview.load_string(
app.load_from_uri(uri),
"text/html",
"utf-8",
uri
)
Thanks, thanks for the help!
source to share
I ended up finding a solution for this (but better for the best ones).
The first thing that loaded the page was pretty simple. Flask provides the ability to test applications that basically just install whatever WSGI can handle. This is what I need, so I used it like this:
from flask import Flask
class WebViewFlask(Flask):
"""
Adds the ability to load a uri without the
need of a HTTP server.
"""
def load_from_uri(self, uri):
"""
Loads a uri without a running HTTP server.
"""
with self.test_client() as c:
response = c.get(uri)
return response.data, response.mimetype
The second part, overriding "when the user clicks the link", is a little more complicated.
import os
import webkit
class FlaskAppView(webkit.WebView):
"""
Loads pages for flask apps into a WebView.
"""
def __init__(self, flask_app, *args, **kwargs):
# Protocol for flask app, by default file:// is used
# so a protocol is defined here to prevent that.
self.PROTOCOL = 'flask://'
super(webkit.WebView, self).__init__(*args, **kwargs)
self._flask_app = flask_app
# Register new navigation handler.
self.connect(
"navigation-policy-decision-requested",
self._nav_request
)
# For navigation handler.
self.prev_uri = None
# Redefine open like this as when using super
# an error like this occurs:
# AttributeError: 'super' object has no attribute 'open'
self._open = self.open
self.open = self.open_
def _nav_request(self, view, frame, net_req, nav_act, pol_dec):
"""
WebView navigation handler for Flask apps.
"""
# Get the uri
uri = net_req.get_uri()
# In order for flask apps to use relative links
# the protocol is removed and it is made into an absolute
# path.
if uri.startswith(self.PROTOCOL):
# In this case it is not relative but
# it needs to have it protocol removed
uri = uri[len(self.PROTOCOL):]
elif not self.prev_uri.endswith(uri):
# It is relative and self.prev_uri needs to
# be appended.
uri = os.path.normpath(os.path.join(self.prev_uri, uri))
# This is used to prevent an infinite recursive loop due
# to view.load_string running this function with the same
# input.
if uri == self.prev_uri:
return False
self.prev_uri = uri
# Create response from Flask app.
response = app.load_from_uri(uri) + ('utf-8', uri)
# Load response.
view.load_string(*response)
# Return False to prevent additional
# handlers from running.
return False
def open_(self, uri):
"""
Prepends protocol to uri for webkit.WebView.open.
"""
self._open(self.PROTOCOL + uri)
Basically a new navigation event handler is registered with some code to ensure successful recursion and support for relative paths.
Anyway, with this code above, just replacing Flask
with WebViewFlask
and WebView
with FlaskAppView
, things pretty much just work.
And the result:
Which flash application is loaded into webkit.WebView without any server. The best thing about this is to just switch app
back to instance Flask
instead of WebViewFlask
It's a simple webapp again.
source to share