{ "cells": [ { "cell_type": "code", "execution_count": 1, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "import kotlinx.serialization.*\n", "import kotlinx.serialization.json.*" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "@Serializable\n", "data class Pt(val x: Int, val y: Int)" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "data": { "text/plain": [ "{\"x\":2,\"y\":3}" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Json.encodeToString(Pt(2, 3))" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "var pt: Pt? = null\n", "notebook.commManager.registerCommTarget(\"t_clicker\") { comm, openData -> \n", " comm.onData { msgPt ->\n", " // Thread.sleep(3000)\n", " pt = msgPt \n", " }\n", "}" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "data": { "text/html": [ "
\n", "
\n", "" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "HTML(\n", " \"\"\"\n", "
\n", "
\n", " \n", " \"\"\".trimIndent()\n", ")" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "data": { "text/plain": [ "Pt(x=144, y=101)" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pt" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "interface Counter {\n", " fun inc()\n", " fun dec()\n", " val value: Int\n", "}" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "class Counter1(private var cnt: Int = 0): Counter {\n", " override fun inc() { ++cnt }\n", " override fun dec() { --cnt }\n", " \n", " override val value: Int get() = cnt\n", "}\n", "\n", "USE { \n", " render { \n", " HTML(\"\"\"\n", " \n", " ${it.value}\n", " \n", " \n", " \n", " \"\"\".trimIndent())\n", " }\n", "}" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "data": { "text/html": [ "\n", "0\n", "\n", "\n", "" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "val cnt1 = Counter1()\n", "cnt1" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "data": { "text/plain": [ "0" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "cnt1.value" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": 12, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "import kotlinx.serialization.*\n", "import kotlinx.serialization.json.*\n", "\n", "@Serializable\n", "class CounterOpen(val id: String)\n", "\n", "@Serializable\n", "class CounterCommand(val command: String)\n", "\n", "@Serializable\n", "class CounterValueUpdate(val value: Int)\n", "\n", "\n", "object CounterFactory {\n", " private val idToCtr = mutableMapOf()\n", " private val idToComm = mutableMapOf()\n", " \n", " fun create(): Counter {\n", " val ctr = CounterImpl()\n", " idToCtr[ctr.id] = ctr\n", " return ctr\n", " }\n", " \n", " fun addComm(id: String, comm: Comm) {\n", " idToComm[id] = comm\n", " }\n", " \n", " fun getCounter(id: String): Counter = idToCtr[id]!!\n", " \n", " fun updateValue(id: String, value: Int) {\n", " val comm = idToComm[id] ?: return\n", " comm.sendData(CounterValueUpdate(value))\n", " }\n", " \n", " private class CounterImpl(\n", " private var cnt: Int = 0,\n", " val id: String = java.util.UUID.randomUUID().toString()\n", " ): Counter, Renderable {\n", " override fun inc() {\n", " ++cnt\n", " CounterFactory.updateValue(id, value)\n", " }\n", " override fun dec() {\n", " --cnt\n", " CounterFactory.updateValue(id, value)\n", " }\n", "\n", " override val value: Int get() = cnt \n", " \n", " override fun render(notebook: Notebook): DisplayResult {\n", " return HTML(\"\"\"\n", " \n", " $value\n", " \n", "\n", " \n", " \"\"\".trimIndent())\n", " }\n", " } \n", "}\n", "\n", "\n", "\n", "notebook.commManager.registerCommTarget(\"t_counter\") { comm, openData ->\n", " val counterId = Json.decodeFromJsonElement(openData).id\n", " CounterFactory.addComm(counterId, comm)\n", "\n", " val counter = CounterFactory.getCounter(counterId)\n", " \n", " comm.onData { d ->\n", " val command = d.command\n", " when(command) {\n", " \"inc\" -> counter.inc()\n", " \"dec\" -> counter.dec()\n", " }\n", " }\n", "}" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "val ctr2 = CounterFactory.create()" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "data": { "text/html": [ "\n", "0\n", "\n", "\n", "" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ctr2" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "data": { "text/plain": [ "6" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ctr2.value" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "ctr2.inc()" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "for (i in 1..10) {\n", " ctr2.inc()\n", " Thread.sleep(500)\n", "}" ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "val ctr3 = CounterFactory.create()" ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "data": { "text/html": [ "\n", "0\n", "\n", "\n", "" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ctr3" ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "data": { "text/html": [ "\n", "0\n", "\n", "\n", "" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ctr3" ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "ctr3.inc()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": 26, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "import kotlinx.serialization.*\n", "import kotlinx.serialization.json.*\n", "\n", "interface WidgetConstrictorArg\n", "\n", "interface WidgetState {\n", " val json: JsonObject\n", "}\n", "\n", "interface WidgetCommand\n", "\n", "interface Widget {\n", " val id: String\n", " val state: S\n", " var comm: Comm? \n", " \n", " // call it after every kernel-side update\n", " fun syncState() {\n", " comm?.send(state.json)\n", " }\n", " \n", " fun updateState(command: C)\n", " \n", " // Generally HTML code. May call JS function kotlinCommSend(target, id, command)\n", " // `command` will be deserialized to type C\n", " fun renderState(targetName: String): String\n", " \n", " // arguments are `elem` and `state`. `elem` is a div container where all generated with renderState() is held.\n", " fun setStateJs(): String\n", "}\n", "\n", "abstract class WidgetFactory>(\n", " private val targetName: String,\n", " widgetClass: kotlin.reflect.KClass,\n", " notebook: Notebook\n", ") {\n", " protected abstract fun createImpl(args: A): W\n", " protected abstract fun deserializeCommand(json: JsonObject): C\n", " \n", " private val idToWidget = mutableMapOf()\n", " \n", " init {\n", " notebook.commManager.registerCommTarget(targetName) { comm, openData ->\n", " val widgetId = openData[\"id\"]!!.jsonPrimitive.content\n", " addComm(widgetId, comm)\n", "\n", " val widget = getWidget(widgetId)\n", "\n", " comm.onMessage { d ->\n", " val command = deserializeCommand(d)\n", " widget.updateState(command)\n", " }\n", " \n", " widget.syncState()\n", " }\n", " \n", " notebook.renderersProcessor.registerWithoutOptimizing(createRenderer(widgetClass) { w ->\n", " HTML(\n", " \"\"\"\n", "
${w.renderState(targetName)}
\n", "\n", " \n", " \"\"\".trimIndent()\n", " )\n", " })\n", " }\n", " \n", " \n", " fun create(args: A): W {\n", " return createImpl(args).also { w -> idToWidget[w.id] = w }\n", " }\n", " \n", " fun addComm(id: String, comm: Comm) {\n", " val widget = idToWidget[id] ?: return\n", " widget.comm = comm\n", " }\n", " \n", " fun getWidget(id: String): W = idToWidget[id]!!\n", " \n", " \n", "}" ] }, { "cell_type": "code", "execution_count": 27, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "class ListWidgetArgs(\n", " val list: List,\n", " val shownElementsLimit: Int,\n", "): WidgetConstrictorArg\n", "\n", "@Serializable\n", "class ListWidgetState(\n", " val d: String\n", ") : WidgetState {\n", " override val json get() = Json.encodeToJsonElement(this).jsonObject\n", "}\n", "\n", "@Serializable\n", "class ListWidgetCommand : WidgetCommand\n", "\n", "class ListWidget(\n", " override val id: String,\n", " val data: List,\n", " private var shownElementsLimit: Int,\n", "): Widget {\n", " override val state get() = ListWidgetState(data.take(minOf(shownElementsLimit, data.size)).toString())\n", " override var comm: Comm? = null\n", " \n", " override fun updateState(command: ListWidgetCommand) {\n", " showMore()\n", " }\n", " \n", " fun showMore() {\n", " shownElementsLimit += 10\n", " syncState()\n", " }\n", " \n", " override fun renderState(targetName: String): String {\n", " return \"\"\"\n", " \n", " ${state.d}\n", " \"\"\"\n", " }\n", " \n", " override fun setStateJs(): String {\n", " return \"\"\"\n", " const contentElem = elem.getElementsByClassName('listContent')[0];\n", " contentElem.innerHTML = state.d;\n", " \"\"\"\n", " }\n", " \n", "}\n", "\n", "class ListWidgetFactory(notebook: Notebook): WidgetFactory(\"t_list\", ListWidget::class, notebook) {\n", " override fun createImpl(args: ListWidgetArgs): ListWidget {\n", " return ListWidget(java.util.UUID.randomUUID().toString(), args.list, args.shownElementsLimit)\n", " }\n", " override fun deserializeCommand(json: JsonObject): ListWidgetCommand {\n", " return Json.decodeFromJsonElement(json)\n", " }\n", "}" ] }, { "cell_type": "code", "execution_count": 28, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "val listFactory = ListWidgetFactory(notebook)\n", "val listWidget = listFactory.create(ListWidgetArgs((1..1000).toList(), 5))" ] }, { "cell_type": "code", "execution_count": 29, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "data": { "text/html": [ "
\n", " \n", " [1, 2, 3, 4, 5]\n", "
\n", "\n", " " ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ "listWidget" ] }, { "cell_type": "code", "execution_count": 30, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "listWidget.showMore()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "listWidget" ] }, { "cell_type": "code", "execution_count": 31, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "class ListWidgetArgs(\n", " val list: List,\n", " val shownElementsLimit: Int,\n", "): WidgetConstrictorArg\n", "\n", "@Serializable\n", "class ListWidgetState(\n", " val d: String\n", ") : WidgetState {\n", " override val json get() = Json.encodeToJsonElement(this).jsonObject\n", "}\n", "\n", "@Serializable\n", "class ListWidgetCommand : WidgetCommand\n", "\n", "class ListWidget(\n", " override val id: String,\n", " val data: MutableList,\n", " private var shownElementsLimit: Int,\n", "): Widget {\n", " override val state get() = ListWidgetState(data.take(minOf(shownElementsLimit, data.size)).toString())\n", " override var comm: Comm? = null\n", " \n", " override fun updateState(command: ListWidgetCommand) {\n", " showMore()\n", " }\n", " \n", " fun showMore() {\n", " shownElementsLimit += 10\n", " syncState()\n", " }\n", " \n", " operator fun set(i: Int, v: Int) {\n", " data[i] = v\n", " syncState()\n", " }\n", " \n", " override fun renderState(targetName: String): String {\n", " return \"\"\"\n", " \n", " ${state.d}\n", " \"\"\"\n", " }\n", " \n", " override fun setStateJs(): String {\n", " return \"\"\"\n", " const contentElem = elem.getElementsByClassName('listContent')[0];\n", " contentElem.innerHTML = state.d;\n", " \"\"\"\n", " }\n", " \n", "}\n", "\n", "class ListWidgetFactory(notebook: Notebook): WidgetFactory(\"t_list2\", ListWidget::class, notebook) {\n", " override fun createImpl(args: ListWidgetArgs): ListWidget {\n", " return ListWidget(java.util.UUID.randomUUID().toString(), args.list.toMutableList(), args.shownElementsLimit)\n", " }\n", " override fun deserializeCommand(json: JsonObject): ListWidgetCommand {\n", " return Json.decodeFromJsonElement(json)\n", " }\n", "}" ] }, { "cell_type": "code", "execution_count": 32, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "val listFactory = ListWidgetFactory(notebook)\n", "val listWidget = listFactory.create(ListWidgetArgs((1..1000).toList(), 5))" ] }, { "cell_type": "code", "execution_count": 33, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [ { "data": { "text/html": [ "
\n", " \n", " [1, 2, 3, 4, 5]\n", "
\n", "\n", " " ] }, "execution_count": 33, "metadata": {}, "output_type": "execute_result" } ], "source": [ "listWidget" ] }, { "cell_type": "code", "execution_count": 35, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "listWidget[10] = 42" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Kotlin", "language": "kotlin", "name": "kotlin" }, "language_info": { "codemirror_mode": "text/x-kotlin", "file_extension": ".kt", "mimetype": "text/x-kotlin", "name": "kotlin", "nbconvert_exporter": "", "pygments_lexer": "kotlin", "version": "1.7.20-dev-1299" } }, "nbformat": 4, "nbformat_minor": 4 }