{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# The IPython widgets, now in IHaskell !!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It is highly recommended that users new to jupyter/ipython take the *User Interface Tour* from the toolbar above (Help -> User Interface Tour)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> This notebook introduces the [IPython widgets](https://github.com/ipython/ipywidgets), as implemented in [IHaskell](https://github.com/gibiansky/IHaskell). The `Button` widget is also demonstrated as a live action example." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### The Widget Hierarchy\n", "\n", "These are all the widgets available from IPython/Jupyter." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Uncategorized Widgets\n", "\n", "+ Button\n", "+ Image*Widget*\n", "+ Output*Widget*\n", "\n", "#### Box Widgets\n", "\n", "+ Box\n", "+ FlexBox\n", "+ Accordion\n", "+ Tab*Widget*\n", "\n", "#### Boolean Widgets\n", "\n", "+ CheckBox\n", "+ ToggleButton\n", "\n", "#### Integer Widgets\n", "\n", "+ IntText\n", "+ BoundedIntText\n", "+ IntProgress\n", "+ IntSlider\n", "+ IntRangeSlider\n", "\n", "#### Float Widgets\n", "\n", "+ FloatText\n", "+ BoundedFloatText\n", "+ FloatProgress\n", "+ FloatSlider\n", "+ FloatRangeSlider\n", "\n", "#### Selection Widgets\n", "\n", "+ Selection\n", "+ Dropdown\n", "+ RadioButtons\n", "+ Select\n", "+ SelectMultiple\n", "+ ToggleButtons\n", "\n", "#### String Widgets\n", "\n", "+ HTML*Widget*\n", "+ Latex*Widget*\n", "+ TextArea\n", "+ Text*Widget*" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Using Widgets" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Necessary Extensions and Imports\n", "\n", "All the widgets and related functions are available from a single module, `IHaskell.Display.Widgets`. It is strongly recommended that users use the `OverloadedStrings` extension, as widgets make extensive use of `Text`." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "{-# LANGUAGE OverloadedStrings #-}\n", "import IHaskell.Display.Widgets" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The module can be imported unqualified. Widgets with common names, such as `Text`, `Image` etc. have a `-Widget` suffix to prevent name collisions." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Widget interface\n", "\n", "Each widget has different properties, but the surface level API is the same.\n", "\n", "Every widget has:\n", "\n", "1. A constructor:\n", " An `IO ` value/function of the form `mk`.\n", "2. A set of properties, which can be manipulated using `setField` and `getField`.\n", "\n", "The `setField` and `getField` functions have nasty type signatures, but they can be used by just intuitively understanding them." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/html": [ "setField :: forall f w. (RElemOf f (WidgetFields w), ToKey f, IHaskellWidget (IPythonWidget w), ToPairs (Attr f)) => IPythonWidget w -> FieldType f -> IO ()" ], "text/plain": [ "setField :: forall f w. (RElemOf f (WidgetFields w), ToKey f, IHaskellWidget (IPythonWidget w), ToPairs (Attr f)) => IPythonWidget w -> FieldType f -> IO ()" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":t setField" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `setField` function takes three arguments, the first of which is a typelevel argument:\n", "\n", "1. A type level `Field`\n", "2. A widget\n", "3. A value for the `Field`" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/html": [ "getField :: forall f w. RElemOf f (WidgetFields w) => IPythonWidget w -> IO (FieldType f)" ], "text/plain": [ "getField :: forall f w. RElemOf f (WidgetFields w) => IPythonWidget w -> IO (FieldType f)" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":t getField" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `getField` function takes a type-level `Field` and a `Widget` value and returns the value of that `Field` for the `Widget`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Another utility function is `properties`, which shows all properties of a widget." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/html": [ "properties :: forall w. IPythonWidget w -> IO ()" ], "text/plain": [ "properties :: forall w. IPythonWidget w -> IO ()" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":t properties" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Displaying Widgets\n", "\n", "IHaskell automatically displays anything *displayable* given to it directly." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "\"abc\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "-- Showables\n", "1 + 2\n", "\"abc\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Widgets can either be displayed this way, or explicitly using the `display` function from `IHaskell.Display`." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/html": [ "display :: forall a. IHaskellDisplay a => a -> IO Display" ], "text/plain": [ "display :: forall a. IHaskellDisplay a => a -> IO Display" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import IHaskell.Display\n", ":t display" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Multiple displays\n", "\n", "A widget can be displayed multiple times. All these *views* are representations of a single object, and thus are linked.\n", "\n", "When a widget is created, a model representing it is created in the frontend. This model is used by all the views, and any modification to it propagates to all of them." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Closing widgets\n", "\n", "Widgets can be closed using the `closeWidget` function." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/html": [ "closeWidget :: forall w. IHaskellWidget w => w -> IO ()" ], "text/plain": [ "closeWidget :: forall w. IHaskellWidget w => w -> IO ()" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":t closeWidget" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Our first widget: `Button`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's play with buttons as a starting example:\n", "\n", "As noted before, all widgets have a constructor of the form `mk`. Thus, to create a `Button`, we use `mkButton`." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/html": [ "button :: Button" ], "text/plain": [ "button :: Button" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "button <- mkButton -- Construct a Button\n", ":t button" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Widgets can be displayed by just entering them into a cell." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "5e8125e2-4d0a-4ce4-902d-aad1922ce61b", "version_major": 2, "version_minor": 0 } }, "metadata": {}, "output_type": "display_data" } ], "source": [ "button -- Display the button" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To view a widget's properties, we use the `properties` function. It also shows the type represented by the `Field`, which generally are not visible in type signatures due to high levels of type-hackery." ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "ViewModule ::: Text\n", "ViewModuleVersion ::: Text\n", "ModelModule ::: Text\n", "ModelModuleVersion ::: Text\n", "ModelName ::: Text\n", "ViewName ::: Text\n", "DOMClasses ::: [Text]\n", "Tabbable ::: Maybe Bool\n", "Tooltip ::: Maybe Text\n", "Layout ::: IPythonWidget LayoutType\n", "DisplayHandler ::: IO ()\n", "Description ::: Text\n", "DescriptionAllowHtml ::: Maybe Bool\n", "Style ::: StyleWidget\n", "Disabled ::: Bool\n", "Icon ::: Text\n", "ButtonStyleField ::: ButtonStyleValue\n", "ClickHandler ::: IO ()" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "-- The button widget has many properties.\n", "properties button" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's try making the button widget wider." ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [], "source": [ "import qualified IHaskell.Display.Widgets.Layout as L\n", "btnLayout <- getField @Layout button\n", "setField @L.Width btnLayout $ Just \"100%\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There is a lot that can be customized. For example:" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [], "source": [ "setField @Description button \"Click Me (._.\\\")\"\n", "setField @ButtonStyleField button SuccessButton\n", "setField @L.BorderTop btnLayout $ Just \"ridge 2px\"\n", "setField @L.Padding btnLayout $ Just \"10\"\n", "setField @L.Height btnLayout $ Just \"7em\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The button widget also provides a click handler. We can make it do anything, except console input. Universally, no widget event can trigger console input." ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "5e8125e2-4d0a-4ce4-902d-aad1922ce61b", "version_major": 2, "version_minor": 0 } }, "metadata": {}, "output_type": "display_data" } ], "source": [ "setField @ClickHandler button $ putStrLn \"fO_o\"\n", "button -- Displaying again for convenience" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now try clicking the button, and see the output.\n", "\n", "> Note: If you display to stdout using Jupyter Lab, it will be displayed in a log entry, not as the cell output.\n", "\n", "We can't do console input, though, but you can always use another widget! See the other notebooks with examples for more information" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [], "source": [ "setField @ClickHandler button $ getLine >>= putStrLn" ] } ], "metadata": { "kernelspec": { "display_name": "Haskell", "language": "haskell", "name": "haskell" }, "language_info": { "codemirror_mode": "ihaskell", "file_extension": ".hs", "mimetype": "text/x-haskell", "name": "haskell", "pygments_lexer": "Haskell", "version": "9.8.2" } }, "nbformat": 4, "nbformat_minor": 4 }