Created
September 27, 2018 12:50
-
-
Save jsomers/d32dd3507e5406c56e47b4cd6f28c60e to your computer and use it in GitHub Desktop.
Revisions
-
jsomers revised this gist
Sep 27, 2018 . 1 changed file with 2 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,3 +1,5 @@ # Using websockets to easily build GUIs for Python programs I recently built a small [agent-based model](https://en.wikipedia.org/wiki/Agent-based_model) using Python and wanted to visualize the model in action. But as much as Python is an ideal tool for scientific computation (numpy, scipy, matplotlib), it's not as good for dynamic visualization (pygame?). You know what's a very mature and flexible tool for drawing graphics? The DOM! For simple graphics you can use HTML and CSS; for more complicated stuff you can use Canvas, SVG, or WebGL. There are countless frameworks, libraries, and tutorials to help you draw exactly what you need. In my case, this was the animation I wanted: -
jsomers created this gist
Sep 27, 2018 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,106 @@ I recently built a small [agent-based model](https://en.wikipedia.org/wiki/Agent-based_model) using Python and wanted to visualize the model in action. But as much as Python is an ideal tool for scientific computation (numpy, scipy, matplotlib), it's not as good for dynamic visualization (pygame?). You know what's a very mature and flexible tool for drawing graphics? The DOM! For simple graphics you can use HTML and CSS; for more complicated stuff you can use Canvas, SVG, or WebGL. There are countless frameworks, libraries, and tutorials to help you draw exactly what you need. In my case, this was the animation I wanted:  (Each row represents a "worker" in my model, and each rectangle represents a "task.") I would have had no idea how to do this in Python. But I knew it would be almost trivial using HTML and CSS. So I turned my command-line program into a simple server, and sent data over a websocket to a web page, which did all the drawing. ### Server-side: do fancy scientific calculations and publish data to the websocket Using [a tiny websocket library](https://github.com/Pithikos/python-websocket-server) for Python, I set up my server as follows: ```python import json import numpy import time from websocket_server import WebsocketServer server = WebsocketServer(9001) def run(): """The actual model""" ... def new_client(client, server): run() server.set_fn_new_client(new_client) server.run_forever() ``` Then, in the body of the `run` function, which runs on a loop, I simply pushed the state of my model whenever I needed to: ```python def run(): while True: ... # do stuff data = {"rows": [...]} # model data server.send_message_to_all(json.dumps(data)) time.sleep(0.1) # so as not to overwhelm the web UI. this gives us our effective "frame rate" ``` ### Client-side: draw the data using our old friends HTML, CSS, and Javascript The client is equally simple: we just listen for websocket push events and rewrite our DOM. For my model, I just needed to draw those rectangles you see above. I could color and position them using CSS, and size them dynamically based on the data. ```html <title>Client</title> <style> .row { height: 6px; font-size: 13px; margin-bottom: 2px; } .rectangle { background-color: lightgray; float: left; margin-right: 2px; height: 6px; } .rectangle.highlight.current { background-color: pink; } .rectangle.highlight { background-color: red; } </style> <div id="rows"></div> <script type="text/javascript"> const WIDTH_SCALE_FACTOR = 1; const draw_row = ({tasks}) => { const html = tasks.map((task, i) => { return ` <div class="rectangle ${task.status} ${task.is_current ? 'current' : ''}" style="width: ${Math.ceil(task.cost / WIDTH_SCALE_FACTOR)}px" ></div> `; }).join(''); return `<div class="row">${html}</div>`; } const ws = new WebSocket('ws://localhost:9001/'); ws.onmessage = ({data}) => { const rows = JSON.parse(data).rows; const html = rows.map(row => draw_row(row)).join(''); document.getElementById('rows').innerHTML = html; }; </script> ``` ### Conclusion: use websockets to bridge your favorite computing environment with the web The same technique would of course work for any language besides Python. The broader point is that you probably have a language you prefer using for scientific / numerical computing, and it's probably not Javascript. But HTML, CSS, Javascript, and Canvas offer a flexible and easy-to-use toolkit for drawing graphics—and the browser is a natural and ubiquitous GUI. Websockets can be the bridge between these two worlds.