forked from jtpio/ipylab
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Switch back from DocumentWidget to MainAreaWidget. Added AutoScroll, …
…SimpleOutput and logging.
- Loading branch information
Alan Fleming
committed
Nov 22, 2024
1 parent
d2ee1ce
commit 312b338
Showing
20 changed files
with
1,200 additions
and
472 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,328 @@ | ||
{ | ||
"cells": [ | ||
{ | ||
"cell_type": "markdown", | ||
"id": "0", | ||
"metadata": {}, | ||
"source": [ | ||
"**To use this notebook:** Run one line at a time waiting for each cell to return before running the next cell." | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"id": "1", | ||
"metadata": { | ||
"jp-MarkdownHeadingCollapsed": true | ||
}, | ||
"source": [ | ||
"## Simple Output\n", | ||
"\n", | ||
"SimpleOutput is a widget that provides an output area to display all types of output. \n", | ||
"\n", | ||
"It is designed to minimise the size of messages and/or number of messages sent to the frontend. It is not supposed to be a drop in replacement for the Ipywidget `Output' widget, rather it provides an alternate type of interface.\n", | ||
"\n", | ||
"Compared to the Ipywidgets `Output` maintains a synchronised model of all loaded outputs. Each item added to `SimpleOutput` is serialized and sent to the frontend. There is no representation of the data left on the Python side meaning that `SimpleOutput` is more suitable for logging applications. " | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"id": "2", | ||
"metadata": {}, | ||
"source": [ | ||
"## Methods\n", | ||
"\n", | ||
"There are two methods to add outputs \n", | ||
"1. `push`\n", | ||
"2. `set`\n", | ||
"\n", | ||
"and one '`clear`' to clear the outputs.\n" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"id": "3", | ||
"metadata": {}, | ||
"source": [ | ||
"### `push`\n", | ||
"\n", | ||
"`push` serializes and sends data as a custom message which is appended to the existing output." | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"id": "4", | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"import ipylab\n", | ||
"from ipylab.simple_output import SimpleOutput" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"id": "5", | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"so = SimpleOutput(layout={\"max_height\": \"200px\"})" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"id": "6", | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"for i in range(50):\n", | ||
" so.push(f\"test {i}\\n\")" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"id": "7", | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"so" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"id": "8", | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"# or we could do it with one message\n", | ||
"so.clear()\n", | ||
"so.push(*(f\"test {i}\\n\" for i in range(100)))\n", | ||
"# Not that we've just sent 100 'outputs' in one message.\n", | ||
"# All of the 100 lines are squashed into the 200px high simple output." | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"id": "9", | ||
"metadata": {}, | ||
"source": [ | ||
"### set\n", | ||
"\n", | ||
"`Set` is similar to push, but is run as task and clears the output prior at adding the new outputs. The task returns the number of outputs in use." | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"id": "10", | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"t = so.set(\"Line one\\n\", \"Line two\")" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"id": "11", | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"so" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"id": "12", | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"assert so.length == t.result() # noqa: S101\n", | ||
"so.length" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"id": "13", | ||
"metadata": {}, | ||
"source": [ | ||
"## max_continuous_streams\n", | ||
"\n", | ||
"Notice that above the length is 1 even though we sent two values? \n", | ||
"\n", | ||
"This is because both items are streams, and by default they get put into the same output in the frontend. \n", | ||
"\n", | ||
"The maximum number of consecutive streams is configurable with `max_continuous_streams`." | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"id": "14", | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"# Make each stream go into a new output.\n", | ||
"so.max_continuous_streams = 0" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"id": "15", | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"t = so.set(\"Line one\\n\", \"Line two\")" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"id": "16", | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"assert so.length == t.result() # noqa: S101\n", | ||
"so.length" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"id": "17", | ||
"metadata": {}, | ||
"source": [ | ||
"# AutoScroll\n", | ||
"\n", | ||
"AutoScroll is a widget that provides automatic scrolling around a content widget. It is intended to be used in panels placed in the shell, and doesn't work correctly when used in notebooks.\n", | ||
"\n", | ||
"**Note**\n", | ||
"\n", | ||
"Autoscroll uses a relatively new feature `onscrollend` ([detail](https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollend_event)) and **may not work well on Safari** for fast update rates." | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"id": "18", | ||
"metadata": {}, | ||
"source": [ | ||
"## Builtin log viewer\n", | ||
"\n", | ||
"The built in log viewer uses the AutoScroll widget to scroll its output." | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"id": "19", | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"app = ipylab.app\n", | ||
"\n", | ||
"app.log_viewer.add_to_shell()\n", | ||
"app.log_level = \"INFO\"" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"id": "20", | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"for _ in range(100):\n", | ||
" app.log.info(\"Demo\")\n", | ||
" app.log.error(\"Demo\")" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"id": "21", | ||
"metadata": {}, | ||
"source": [ | ||
"## Example usage" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"id": "22", | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"from datetime import datetime\n", | ||
"\n", | ||
"import ipywidgets as ipw\n", | ||
"\n", | ||
"import ipylab\n", | ||
"from ipylab.simple_output import AutoScroll" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"id": "23", | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"vb = ipw.VBox()\n", | ||
"sw = AutoScroll(content=vb)\n", | ||
"sw_holder = ipw.VBox([sw], layout={\"height\": \"200px\", \"border\": \"solid\"})\n", | ||
"\n", | ||
"enabled = ipw.Checkbox(description=\"Auto scroll\", indent=False)\n", | ||
"ipw.link((sw, \"enabled\"), (enabled, \"value\"))\n", | ||
"sleep = ipw.FloatSlider(description=\"Sleep time (s)\", value=0.3, min=0.05, max=1, step=0.01)\n", | ||
"\n", | ||
"b = ipw.Button(description=\"Start\")\n", | ||
"\n", | ||
"\n", | ||
"def on_click(b):\n", | ||
" if b.description == \"Start\":\n", | ||
" import asyncio\n", | ||
"\n", | ||
" async def generate_output():\n", | ||
" while True:\n", | ||
" vb.children = (*vb.children, ipw.HTML(f\"It is now {datetime.now().isoformat()}\")) # noqa: DTZ005\n", | ||
" await asyncio.sleep(sleep.value)\n", | ||
"\n", | ||
" b.task = ipylab.app.to_task(generate_output())\n", | ||
" b.description = \"Stop\"\n", | ||
" else:\n", | ||
" b.task.cancel()\n", | ||
" b.description = \"Start\"\n", | ||
"\n", | ||
"\n", | ||
"b.on_click(on_click)\n", | ||
"\n", | ||
"p = ipylab.Panel([ipw.HBox([enabled, sleep, b], layout={\"justify_content\": \"center\"}), sw_holder])\n", | ||
"p.add_to_shell(mode=ipylab.InsertMode.split_right)" | ||
] | ||
} | ||
], | ||
"metadata": { | ||
"kernelspec": { | ||
"display_name": "Python 3 (ipykernel)", | ||
"language": "python", | ||
"name": "python3" | ||
}, | ||
"language_info": { | ||
"codemirror_mode": { | ||
"name": "ipython", | ||
"version": 3 | ||
}, | ||
"file_extension": ".py", | ||
"mimetype": "text/x-python", | ||
"name": "python", | ||
"nbconvert_exporter": "python", | ||
"pygments_lexer": "ipython3", | ||
"version": "3.11.10" | ||
} | ||
}, | ||
"nbformat": 4, | ||
"nbformat_minor": 5 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.