sofi 0.3.9
Desktop and Web GUI framework based on WebSockets
License: MIT
Requires Python: >=3.6
Keywords: websockets javascript bootstrap gui unity3d desktop html

[![Build Status](https://travis-ci.org/tryexceptpass/sofi.svg?branch=master)](https://travis-ci.org/tryexceptpass/sofi) [![Stories in Ready](https://badge.waffle.io/tryexceptpass/sofi.png?label=ready&title=Tasks%20Ready)](https://waffle.io/tryexceptpass/sofi) [![Join the chat at https://gitter.im/try-except-pass/sofi](https://badges.gitter.im/try-except-pass/sofi.svg)](https://gitter.im/try-except-pass/sofi?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Documentation Status](https://readthedocs.org/projects/sofi-gui-framework/badge/?version=latest)](http://sofi-gui-framework.readthedocs.io/en/latest/?badge=latest)
[![Say Thanks!](https://img.shields.io/badge/Say%20Thanks-!-1EAEDB.svg)](https://saythanks.io/to/tryexceptpass)

## Overview
`sofi` is an OS agnostic UI module for Python.

The main idea is to allow rapid, pythonic GUI development using standard web-based widgets from `Bootstrap` and
other common HTML5 libraries and package them in such a way that all event processing is done within python using
`websockets`.

This was developed as an exercise in poking at the underlying technologies and to see what comes out of merging them together. For a better overview of why it came to be, feel free to take a look at this post: [A Python Ate My GUI - Part 3: Implementation](http://tryexceptpass.org/article/a-python-ate-my-gui-3)

We're in a **beta** stage for now, feedback appreciated.

## Usage
The `sofi.app.Sofi` object runs the main event thread with `.start()` and provides a number of functions to help manipulate the state of the user interface, which is actually a webpage opened in a browser. The `register` and `unregister` methods provide the mechanism for subscribing to events through callbacks, while the remaining functions (`append`, `replace`, `style`, etc.) wrap the commands intended to dynamically update the widgets (which are actual HTML elements).

The commands map directly to `D3` or `jQuery` methods present in the `sofi.js` library that's loaded during initialization and is responsible for performing the requested operations.

Following basic practices from bootstrap, the widgets should be within a `Container`. The base page itself is represented with the `View` class, which wraps the necessary head, body and style tags that will contain the widgets.

Below is a quick idea of how to get things going, but check out `sample.py` for a more complicated hello world which instantiates a navbar item, adds a few links, creates some buttons, registers events and performs some timed updates.


```python
from sofi.app import Sofi
from sofi.ui import Container, Paragraph, Heading, View

import json
import asyncio


async def onload(event):
    # Every page is built on top of a View object, which contains the <head> and <body> tags that are filled in by the other objects
    v = View()

    # Make a bootstrap container in which to put all your widgets
    c = Container()

    # Add a heading and paragraph to the container
    c.addelement(Heading(2, "Dude!"))
    c.addelement(Paragraph("Where's My Car?"))

    # Add the container to the view
    v.addelement(c)

    # Tell the UI to load the HTML generated by the view
    app.load(str(v))


# Instantiate the application
app = Sofi()

# Register the event handler that runs when the UI is ready to receive commands
app.register('init', onload)

# Start the app (opens the default browser) and listen for events
app.start()
```

## What do the widgets look like?

![sample.py](https://cdn-images-1.medium.com/max/800/1*euug6f885sjtRPOMt_Vc6g.png)

![timeline.py](https://cdn-images-1.medium.com/max/800/1*AmbFclbXWFdIRYbpa0cyBw.png)