2024-10-21 15:32:20 -04:00

378 lines
9.7 KiB
Plaintext

{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## The `Bool` Widgets\n",
"\n",
"+ CheckBox\n",
"+ ToggleButton"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"These widgets can be used to represent a Boolean value. The idea is pretty simple, the widget can be in one of two states which represent the two boolean values.\n",
"\n",
" Checked / On : True\n",
" Unchecked / Off : False"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"{-# LANGUAGE OverloadedStrings #-}\n",
"{-# LANGUAGE FlexibleContexts #-}\n",
"import IHaskell.Display.Widgets\n",
"import Data.Text (pack, unpack)\n",
"import Text.Printf (printf)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Simple demonstration\n",
"We are going to create one of each widget.\n",
"\n",
"The valid widget doesn't receive any input, but it displays the status of a boolean variable."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [
{
"data": {
"text/plain": []
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"-- Check box\n",
"chk <- mkCheckBox\n",
"\n",
"-- Toggle button\n",
"tgb <- mkToggleButton\n",
"\n",
"-- Valid widget: Displaying booleans conveniently\n",
"vld <- mkValid"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Below, we represent one boolean using a checkbox, and the other using a toggle button. The valid widget displays the result of applying the logical and (`&&`) between the two."
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "75200e34-c1ee-48d6-98b6-61ff755e96e4",
"version_major": 2,
"version_minor": 0
}
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "db9ce2a9-4a3a-4a0f-b50c-380bae1de262",
"version_major": 2,
"version_minor": 0
}
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "fa3ee396-e46e-4370-a7e0-aa5b521090b7",
"version_major": 2,
"version_minor": 0
}
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"-- Display the widgets\n",
"setField @Description tgb \"Button\"\n",
"chk\n",
"tgb\n",
"vld"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To make the `Valid` widget display the result of the computation, we have to get the value of the Checkbox and the value of the ToggleButton, and then set the value of the Valid widget to the result."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"b1 <- getField @BoolValue chk\n",
"b2 <- getField @BoolValue tgb\n",
"setField @BoolValue vld (b1 && b2)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"You can change the value of the widgets clicking in them, and then run the cell above again. If both widgets are checked, the valid widget should appear with a green check.\n",
"\n",
"But having to run the cell every time we click on the widgets is a bit cumbersome... Why not making it change auto*magic*ally? To do this, we will use a **handler**. A *handler* is a method that is called every time something happens. E.g: every time the button is clicked, or every time its associated value is changed on the frontend. These handlers are functions of type `IO ()`.\n",
"\n",
"Now, we are going to create a handler that is called every time one of the two widget changes and updates the `Valid` widget. We will also set a useful description."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [],
"source": [
"setField @Description chk \"b1\"\n",
"setField @Description tgb \"b2\"\n",
"setField @Description vld \"b1 && b2:\"\n",
"\n",
"ourBoolHandler :: IO ()\n",
"ourBoolHandler = do\n",
" b1 <- getField @BoolValue chk\n",
" b2 <- getField @BoolValue tgb\n",
" setField @BoolValue vld (b1 && b2)\n",
"\n",
"setField @ChangeHandler chk ourBoolHandler\n",
"setField @ChangeHandler tgb ourBoolHandler"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Extended example"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's try to create a graphical 8-bit-binary to decimal converter. We'll represent seven bits using `ToggleButton` widgets, and the negative bit using a `CheckBox`. The binary number is represented using 1+7-bit sign-and-magnitude representation for simplicity.\n",
"\n",
"Boxes are used to layout the widgets in an appealing manner, and the output widget is used to display the result."
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [],
"source": [
"-- First, some library functions\n",
"import Control.Monad (replicateM, forM_)\n",
"import Data.IORef\n",
"import IHaskell.Display (plain)\n",
"import qualified IHaskell.Display.Widgets.Layout as L"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now, we create a `CheckBox` and seven `ToggleButton`s."
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [],
"source": [
"sign <- mkCheckBox\n",
"bits <- replicateM 7 mkToggleButton\n",
"\n",
"setField @Description sign \"Negative\"\n",
"forM_ bits $ \\t -> do\n",
" setField @ButtonStyleField t PrimaryButton\n",
" -- setField @BorderRadius t 20"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Then we create a `FlexBox` to hold the widgets, and an `HTMLWidget` to display the output."
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "cd93f6ed-b871-48cd-b198-d509f1d17cfa",
"version_major": 2,
"version_minor": 0
}
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"box <- mkVBox\n",
"out <- mkHTML\n",
"\n",
"-- Sub-containers\n",
"box1 <- mkBox\n",
"setField @Children box1 [ChildWidget sign, ChildWidget out]\n",
"box2 <- mkBox\n",
"setField @Children box2 (map ChildWidget $ reverse bits)\n",
"\n",
"-- Add widgets to the container\n",
"setField @Children box (map ChildWidget [box1, box2])\n",
"\n",
"-- Add some UI chrome\n",
"setField @BoxStyle box InfoBox\n",
"layout <- getField @Layout out\n",
"setField @L.Width layout $ Just \"100px\"\n",
"setField @L.Height layout $ Just \"30px\"\n",
"setField @L.Margin layout $ Just \"10px\"\n",
"setField @L.BorderTop layout $ Just \"4px groove\"\n",
"\n",
"-- Display the container\n",
"box"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now, we implement the logic of our converter, and make it send the output to the `HTMLWidget` we created above."
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [],
"source": [
"import Control.Arrow (first, second)\n",
"\n",
"-- Mutable value, with a sign bit\n",
"val <- newIORef (0 :: Int, False)\n",
"\n",
"-- Helper function to redraw output\n",
"refresh :: (Int, Bool) -> IO ()\n",
"refresh (x, b) = \n",
" let val = x * if b then (-1) else 1\n",
" fmt = \"<div align=\\\"center\\\"><b>%d</b></div>\"\n",
" in setField @StringValue out (pack $ printf fmt val)\n",
"\n",
"setField @ChangeHandler sign $ do\n",
" -- Change sign for value\n",
" modifyIORef val (second not)\n",
" -- Redraw output\n",
" readIORef val >>= refresh\n",
" \n",
"setField @StringValue out \"<div align=\\\"center\\\"><b>%d</b></div>\"\n",
"\n",
"forM_ (zip bits (iterate (*2) 1)) $ \\(t, n) -> do\n",
" setField @Description t \"0\"\n",
" setField @ChangeHandler t $ do\n",
" f <- getField @BoolValue t\n",
" setField @Description t (if f then \"1\" else \"0\")\n",
" modifyIORef val (first $ if f then (+n) else (\\x->x-n))\n",
" readIORef val >>= refresh"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"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
}