Metadata-Version: 2.1
Name: idom
Version: 0.4.2
Summary: Control the web with Python
Home-page: https://github.com/rmorshea/idom
Author: Ryan Morshead
Author-email: ryan.morshead@gmail.com
License: MIT
Keywords: interactive,widgets,DOM,React
Platform: Linux
Platform: Mac OS X
Platform: Windows
Requires-Python: >=3.6,<4.0
Description-Content-Type: text/markdown
Requires-Dist: sanic (<19.0,>=18.12)
Provides-Extra: all
Requires-Dist: vdom ; extra == 'all'
Requires-Dist: matplotlib ; extra == 'all'
Provides-Extra: matplotlib
Requires-Dist: matplotlib ; extra == 'matplotlib'
Provides-Extra: vdom
Requires-Dist: vdom ; extra == 'vdom'

# iDOM

<a href="https://travis-ci.com/rmorshea/idom">
  <img alt="Build Status" src="https://travis-ci.com/rmorshea/idom.svg?branch=master"/>
</a>
<a href="https://pypi.python.org/pypi/idom">
  <img alt="Version Info" src="https://img.shields.io/pypi/v/spectate.svg"/>
</a>
<a href="https://github.com/rmorshea/idom/blob/master/LICENSE"/>
  <img alt="License: MIT" src="https://img.shields.io/badge/License-MIT-purple.svg">
</a>

Libraries for creating and controlling interactive web pages with Python 3.6 and above.

* [Python Documentation](https://idom.readthedocs.io/en/latest/)
* [Javascript Documentation](https://github.com/rmorshea/idom/blob/master/src/js/README.md)

iDOM is still young. If you have ideas or find a bug, be sure to post an
[issue](https://github.com/rmorshea/idom/issues)
or create a
[pull request](https://github.com/rmorshea/idom/pulls). Thanks in advance!


<h3>
  <a href="https://mybinder.org/v2/gh/rmorshea/idom/master?filepath=examples%2Fintroduction.ipynb">
    Try it Now
    <img alt="Binder" valign="bottom" height="25px"
    src="https://mybinder.org/badge_logo.svg"
    />
  </a>
</h3>

Click the badge above to get started! It will take you to a [Jupyter Notebooks](https://jupyter.org/)
hosted by [Binder](https://mybinder.org/) with some great examples.


### Or Install it Now

```bash
pip install idom
```


# At a Glance

iDOM can be used to create a simple slideshow which changes whenever a user clicks an image.

```python
import idom

@idom.element
async def Slideshow(self, index=0):
    events = idom.Events()

    @events.on("click")
    async def change():
        self.update(index + 1)

    url = f"https://picsum.photos/800/300?image={index}"
    return idom.node("img", src=url, eventHandlers=events)

server = idom.server.sanic.PerClientState(Slideshow)
server.daemon("localhost", 8765).join()
```

Running this will serve our slideshow to `"https://localhost:8765/client/index.html"`

<img src='https://picsum.photos/800/300?random'/>

You could even display the same thing in a Jupyter notebook!

```python
idom.display("jupyter", "https://localhost:8765/stream")
```

Every click will then cause the image to change (it won't here of course).


## Breaking it Down

That might have been a bit much to throw out at once. Let's break down each piece of the
example above:

```python
@idom.element
async def Slideshow(self, index=0):
```

The `idom.element` decorator indicates that the
[asynchronous function](https://realpython.com/async-io-python/)
to follow returns a data structure which represents a user interface or Document Object
Model (DOM). We call this structural representation of the DOM a [Virtual DOM] (VDOM) - a
term familiar to those who work with
[ReactJS](https://reactjs.org/docs/faq-internals.html). In the case of `Slideshow` it
will return a VDOM representing an image which, when clicked, will change.

```python
    events = idom.Events()
```

`Events` creates an object to which event handlers will be assigned. Adding an `Events`
object to a VDOM will given you the ability to respond when users interact with you
interface. Under the hood though, `Events` is just a mapping that conforms to the
[VDOM event specification](https://github.com/nteract/vdom/blob/master/docs/event-spec.md).

```python
    @events.on("click")
    def change():
        self.update(index + 1)
```

By using the `Events` object we created above, we can register a function as an event
handler. In this case our handler is listening for `onClick` events and will be called
once a user clicks on the image. All supported events are listed
[here](https://reactjs.org/docs/events.html).

You can add parameters to this handler which will allow you to access attributes of the
JavaScript event which occurred in the browser. For example when a key is pressed in
an `<input/>` element you can access the key's name by adding a `key` parameter to
the event handler.

Inside the handler itself we update `self` which is out `Slideshow` element. Calling
`self.update(*args, **kwargs)` will schedule a new render of the `Slideshow` element to
be performed with new `*args` and `**kwargs`.

```python
    url = f"https://picsum.photos/800/300?image={index}"
    return idom.node("img", src=url, eventHandlers=events)
```

We return a VDOM for an `<img/>` element which draws its image from https://picsum.photos
and will respond to the `events` we defined earlier. Similarly to the `events` object
`idom.node` returns a mapping which conforms to the
[VDOM mimetype specification](https://github.com/nteract/vdom/blob/master/docs/mimetype-spec.md)


```python
server = idom.server.sanic.PerClientState(Slideshow)
server.daemon("localhost", 8765).join()
```

This sets up a simple web server which will display the layout of elements and update
them when events occur over a websocket. The server has "per client state" because
each client that connects to it will see a fresh view of the layout. If clients should
see views with a common state you can use the `SharedClientState` server.

To display the layout we can
navigate to http://localhost:8765/client/index.html or use `idom.display()` to show
it in a Jupyter Notebook via a widget. The exact protocol for communicating VDOM
over a network is not documented yet.


### Adding iDOM To Existing Applications

If you want to add iDOM to an existing server you can register its routes instead:

```python
from sanic import Sanic

app = Sanic()
extension = idom.server.sanic.PerClientState(Slideshow)
extension.configure(url_prefix="/idom").register(app)

app.run()
```


