{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "Python Code" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import ipywidgets.widgets as widgets\n", "from traitlets import Unicode, Dict, List\n", "from random import randint\n", "\n", "class PolygonSelector(widgets.DOMWidget):\n", " _view_name = Unicode('PolygonSelectorView').tag(sync=True)\n", " _view_module = Unicode('polygonselector').tag(sync=True)\n", " groups_dict = Dict().tag(sync=True)\n", " current_list = List().tag(sync=True)\n", " content = Unicode().tag(sync=True)\n", "\n", " html_template = '''\n", " \n", " \n", " \n", " \n", "
{svg}
\n", " '''\n", "\n", " # provide some default colors; can override if desired\n", " fill_selected = \"plum\"\n", " fill_hovered = \"lavender\"\n", " group_colors = [\"#{:06X}\".format(randint(0,0xFFFFFF)) for _ in range(100)]\n", "\n", " def __init__(self, svg):\n", " super().__init__()\n", " self.update_content(svg)\n", "\n", " def update_content(self, svg):\n", " self.content = self.html_template.format(\n", " fill_selected = self.fill_selected,\n", " fill_hovered = self.fill_hovered,\n", " selection_styles = self.selection_styles,\n", " svg = svg\n", " )\n", "\n", " @property\n", " def selection_styles(self):\n", " return \"\".join(f'''\n", " # polygonGeometry .selection_{group_idx} {{\n", " fill: {self.group_colors[group_idx]!r};\n", " }}\n", " ''' for group_idx in range(len(self.groups_dict)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Javascript Code" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "application/javascript": [ "\n", "require.undef('polygonselector');\n", "\n", "define('polygonselector', [\"@jupyter-widgets/base\"], function(widgets) {\n", "\n", " var PolygonSelectorView = widgets.DOMWidgetView.extend({\n", "\n", " initialized: 0,\n", "\n", " init_render: function(){\n", "\n", " },\n", "\n", "\n", " // Add item to selection list\n", " add: function(id) {\n", " this.current_list.push(id);\n", " console.log('pushed #', id);\n", " },\n", "\n", " // Remove item from selection list\n", " remove: function(id) {\n", " this.current_list = this.current_list.filter(function(_id) {\n", " return _id !== id;\n", " })\n", " console.log('filtered #', id);\n", " },\n", "\n", " // Remove all items, closure\n", " clear: function(thisView) {\n", " return function() {\n", " // `this` is the button element\n", " console.log('clear() clicked');\n", " thisView.el.querySelector('#name').value = '';\n", " thisView.current_list.length = 0;\n", " Array.from(thisView.el.querySelectorAll('.selectedPolygon')).forEach(function(path) {\n", " console.log(\"path classList is: \", path.classList)\n", " path.classList.remove('selectedPolygon');\n", " })\n", " console.log('Data cleared');\n", " console.log(thisView.current_list)\n", " };\n", " },\n", "\n", " // Add current selection to groups_dict, closure\n", " save: function(thisView) {\n", " return function() {\n", " // `this` is the button element\n", " console.log('save() clicked');\n", " const newName = thisView.el.querySelector('#name').value;\n", " console.log('Current name: ', newName)\n", " if (!newName || thisView.current_list.length < 1) {\n", " console.log(\"Can't save, newName: \", newName, \" list length: \", thisView.current_list.length)\n", " alert('A new selection must have a name and selected polygons');\n", " }\n", " else {\n", " console.log('Attempting to save....')\n", " thisView.groups_dict[newName] = thisView.current_list.slice(0)\n", " console.log('You saved some data');\n", " console.log(\"Selection Name: \", newName);\n", " console.log(thisView.groups_dict[newName]);\n", " thisView.model.trigger('change:groups_dict');\n", " }\n", " }\n", " },\n", "\n", " render: function() {\n", " PolygonSelectorView.__super__.render.apply(this, arguments);\n", " this.groups_dict = this.model.get('groups_dict')\n", " this.current_list = this.model.get('current_list')\n", "\n", " this.content_changed();\n", " this.el.innerHTML = `${this.model.get('content')}`;\n", "\n", " this.model.on('change:content', this.content_changed, this);\n", " this.model.on('change:current_list', this.content_changed, this);\n", " this.model.on('change:groups_dict', this.content_changed, this);\n", "\n", " // Each path element is a polygon\n", " const polygons = this.el.querySelectorAll('#polygonGeometry path');\n", "\n", " // Add click event to polygons\n", " console.log('iterating through polygons');\n", " var thisView = this\n", " let arr = Array.from(polygons)\n", " console.log('created array:', arr)\n", " arr.forEach(function(path, i) {\n", " console.log(\"Array item #\", i)\n", " path.addEventListener('click', function() {\n", " console.log('path object clicked')\n", " if (thisView.current_list.includes(i)) {\n", " path.classList.remove('selectedPolygon')\n", " thisView.remove(i);\n", " console.log('path #', i, ' removed');\n", " } else {\n", " path.classList.add('selectedPolygon')\n", " thisView.add(i);\n", " console.log('path #', i, ' added');\n", " }\n", " thisView.content_changed();\n", " });\n", " console.log('path #', i, ' click set');\n", " });\n", "\n", " // Attach functions to buttons\n", " this.el.querySelector('#clearBtn').addEventListener('click', this.clear(this));\n", " console.log('clearBtn action set to current view context');\n", " this.el.querySelector('#saveBtn').addEventListener('click', this.save(this));\n", " console.log('saveBtn action set to current view context');\n", "\n", " console.log('render exit')\n", "\n", " },\n", "\n", " content_changed: function() {\n", " console.log('content changed');\n", " this.model.save();\n", " console.log(\"Current list: \", this.current_list);\n", " console.log(\"Groups dict: \", this.groups_dict);\n", " },\n", " });\n", "\n", " return {\n", " PolygonSelectorView : PolygonSelectorView\n", " };\n", "});" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%javascript\n", "\n", "require.undef('polygonselector');\n", "\n", "define('polygonselector', [\"@jupyter-widgets/base\"], function(widgets) {\n", "\n", " var PolygonSelectorView = widgets.DOMWidgetView.extend({\n", "\n", " initialized: 0,\n", "\n", " init_render: function(){\n", "\n", " },\n", "\n", "\n", " // Add item to selection list\n", " add: function(id) {\n", " this.current_list.push(id);\n", " console.log('pushed #', id);\n", " },\n", "\n", " // Remove item from selection list\n", " remove: function(id) {\n", " this.current_list = this.current_list.filter(function(_id) {\n", " return _id !== id;\n", " })\n", " console.log('filtered #', id);\n", " },\n", "\n", " // Remove all items, closure\n", " clear: function(thisView) {\n", " return function() {\n", " // `this` is the button element\n", " console.log('clear() clicked');\n", " thisView.el.querySelector('#name').value = '';\n", " thisView.current_list.length = 0;\n", " Array.from(thisView.el.querySelectorAll('.selectedPolygon')).forEach(function(path) {\n", " console.log(\"path classList is: \", path.classList)\n", " path.classList.remove('selectedPolygon');\n", " })\n", " console.log('Data cleared');\n", " console.log(thisView.current_list)\n", " };\n", " },\n", "\n", " // Add current selection to groups_dict, closure\n", " save: function(thisView) {\n", " return function() {\n", " // `this` is the button element\n", " console.log('save() clicked');\n", " const newName = thisView.el.querySelector('#name').value;\n", " console.log('Current name: ', newName)\n", " if (!newName || thisView.current_list.length < 1) {\n", " console.log(\"Can't save, newName: \", newName, \" list length: \", thisView.current_list.length)\n", " alert('A new selection must have a name and selected polygons');\n", " }\n", " else {\n", " console.log('Attempting to save....')\n", " thisView.groups_dict[newName] = thisView.current_list.slice(0)\n", " console.log('You saved some data');\n", " console.log(\"Selection Name: \", newName);\n", " console.log(thisView.groups_dict[newName]);\n", " thisView.model.trigger('change:groups_dict');\n", " }\n", " }\n", " },\n", "\n", " render: function() {\n", " PolygonSelectorView.__super__.render.apply(this, arguments);\n", " this.groups_dict = this.model.get('groups_dict')\n", " this.current_list = this.model.get('current_list')\n", "\n", " this.content_changed();\n", " this.el.innerHTML = `${this.model.get('content')}`;\n", "\n", " this.model.on('change:content', this.content_changed, this);\n", " this.model.on('change:current_list', this.content_changed, this);\n", " this.model.on('change:groups_dict', this.content_changed, this);\n", "\n", " // Each path element is a polygon\n", " const polygons = this.el.querySelectorAll('#polygonGeometry path');\n", "\n", " // Add click event to polygons\n", " console.log('iterating through polygons');\n", " var thisView = this\n", " let arr = Array.from(polygons)\n", " console.log('created array:', arr)\n", " arr.forEach(function(path, i) {\n", " console.log(\"Array item #\", i)\n", " path.addEventListener('click', function() {\n", " console.log('path object clicked')\n", " if (thisView.current_list.includes(i)) {\n", " path.classList.remove('selectedPolygon')\n", " thisView.remove(i);\n", " console.log('path #', i, ' removed');\n", " } else {\n", " path.classList.add('selectedPolygon')\n", " thisView.add(i);\n", " console.log('path #', i, ' added');\n", " }\n", " thisView.content_changed();\n", " });\n", " console.log('path #', i, ' click set');\n", " });\n", "\n", " // Attach functions to buttons\n", " this.el.querySelector('#clearBtn').addEventListener('click', this.clear(this));\n", " console.log('clearBtn action set to current view context');\n", " this.el.querySelector('#saveBtn').addEventListener('click', this.save(this));\n", " console.log('saveBtn action set to current view context');\n", "\n", " console.log('render exit')\n", "\n", " },\n", "\n", " content_changed: function() {\n", " console.log('content changed');\n", " this.model.save();\n", " console.log(\"Current list: \", this.current_list);\n", " console.log(\"Groups dict: \", this.groups_dict);\n", " },\n", " });\n", "\n", " return {\n", " PolygonSelectorView : PolygonSelectorView\n", " };\n", "});" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Use Tool" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "scrolled": true }, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "0f13f1d9d66e43f8b9433d0c355a5737", "version_major": 2, "version_minor": 0 }, "text/plain": [ "PolygonSelector(content='\\n