Python WSGI with Node-Webkit

[Repost from the old blog, don’t know if it works anymore]

Let us combine the greatness of HTML/CSS/Javascript for user interface with Python the best way to build backend to create a desktop app.

The basic idea is to use create a python webserver process, wait for it to start and display your app. After your app closes it will close the webserver process.

The code you can see and download below has a few extra features:

  • Splash screen while python process starts
  • Waits till webserver process returns a result so we really know it has started
  • F5 – Refresh
  • F11 – Fullscreen toggle
  • F12 – Show Dev Tools

To make this work use the zip file at the bottom and put it in same folder as NWJS.

Only tested on windows with node-webkit 0.9.2(0.11.9)

A pretty standard node-webkit package file:

JavaScript
10 lines
1
2
3
4
5
6
7
8
9
10
{
"name": "wsgi",
"main": "app://whatever/start.html",
"window": {
"width": 320,
"height": 240,
"toolbar": false,
"fullscreen": false
}
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
package.json

The simple python WSGI app:

Python
23 lines
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from wsgiref.util import setup_testing_defaults
from wsgiref.simple_server import make_server
def simple_app(environ, start_response):
setup_testing_defaults(environ)
status = '200 OK'
headers = [('Content-type', 'text/html')]
start_response(status, headers)
yield "<h1>Python WSGI App</h1>"
yield "<pre>"
for key, value in environ.iteritems():
yield "%s: %s\n" % (key, value)
if __name__ == '__main__':
port = 44408
httpd = make_server('', port, simple_app)
print "Serving on port "+str(port)+"..."
httpd.serve_forever()
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
wsgi_app.py

Where the magic happens:

HTML
168 lines
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
<!DOCTYPE html>
<html>
<head>
<title>Start</title>
<script type="text/javascript">
var platform = require("os").platform;
</script>
<style>
body {
background: #272822;
color: #FFF;
font-family: "Segoe UI", Frutiger, "Frutiger Linotype", "Dejavu Sans", "Helvetica Neue", Arial, sans-serif;
cursor: default;
}
</style>
</head>
<body>
<h1>Loading...</h1>
We are using node.js <script>document.write(process.version)</script>.<br/>
platform: <script>document.write(platform())</script>.<br/>
status: <span id="status">status</span>
<script type="text/javascript">
//************************************************************
var pyexec = 'C:/Python27/python.exe';
var basedir = '.'; //working dir
var pywsgi = basedir+'/wsgi_app.py';
var startpage = 'http://127.0.0.1:44408/';
var pagecheck = 'http://127.0.0.1:44408/404'; //no side-effect page to check if webserver is running
//************************************************************
var wsgi_process = null;
var spawn = require("child_process").spawn;
//------------------------------------------------------------
function StartWSGI()
{
console.log('StartWSGI');
if (platform() == 'win32')
{
pyexec = pyexec.replace('/','\\');
pywsgi = pywsgi.replace('/','\\');
basedir = basedir.replace('/','\\');
}
child = spawn(pyexec, [pywsgi,],{cwd:basedir});
child.stdout.on("data", function(data) {
return console.log("stdout:" + data);
});
child.stderr.on("data", function(data) {
return console.log("ERROR: " + data);
});
child.on("exit", function(code) {
return console.log("Encoding process exited with code: " + code);
});
console.log("StartWSGI_End");
wsgi_process = child;
function transferComplete(evt) {
InitMainWindow();
}
function transferFailed(evt) {
document.getElementById("status").textContent = "Waiting...";
checkServer();
}
function reqListener () {
document.getElementById("status").textContent = "Launching...";
};
function checkServer()
{
var oReq = new XMLHttpRequest();
oReq.addEventListener("load", transferComplete, false);
oReq.addEventListener("error", transferFailed, false);
oReq.onload = reqListener;
oReq.open("GET", pagecheck, true);
oReq.send();
}
document.getElementById("status").textContent = "checking...";
checkServer();
}
//------------------------------------------------------------
function InitMainWindow()
{
var gui = require('nw.gui');
// Get the current window
var win = gui.Window.get();
var new_win =
gui.Window.open(startpage, {
position: 'center',
width: 640,
height: 480,
"toolbar": false,
focus: true,
show: false
});
// And listen to new window's focus event
new_win.on('focus', function() {
console.log('New window is focused-b');
});
// Release the 'win' object here after the new window is closed.
new_win.on('closed', function() {
console.log("mainclose");
wsgi_process.kill();
win.close();
win = null;
});
new_win.on('loaded', function() {
console.log('New window is loaded');
win.hide();
new_win.show();
new_win.focus();
var awin = gui.Window.get(this);
awin = this.window;
awin.onkeyup = function(e) {
//console.log(e);
if (e.keyIdentifier == "F5")
{
new_win.reload();
}
if (e.keyIdentifier == "F11")
{
new_win.toggleFullscreen();
}
if (e.keyIdentifier == "F12")
{
if (new_win.isDevToolsOpen())
{
new_win.closeDevTools();
}else{
new_win.showDevTools();
}
}
};
});
}
//======================================================================
var gui = require('nw.gui');
var win = gui.Window.get();
win.on('loaded', function() {
console.log('New window is loaded');
StartWSGI();
});
//======================================================================
</script>
</body>
</html>
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
start.html

Download code (Zip)