In the first part of this series, we discussed a few existing modules that give us tools for building interfaces. I promised to come back with some ideas on how I would attempt to solve the situation, and this post is intended to cover aspects of my initial design.
However, first I need to discuss one more point on existing modules. In case you’re not aware, PyCon 2016 happened a few wks ago in Portland, and amongst the many wonderful talks, keynotes and open spaces, there was one in particular that is relevant to the topic of our discussion here: Russell Keith Magee’s talk on BeeWare and the work he’s done to solve the same problem. I highly recommend you take a look and help out with his projects if you can. It’s definitely a bold and brave solution that’s highly complex, but he already has it at a usable state and deserves massive props for going down that path. The Podcast.__init__ guys also have a great interview with him going over the details.
That being said, I still want to go through the exercise, so let’s get into the meat of my design choices, put together with the following overall goal: portable simplicity that helps me learn something new without reinventing the wheel.
Let’s restate what I’m looking for:
- Cross-platform user interface for an application that will function on mobile and desktop without writing any new code (beyond supporting different input systems).
- All code written in python, including the UI and event handling.
- Performant with large tables of data and vector graphics.
Browsers are already cross-platform
While there may still be some holes, the advent several years ago of common frameworks like Bootstrap has really made cross-browser work a lot easier, as well as provided additional support (usually by default) for mobile interfaces. I’ve personally been using Bootstrap for my sites for a while now and I’m quite pleased with what I can do with it.
These innovations, coupled with the growing popularity of HTTP enabled RESTful interfaces, have provided the basis for what we today are calling single-page web applications.
HTML is too much work
If it’s not evident already by the fact that I’m making a module that writes code for me, I’m a lazy bum! And have been known to do a week’s worth of work just to not do a day’s worth of repetitive tasks. While simple and flexible, I find HTML to be hard to read and awkward to write. DOM modification can also be tricky unless you’re using a library that wraps it for you (read jQuery or D3).
I don’t want to transfer these aspects of the language to whatever becomes our solution, so another requirement is that all of these interactions must be handled by our module. To the programmer, it must seem like he’s working with a widget library, not some web page. Part of the value added is the abstraction from that layer.
What about React?
How to provide eventing?
Websockets! These little guys have been around for a while now. They are more lightweight than an HTTP request and can be kept alive as needed. All we need is a communications protocol to send events to the backend and interface updates to the frontend.
If you don’t believe that websockets can hack it, I invite you to look at Google’s Chromecast. The display on your TV is a webpage and its API uses websocket connections to mirror your browser, send commands and even live debug.
What about tables and data visualization?
As far as charting and manipulating the UI with vector graphics, D3 handles that fairly well, so we’ll just wrap it into something pretty and usable from python.
What are we implementing?
Let’s write a Python package providing a module with the object hierarchy that wraps basic Bootstrap widgets (like a button) and then composites them into something more complicated (like a navbar) with some helper methods for putting them together.
If we override the
__str__ method to output the necessary HTML, then it becomes fairly easy to construct more complicated UIs, and we get the added benefit of using this module as a shortcut to producing the markup necessary for any webpage regardless of whether we use the rest of our solution or not.
With the UI representation in place, we can then use the autobahn module to provide the websocket layer with which to communicate. I haven’t used autobahn before, but it supports both Python 2 (tornado) and 3 (asyncio), and we get a chance to learn something new.
For comms between the UI and Python, JSON should be a sufficient format in which to exchange information. It’s simply a matter of defining the events and commands to send back and forth. We’ll only need a JS module loaded on the page that interprets the commands and calls the appropriate jQuery or D3 functions for us to manipulate the DOM.
The main entry point should be a simple HTML page which loads our JS library and then signals the Python backend when it’s ready to receive commands, while on the Python side we’ll want an object that functions like a server that can listen when we need it to.
For a view into how I wound up implementing these ideas, take a look at the next part in the series.
What do you think? At the very least it will serve as a good exercise in tying multiple things together under python. If you have any suggestions or comments feel free to ping me on Twitter.