Reactive narratives inspired by Tangle in the Jupyter Notebook.
IPyTangle makes plain markdown into an interactive part of your data-driven narrative.
Get the most recent release with:
pip install ipytangle
Or the bleeding-edge version:
pip install -e git+https://github.com/bollwyvl/ipytangle#egg=IPyTangle
In a Notebook, write this python...
from ipytangle import tangle
tangle(cookies=3, calories=lambda cookies: cookies * 50)
And this markdown:
When you eat [`cookies` cookies](#:cookies), you consume [`calories` calories](#:calories).
And you would see something like this:
When you eat`2
cookies <#:cookies>`__, you consume`150
calories <#:>`__.
And interacting with the links would cause the result to update.
Adapted from the originals from Tangle.
Additionally, ipytangle
comes with integration with IPython's
interact
, the easiest way to start using widgets.
`Interact
ing with a tangle <notebooks/examples/Interacting%20with%20a%20tangle.ipynb>`__- Tangling up ``interact` <notebooks/examples/Tangling%20up%20interact.ipynb>`__
ipytangle
implements most of the
TangleKit
baseline as markdown links. Because it adds no new notation, unrendered
Tangle Markdown should still render in a useful way.
Generally, think of a link as:
[what the user sees](#:tangle types)
In the link text, backticks, `` are used to represent a JavaScript
expression that will be updated on user interaction, cascading updates,
or other updates from the kernel. In addition to any variables defined
with ipytangle
, some formatting libraries are
provided. window
globals should also work 😉.
Only the generated code
blocks will be transformed, the rest of the
elements (if any) will be preserved. This means you can use images,
inside the label of a control, for example.
just display a view
For [`years` years](#:) have I trained Jedi.
display a view and update an integer based on dragging
[made the kessel run in `distance` parsecs](#:distance)
[<booly expression>](#:if)
... [_ ``[<booly expression>](#elif)``_] ... [_ ``[](#:else)`` _] ... [](#:endif)
mark some text (which may have other fields) to only display based on condition
What's more foolish? The [`fool_is_more_foolish`](#:if)fool[](#:else)the fool who follows him(#:endif).
you may also have an else
and any number of elif
s... because
they are markdown span-level elements, you may use newlines for easier
editing
[`feeling == "bad"`](#:if) I have a bad feeling about this.
[`feeling == "cautious"`](#:elif) You will never find a more wretched hive of scum and villainy.
[](#:else) Search your feelings.
[](#:endif)
display a view and update some type
of variable. Additionally,
min
imum max
imum and step
-size values can be specified.
Replace the variable
, for which a linked widget has been registered
(TBD) with the widget itself inline. > Needs research.
At it's core, ipytangle
provides Tangle
, an IPython Widget
.
Because of this, a Tangle
can connect to any other IPython widgets
via traitlets and links
. Unlike many widgets, a Tangle
doesn't
do much with its screen real estate, and might not be very interesting
to put inside a layout. Yet.
There are several methods for creating a Tangle
.
As in the above example, this tries to be the most pythonic approach.
from ipytangle import tangle
square = tangle(x=1, y=(1, lambda x: x*x))
print("square is", square)
square.x = 2
print("y is", square.y)
square is <IPython.utils.traitlets.DynamicAutoTangle139717235746760 object at 0x7f1274347ef0> y is 4
tangle
makes working with other core and custom widgets easy,
automatically copying trait information. If a widget implements
value
, as most of the core widgets do, you can reference it
directly. Alternately, specify a tuple
of (widget, "trait_name")
to subscribe to that trait.
from IPython.html.widgets import IntSlider
x = IntSlider(1)
square = tangle(x=x, y=(1, lambda x: x*x))
print("square is", square)
x.value = 2
print("y is", square.y)
square is <IPython.utils.traitlets.DynamicAutoTangle139717235839048 object at 0x7f1274362080> y is 4
If you are already familiar with widgets, subclassing Tangle
might
be the most convenient:
from ipytangle import Tangle
from IPython.utils import traitlets
class Square(Tangle):
x = traitlets.CInt(1, sync=True)
y = traitlets.CInt(1, sync=True)
def _x_changed(self):
self.y = self.x * self.x
square = Square()
print("square is", square)
square.x = 2
print("y is", square.y)
square is <__main__.Square object at 0x7f127434b7f0> y is 4 A future version of IPython Widgets will allow dynamically-added traits with ``add_trait``. This will open up whole new areas for tinkering. Stay tuned!
In addition to your locally-bound variables, ipytangle
bundles
several nice JavaScript libraries and shortcuts for formatting text and
numbers: - `moment
<http://momentjs.com/>`__ dates and times -
`mathjs
<http://mathjs.org/>`__ scientific
units- [
numeral`](http://numeraljs.com/) currency, and
miscellany
Of course, Brett Victor's Tangle is the primary inspiration, as well as: - tributary - derby - d3
- support TangleKit baseline
- float
- switch
- L_AT^EX (sic)
- sparklines, distributions, etc.
- offline/nbviewer?