From 7869153d06311b0ba29fc8be980e47c9627c5d26 Mon Sep 17 00:00:00 2001 From: Caitlin Lewis Date: Wed, 17 May 2023 16:27:54 -0400 Subject: [PATCH 01/12] working on toolbar --- examples/buttons.ipynb | 188 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 188 insertions(+) create mode 100644 examples/buttons.ipynb diff --git a/examples/buttons.ipynb b/examples/buttons.ipynb new file mode 100644 index 000000000..a6be76cb7 --- /dev/null +++ b/examples/buttons.ipynb @@ -0,0 +1,188 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "6725ce7d-eea7-44f7-bedc-813e8ce5bf4f", + "metadata": {}, + "outputs": [], + "source": [ + "from fastplotlib import Plot\n", + "from ipywidgets import HBox, Checkbox, Image, VBox, Layout" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "6e893bc8-26e3-4ec4-968f-35f6f876ddfe", + "metadata": {}, + "outputs": [], + "source": [ + "# need to create some icons\n", + "# put icons in an HBox\n", + "tools = list()" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "7415ec97-ee57-4061-8906-e6bf8a6b91b7", + "metadata": {}, + "outputs": [], + "source": [ + "# auto scale tool button\n", + "img = open(\"/home/clewis7/Desktop/autoscale.png\", \"rb\")\n", + "image = img.read()\n", + "auto = Image(value=image, format='png', width=50, height=50)\n", + "tools.append(auto)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "2ce8ff08-217c-4d1f-8e41-f64ebc8057e9", + "metadata": {}, + "outputs": [], + "source": [ + "# center scene tool button\n", + "img = open(\"/home/clewis7/Desktop/center.png\", \"rb\")\n", + "image = img.read()\n", + "center = Image(value=image, format='png', width=50, height=50)\n", + "tools.append(center)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "648c1242-b472-4269-b67d-85d114199cd4", + "metadata": {}, + "outputs": [], + "source": [ + "img = open(\"/home/clewis7/Desktop/panzoom.png\", \"rb\")\n", + "image = img.read()\n", + "panzoom = Image(value=image, format='png', width=50, height=50)\n", + "tools.append(panzoom)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "9e05ed4a-3ce9-4c0f-a684-034a12a853d1", + "metadata": {}, + "outputs": [], + "source": [ + "# maintain aspect tool button\n", + "m_aspect = Checkbox(value=False, description=\"maintain aspect\", indent=False)\n", + "tools.append(m_aspect)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "4888dc48-f422-4c1e-97eb-cad4cc390b58", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "acb3f6627dba429781ed3b0204033f37", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(Image(value=b'\\x89PNG\\r\\n\\x1a\\n\\x00\\x00\\x00\\rIHDR\\x00\\x00\\x02\\xbc\\x00\\x00\\x02\\xbc\\x08\\x06\\x00\\x…" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "HBox(tools)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "33bf59c4-14e5-43a8-8a16-69b6859864c5", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "e30cabb3660c43b8bd402620a3179ccc", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "RFBOutputContext()" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plot = Plot()" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "c02159c1-ddcf-4e2d-a0aa-67c73112777e", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "ea23c58912594fb5bfe0376c33223c8d", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "VBox(children=(JupyterWgpuCanvas(), HBox(children=(Image(value=b'\\x89PNG\\r\\n\\x1a\\n\\x00\\x00\\x00\\rIHDR\\x00\\x00\\x…" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "VBox([plot.show(), HBox(tools)])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "46b9b7af-a029-4c9b-a0d7-55614b618ea5", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "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.9.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From 6a9fa9ab5979aaccbded0debae75f96f5bc8291d Mon Sep 17 00:00:00 2001 From: Caitlin Lewis Date: Thu, 18 May 2023 11:51:12 -0400 Subject: [PATCH 02/12] toolbar updates --- examples/buttons.ipynb | 108 +++++++++++++++++++++++++++++++++-------- 1 file changed, 88 insertions(+), 20 deletions(-) diff --git a/examples/buttons.ipynb b/examples/buttons.ipynb index a6be76cb7..25d84f315 100644 --- a/examples/buttons.ipynb +++ b/examples/buttons.ipynb @@ -8,7 +8,8 @@ "outputs": [], "source": [ "from fastplotlib import Plot\n", - "from ipywidgets import HBox, Checkbox, Image, VBox, Layout" + "from ipywidgets import HBox, Checkbox, Image, VBox, Layout, ToggleButton, Button\n", + "import numpy as np" ] }, { @@ -26,41 +27,36 @@ { "cell_type": "code", "execution_count": 3, - "id": "7415ec97-ee57-4061-8906-e6bf8a6b91b7", + "id": "8c519521-3409-45a8-89b8-b02baca20b24", "metadata": {}, "outputs": [], "source": [ "# auto scale tool button\n", - "img = open(\"/home/clewis7/Desktop/autoscale.png\", \"rb\")\n", - "image = img.read()\n", - "auto = Image(value=image, format='png', width=50, height=50)\n", + "auto = Button(value=False, disabled=False, icon='expand-arrows-alt', layout=Layout(width='auto'))\n", "tools.append(auto)" ] }, { "cell_type": "code", "execution_count": 4, - "id": "2ce8ff08-217c-4d1f-8e41-f64ebc8057e9", + "id": "e47e763a-7d90-4455-9d28-e93a8d3726f1", "metadata": {}, "outputs": [], "source": [ "# center scene tool button\n", - "img = open(\"/home/clewis7/Desktop/center.png\", \"rb\")\n", - "image = img.read()\n", - "center = Image(value=image, format='png', width=50, height=50)\n", + "center = Button(value=False, disabled=False, icon='compress-arrows-alt', layout=Layout(width='auto'))\n", "tools.append(center)" ] }, { "cell_type": "code", "execution_count": 5, - "id": "648c1242-b472-4269-b67d-85d114199cd4", + "id": "253cd1ce-aab3-41a1-a86a-dd155e0cedfe", "metadata": {}, "outputs": [], "source": [ - "img = open(\"/home/clewis7/Desktop/panzoom.png\", \"rb\")\n", - "image = img.read()\n", - "panzoom = Image(value=image, format='png', width=50, height=50)\n", + "# panzoom tool button\n", + "panzoom = ToggleButton(value=False, disabled=False, icon='hand-pointer', layout=Layout(width='auto'))\n", "tools.append(panzoom)" ] }, @@ -87,12 +83,12 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "acb3f6627dba429781ed3b0204033f37", + "model_id": "9622defc63654f878af4e2299d5f70ba", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "HBox(children=(Image(value=b'\\x89PNG\\r\\n\\x1a\\n\\x00\\x00\\x00\\rIHDR\\x00\\x00\\x02\\xbc\\x00\\x00\\x02\\xbc\\x08\\x06\\x00\\x…" + "HBox(children=(Button(icon='expand-arrows-alt', layout=Layout(width='auto'), style=ButtonStyle()), Button(icon…" ] }, "execution_count": 7, @@ -113,7 +109,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "e30cabb3660c43b8bd402620a3179ccc", + "model_id": "c4bf363bbc4c44f6876f8336c07ba526", "version_major": 2, "version_minor": 0 }, @@ -123,10 +119,30 @@ }, "metadata": {}, "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/clewis7/repos/fastplotlib/fastplotlib/graphics/features/_base.py:33: UserWarning: converting float64 array to float32\n", + " warn(f\"converting {array.dtype} array to float32\")\n" + ] + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ - "plot = Plot()" + "plot = Plot()\n", + "img = np.random.rand(512, 512)\n", + "plot.add_image(img)" ] }, { @@ -138,12 +154,12 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "ea23c58912594fb5bfe0376c33223c8d", + "model_id": "667f2e2de82b4f6abe7d9f2dbccb9e95", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "VBox(children=(JupyterWgpuCanvas(), HBox(children=(Image(value=b'\\x89PNG\\r\\n\\x1a\\n\\x00\\x00\\x00\\rIHDR\\x00\\x00\\x…" + "VBox(children=(JupyterWgpuCanvas(), HBox(children=(Button(icon='expand-arrows-alt', layout=Layout(width='auto'…" ] }, "execution_count": 9, @@ -157,10 +173,62 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 10, "id": "46b9b7af-a029-4c9b-a0d7-55614b618ea5", "metadata": {}, "outputs": [], + "source": [ + "def auto_scale(b):\n", + " if m_aspect.value: \n", + " plot.auto_scale(maintain_aspect=True)\n", + " else:\n", + " plot.auto_scale()\n", + "def center_scene(b):\n", + " plot.center_scene()" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "f02c536f-32fa-484f-9a85-7549f83a63d9", + "metadata": {}, + "outputs": [], + "source": [ + "auto.on_click(auto_scale)\n", + "center.on_click(center_scene)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "2df49dec-e547-4912-886f-8828a70a96aa", + "metadata": {}, + "outputs": [], + "source": [ + "def panzoom_control(obj):\n", + " if panzoom.value:\n", + " # toggle pan zoom controller\n", + " plot.controller.enabled = False\n", + " else: \n", + " plot.controller.enabled = True" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "a4798429-0db2-48ea-ab2e-24e54fc35541", + "metadata": {}, + "outputs": [], + "source": [ + "panzoom.observe(panzoom_control, 'value')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "49d42ecf-41d6-40da-a41e-842570e21876", + "metadata": {}, + "outputs": [], "source": [] } ], From 4bab1cec6be50c652ea4508163c22d21e7970e6c Mon Sep 17 00:00:00 2001 From: Caitlin Lewis Date: Fri, 19 May 2023 13:25:46 -0400 Subject: [PATCH 03/12] basic toolbar for plot implemented --- examples/buttons.ipynb | 41 ++++++++++++++++++++++++------ fastplotlib/plot.py | 57 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 90 insertions(+), 8 deletions(-) diff --git a/examples/buttons.ipynb b/examples/buttons.ipynb index 25d84f315..235f520e2 100644 --- a/examples/buttons.ipynb +++ b/examples/buttons.ipynb @@ -68,7 +68,8 @@ "outputs": [], "source": [ "# maintain aspect tool button\n", - "m_aspect = Checkbox(value=False, description=\"maintain aspect\", indent=False)\n", + "m_aspect = ToggleButton(value=False, disabled=False, description=\"1:1\", layout=Layout(width='auto'))\n", + "m_aspect.style.font_weight = \"bold\"\n", "tools.append(m_aspect)" ] }, @@ -83,7 +84,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "9622defc63654f878af4e2299d5f70ba", + "model_id": "5a28ab7e957a43409f0ca0863780a394", "version_major": 2, "version_minor": 0 }, @@ -102,14 +103,14 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 2, "id": "33bf59c4-14e5-43a8-8a16-69b6859864c5", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "c4bf363bbc4c44f6876f8336c07ba526", + "model_id": "0472ef2ee6284842aca8df23f0f0048d", "version_major": 2, "version_minor": 0 }, @@ -131,10 +132,10 @@ { "data": { "text/plain": [ - "" + "" ] }, - "execution_count": 8, + "execution_count": 2, "metadata": {}, "output_type": "execute_result" } @@ -145,6 +146,32 @@ "plot.add_image(img)" ] }, + { + "cell_type": "code", + "execution_count": 3, + "id": "68ea8011-d6fd-448f-9bf6-34073164d271", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "585aa41941eb4c4e9f230e0a9ecebacf", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "VBox(children=(JupyterWgpuCanvas(), HBox(children=(Button(icon='expand-arrows-alt', layout=Layout(width='auto'…" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "plot.show()" + ] + }, { "cell_type": "code", "execution_count": 9, @@ -226,7 +253,7 @@ { "cell_type": "code", "execution_count": null, - "id": "49d42ecf-41d6-40da-a41e-842570e21876", + "id": "0bbb459c-cb49-448e-b0b8-c541e55da313", "metadata": {}, "outputs": [], "source": [] diff --git a/fastplotlib/plot.py b/fastplotlib/plot.py index 97e19effd..c61bb9eeb 100644 --- a/fastplotlib/plot.py +++ b/fastplotlib/plot.py @@ -1,4 +1,5 @@ from typing import * +from ipywidgets import HBox, Checkbox, Layout, Button, ToggleButton, VBox import pygfx from wgpu.gui.auto import WgpuCanvas from .layouts._subplot import Subplot @@ -11,6 +12,7 @@ def __init__( renderer: pygfx.Renderer = None, camera: str = '2d', controller: Union[pygfx.PanZoomController, pygfx.OrbitController] = None, + toolbar: bool = True, **kwargs ): """ @@ -83,6 +85,8 @@ def __init__( **kwargs ) + self.toolbar = toolbar + def render(self): super(Plot, self).render() @@ -103,4 +107,55 @@ def show(self, autoscale: bool = True): if autoscale: self.auto_scale(maintain_aspect=True, zoom=0.95) - return self.canvas + if self.toolbar: + tools = ToolBar(self).toolbar + return VBox([self.canvas, tools]) + else: + return self.canvas + + +class ToolBar: + def __init__(self, + plot: Plot): + """ + Basic toolbar for a Plot instance. + + Parameters + ---------- + plot: + """ + self.plot = plot + + self._tools = list() + + auto_tool = Button(value=False, disabled=False, icon='expand-arrows-alt', layout=Layout(width='auto')) + center_tool = Button(value=False, disabled=False, icon='compress-arrows-alt', layout=Layout(width='auto')) + panzoom_tool = ToggleButton(value=False, disabled=False, icon='hand-pointer', layout=Layout(width='auto')) + maintain_aspect_tool = ToggleButton(value=False, disabled=False, description="1:1", layout=Layout(width='auto')) + maintain_aspect_tool.style.font_weight = "bold" + self._tools.extend([auto_tool, center_tool, panzoom_tool, maintain_aspect_tool]) + + def auto_scale(obj): + if maintain_aspect_tool.value: + self.plot.auto_scale(maintain_aspect=True) + else: + self.plot.auto_scale() + + def center_scene(obj): + self.plot.center_scene() + + def panzoom_control(obj): + if panzoom_tool.value: + # toggle pan zoom controller + self.plot.controller.enabled = False + else: + self.plot.controller.enabled = True + + panzoom_tool.observe(panzoom_control, 'value') + auto_tool.on_click(auto_scale) + center_tool.on_click(center_scene) + + @property + def toolbar(self): + return HBox(self._tools) + \ No newline at end of file From 3e916c97eae93c2e5d9237a74cbc199cd29a603e Mon Sep 17 00:00:00 2001 From: Caitlin Lewis Date: Fri, 19 May 2023 14:31:54 -0400 Subject: [PATCH 04/12] base logic in notebook for gridplot toolbar --- examples/buttons.ipynb | 282 ++++++++++++++++++++++++----------------- fastplotlib/plot.py | 1 - 2 files changed, 166 insertions(+), 117 deletions(-) diff --git a/examples/buttons.ipynb b/examples/buttons.ipynb index 235f520e2..cdcc1b82f 100644 --- a/examples/buttons.ipynb +++ b/examples/buttons.ipynb @@ -15,112 +15,118 @@ { "cell_type": "code", "execution_count": 2, - "id": "6e893bc8-26e3-4ec4-968f-35f6f876ddfe", + "id": "33bf59c4-14e5-43a8-8a16-69b6859864c5", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "257f4ccf14c54f1ba5a0642b2d7e82cc", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "RFBOutputContext()" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/clewis7/repos/fastplotlib/fastplotlib/graphics/features/_base.py:33: UserWarning: converting float64 array to float32\n", + " warn(f\"converting {array.dtype} array to float32\")\n" + ] + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "# need to create some icons\n", - "# put icons in an HBox\n", - "tools = list()" + "plot = Plot()\n", + "img = np.random.rand(512, 512)\n", + "plot.add_image(img)" ] }, { "cell_type": "code", "execution_count": 3, - "id": "8c519521-3409-45a8-89b8-b02baca20b24", - "metadata": {}, - "outputs": [], - "source": [ - "# auto scale tool button\n", - "auto = Button(value=False, disabled=False, icon='expand-arrows-alt', layout=Layout(width='auto'))\n", - "tools.append(auto)" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "e47e763a-7d90-4455-9d28-e93a8d3726f1", + "id": "68ea8011-d6fd-448f-9bf6-34073164d271", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "832b884047e74f45b9e9307b0ced48cb", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "VBox(children=(JupyterWgpuCanvas(), HBox(children=(Button(icon='expand-arrows-alt', layout=Layout(width='auto'…" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "# center scene tool button\n", - "center = Button(value=False, disabled=False, icon='compress-arrows-alt', layout=Layout(width='auto'))\n", - "tools.append(center)" + "plot.show()" ] }, { "cell_type": "code", "execution_count": 5, - "id": "253cd1ce-aab3-41a1-a86a-dd155e0cedfe", - "metadata": {}, - "outputs": [], - "source": [ - "# panzoom tool button\n", - "panzoom = ToggleButton(value=False, disabled=False, icon='hand-pointer', layout=Layout(width='auto'))\n", - "tools.append(panzoom)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "9e05ed4a-3ce9-4c0f-a684-034a12a853d1", + "id": "0bbb459c-cb49-448e-b0b8-c541e55da313", "metadata": {}, "outputs": [], "source": [ - "# maintain aspect tool button\n", - "m_aspect = ToggleButton(value=False, disabled=False, description=\"1:1\", layout=Layout(width='auto'))\n", - "m_aspect.style.font_weight = \"bold\"\n", - "tools.append(m_aspect)" + "from fastplotlib import GridPlot\n", + "from ipywidgets import Dropdown" ] }, { "cell_type": "code", "execution_count": 7, - "id": "4888dc48-f422-4c1e-97eb-cad4cc390b58", - "metadata": { - "tags": [] - }, + "id": "91a31531-818b-46a2-9587-5d9ef5b59b93", + "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "5a28ab7e957a43409f0ca0863780a394", + "model_id": "7fedb853c0c74d10bfbb900a988f6a98", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "HBox(children=(Button(icon='expand-arrows-alt', layout=Layout(width='auto'), style=ButtonStyle()), Button(icon…" + "RFBOutputContext()" ] }, - "execution_count": 7, "metadata": {}, - "output_type": "execute_result" + "output_type": "display_data" } ], "source": [ - "HBox(tools)" + "gp = GridPlot(\n", + " shape=(1,2),\n", + " names=[[\"plot1\", \"plot2\"]])" ] }, { "cell_type": "code", - "execution_count": 2, - "id": "33bf59c4-14e5-43a8-8a16-69b6859864c5", + "execution_count": 9, + "id": "e96bbda7-3693-42f2-bd52-f668f39134f6", "metadata": {}, "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "0472ef2ee6284842aca8df23f0f0048d", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "RFBOutputContext()" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, { "name": "stderr", "output_type": "stream", @@ -128,132 +134,176 @@ "/home/clewis7/repos/fastplotlib/fastplotlib/graphics/features/_base.py:33: UserWarning: converting float64 array to float32\n", " warn(f\"converting {array.dtype} array to float32\")\n" ] - }, - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" } ], "source": [ - "plot = Plot()\n", - "img = np.random.rand(512, 512)\n", - "plot.add_image(img)" + "for subplot in gp:\n", + " subplot.add_image(data=img)" ] }, { "cell_type": "code", - "execution_count": 3, - "id": "68ea8011-d6fd-448f-9bf6-34073164d271", + "execution_count": 15, + "id": "1ff67ee5-c942-4171-9058-9dd910c99d4b", "metadata": {}, "outputs": [ { "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "585aa41941eb4c4e9f230e0a9ecebacf", - "version_major": 2, - "version_minor": 0 - }, "text/plain": [ - "VBox(children=(JupyterWgpuCanvas(), HBox(children=(Button(icon='expand-arrows-alt', layout=Layout(width='auto'…" + "['plot1', 'plot2']" ] }, - "execution_count": 3, + "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "plot.show()" + "list(gp.names[0])" ] }, { "cell_type": "code", - "execution_count": 9, - "id": "c02159c1-ddcf-4e2d-a0aa-67c73112777e", + "execution_count": 20, + "id": "95e7f25a-d936-42da-ae51-33550cf1f944", + "metadata": {}, + "outputs": [], + "source": [ + "names = list(gp.names[0])\n", + "\n", + "dropdown = Dropdown(options=[name for name in names], disabled=False, description='Plots:')" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "f0ec8d8c-fbc8-4e83-a764-a191b2977675", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "667f2e2de82b4f6abe7d9f2dbccb9e95", + "model_id": "c7eea34a49bf44e8a353c2ffbe436986", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "VBox(children=(JupyterWgpuCanvas(), HBox(children=(Button(icon='expand-arrows-alt', layout=Layout(width='auto'…" + "Dropdown(description='Plots:', options=('plot1', 'plot2'), value='plot1')" ] }, - "execution_count": 9, + "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "VBox([plot.show(), HBox(tools)])" + "dropdown" ] }, { "cell_type": "code", - "execution_count": 10, - "id": "46b9b7af-a029-4c9b-a0d7-55614b618ea5", + "execution_count": 27, + "id": "84e90ac1-8e35-4cb8-b9f2-a5ea6e0e742f", "metadata": {}, "outputs": [], "source": [ - "def auto_scale(b):\n", - " if m_aspect.value: \n", - " plot.auto_scale(maintain_aspect=True)\n", - " else:\n", - " plot.auto_scale()\n", - "def center_scene(b):\n", - " plot.center_scene()" + "tools = list()" ] }, { "cell_type": "code", - "execution_count": 11, - "id": "f02c536f-32fa-484f-9a85-7549f83a63d9", + "execution_count": 28, + "id": "52fbc321-0daf-49d6-bb61-cb6a16e585c3", "metadata": {}, "outputs": [], "source": [ - "auto.on_click(auto_scale)\n", - "center.on_click(center_scene)" + "auto_tool = Button(value=False, disabled=False, icon='expand-arrows-alt', layout=Layout(width='auto'))\n", + "center_tool = Button(value=False, disabled=False, icon='compress-arrows-alt', layout=Layout(width='auto'))\n", + "panzoom_tool = ToggleButton(value=False, disabled=False, icon='hand-pointer', layout=Layout(width='auto'))\n", + "maintain_aspect_tool = ToggleButton(value=False, disabled=False, description=\"1:1\", layout=Layout(width='auto'))\n", + "maintain_aspect_tool.style.font_weight = \"bold\"" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "cf9c4930-8043-4d66-9e99-9e4b5b7c90e4", + "metadata": {}, + "outputs": [], + "source": [ + "tools.extend([auto_tool, center_tool, panzoom_tool, maintain_aspect_tool])\n", + "toolbar = HBox(tools)" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "f3699979-ff0a-4490-9d15-b368f22fe5b8", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "0e7128ecae234179812d5e784930a131", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "VBox(children=(JupyterWgpuCanvas(frame_feedback={'index': 5018, 'timestamp': 1684520752.8116126, 'localtime': …" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "VBox([gp.show(), HBox([toolbar, dropdown])])" ] }, { "cell_type": "code", - "execution_count": 12, - "id": "2df49dec-e547-4912-886f-8828a70a96aa", + "execution_count": 32, + "id": "cb64a77a-651e-4efd-8127-3a0e2dc87239", "metadata": {}, "outputs": [], "source": [ + "def auto_scale(obj):\n", + " current = dropdown.value\n", + " if maintain_aspect_tool.value:\n", + " gp[current].auto_scale(maintain_aspect=True)\n", + " else:\n", + " gp[current].auto_scale()\n", + "\n", + "def center_scene(obj):\n", + " current = dropdown.value\n", + " gp[current].center_scene()\n", + "\n", "def panzoom_control(obj):\n", - " if panzoom.value:\n", + " current = dropdown.value\n", + " if panzoom_tool.value:\n", " # toggle pan zoom controller\n", - " plot.controller.enabled = False\n", - " else: \n", - " plot.controller.enabled = True" + " gp[current].controller.enabled = False\n", + " else:\n", + " gp[current].controller.enabled = True" ] }, { "cell_type": "code", - "execution_count": 13, - "id": "a4798429-0db2-48ea-ab2e-24e54fc35541", + "execution_count": 33, + "id": "52b779a2-17ad-4888-863f-da9883a2adb8", "metadata": {}, "outputs": [], "source": [ - "panzoom.observe(panzoom_control, 'value')" + "panzoom_tool.observe(panzoom_control, 'value')\n", + "auto_tool.on_click(auto_scale)\n", + "center_tool.on_click(center_scene)" ] }, { "cell_type": "code", "execution_count": null, - "id": "0bbb459c-cb49-448e-b0b8-c541e55da313", + "id": "36f5e040-cc58-4b0a-beb1-1f66ea02ccb9", "metadata": {}, "outputs": [], "source": [] diff --git a/fastplotlib/plot.py b/fastplotlib/plot.py index c61bb9eeb..aa4417d35 100644 --- a/fastplotlib/plot.py +++ b/fastplotlib/plot.py @@ -158,4 +158,3 @@ def panzoom_control(obj): @property def toolbar(self): return HBox(self._tools) - \ No newline at end of file From 2fa309b0a33ddff865046a69e17b7b49d44d8888 Mon Sep 17 00:00:00 2001 From: Caitlin Lewis Date: Fri, 19 May 2023 15:55:26 -0400 Subject: [PATCH 05/12] gp toolbar working when subplots have names & mix names/no names --- fastplotlib/layouts/_gridplot.py | 67 +++++++++++++++++++++++++++++++- fastplotlib/plot.py | 2 +- 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/fastplotlib/layouts/_gridplot.py b/fastplotlib/layouts/_gridplot.py index 3f694b007..7042f94ca 100644 --- a/fastplotlib/layouts/_gridplot.py +++ b/fastplotlib/layouts/_gridplot.py @@ -7,6 +7,7 @@ from wgpu.gui.auto import WgpuCanvas from ._defaults import create_controller from ._subplot import Subplot +from ipywidgets import HBox, Layout, Button, ToggleButton, VBox, Dropdown def to_array(a) -> np.ndarray: @@ -30,6 +31,7 @@ def __init__( controllers: Union[np.ndarray, str] = None, canvas: WgpuCanvas = None, renderer: pygfx.Renderer = None, + toolbar: bool = True, **kwargs ): """ @@ -64,6 +66,7 @@ def __init__( """ self.shape = shape + self.toolbar = toolbar if isinstance(cameras, str): if cameras not in valid_cameras: @@ -266,7 +269,11 @@ def show(self): for subplot in self: subplot.auto_scale(maintain_aspect=True, zoom=0.95) - return self.canvas + if self.toolbar: + tools = GridPlotToolBar(self).toolbar + return VBox([self.canvas, tools]) + else: + return self.canvas def _get_iterator(self): return product(range(self.shape[0]), range(self.shape[1])) @@ -281,3 +288,61 @@ def __next__(self) -> Subplot: def __repr__(self): return f"fastplotlib.{self.__class__.__name__} @ {hex(id(self))}\n" + + +class GridPlotToolBar: + def __init__(self, + plot: GridPlot): + """ + Basic toolbar for a GridPlot instance. + + Parameters + ---------- + plot: + """ + self.plot = plot + + self._tools = list() + + auto_tool = Button(value=False, disabled=False, icon='expand-arrows-alt', layout=Layout(width='auto')) + center_tool = Button(value=False, disabled=False, icon='compress-arrows-alt', layout=Layout(width='auto')) + panzoom_tool = ToggleButton(value=False, disabled=False, icon='hand-pointer', layout=Layout(width='auto')) + maintain_aspect_tool = ToggleButton(value=False, disabled=False, description="1:1", layout=Layout(width='auto')) + maintain_aspect_tool.style.font_weight = "bold" + self._tools.extend([auto_tool, center_tool, panzoom_tool, maintain_aspect_tool]) + + positions = [[i, j] for i, j in self.plot._get_iterator()] + values = list() + for pos in positions: + if self.plot[pos].name is not None: + values.append(self.plot[pos].name) + else: + values.append(tuple(pos)) + self._dropdown = Dropdown(options=values, disabled=False, description='Plots:') + + def auto_scale(obj): + current = self._dropdown.value + if maintain_aspect_tool.value: + self.plot[current].auto_scale(maintain_aspect=True) + else: + self.plot[current].auto_scale() + + def center_scene(obj): + current = self._dropdown.value + self.plot[current].center_scene() + + def panzoom_control(obj): + current = self._dropdown.value + if panzoom_tool.value: + # toggle pan zoom controller + self.plot[current].controller.enabled = False + else: + self.plot[current].controller.enabled = True + + panzoom_tool.observe(panzoom_control, 'value') + auto_tool.on_click(auto_scale) + center_tool.on_click(center_scene) + + @property + def toolbar(self): + return HBox([HBox(self._tools), self._dropdown]) diff --git a/fastplotlib/plot.py b/fastplotlib/plot.py index aa4417d35..4547dae17 100644 --- a/fastplotlib/plot.py +++ b/fastplotlib/plot.py @@ -1,5 +1,5 @@ from typing import * -from ipywidgets import HBox, Checkbox, Layout, Button, ToggleButton, VBox +from ipywidgets import HBox, Layout, Button, ToggleButton, VBox import pygfx from wgpu.gui.auto import WgpuCanvas from .layouts._subplot import Subplot From b985f31a0ac3f9faec16be37761357c4b9d8204c Mon Sep 17 00:00:00 2001 From: Caitlin Lewis Date: Fri, 19 May 2023 16:59:07 -0400 Subject: [PATCH 06/12] initial changes to simple plot toolbar --- fastplotlib/plot.py | 48 ++++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/fastplotlib/plot.py b/fastplotlib/plot.py index 4547dae17..e46159a79 100644 --- a/fastplotlib/plot.py +++ b/fastplotlib/plot.py @@ -12,7 +12,6 @@ def __init__( renderer: pygfx.Renderer = None, camera: str = '2d', controller: Union[pygfx.PanZoomController, pygfx.OrbitController] = None, - toolbar: bool = True, **kwargs ): """ @@ -85,15 +84,13 @@ def __init__( **kwargs ) - self.toolbar = toolbar - def render(self): super(Plot, self).render() self.renderer.flush() self.canvas.request_draw() - def show(self, autoscale: bool = True): + def show(self, autoscale: bool = True, toolbar: bool = True): """ begins the rendering event loop and returns the canvas @@ -107,7 +104,7 @@ def show(self, autoscale: bool = True): if autoscale: self.auto_scale(maintain_aspect=True, zoom=0.95) - if self.toolbar: + if toolbar: tools = ToolBar(self).toolbar return VBox([self.canvas, tools]) else: @@ -122,21 +119,28 @@ def __init__(self, Parameters ---------- - plot: + plot: encapsulated plot instance that will be manipulated using the toolbar buttons """ self.plot = plot - self._tools = list() - - auto_tool = Button(value=False, disabled=False, icon='expand-arrows-alt', layout=Layout(width='auto')) - center_tool = Button(value=False, disabled=False, icon='compress-arrows-alt', layout=Layout(width='auto')) - panzoom_tool = ToggleButton(value=False, disabled=False, icon='hand-pointer', layout=Layout(width='auto')) - maintain_aspect_tool = ToggleButton(value=False, disabled=False, description="1:1", layout=Layout(width='auto')) - maintain_aspect_tool.style.font_weight = "bold" - self._tools.extend([auto_tool, center_tool, panzoom_tool, maintain_aspect_tool]) + self.autoscale_button = Button(value=False, disabled=False, icon='expand-arrows-alt', + layout=Layout(width='auto'), tooltip='Auto-scale the camera w.r.t to the scene') + self.center_scene_button = Button(value=False, disabled=False, icon='compress-arrows-alt', + layout=Layout(width='auto'), tooltip='Auto-center the scene, does not scale') + self.panzoom_controller_button = ToggleButton(value=True, disabled=False, icon='hand-pointer', + layout=Layout(width='auto'), tooltip='Toggle panzoom controller') + self.maintain_aspect_button = ToggleButton(value=True, disabled=False, description="1:1", + layout=Layout(width='auto'), + tooltip='Maintain camera aspect ratio for all dims') + self.maintain_aspect_button.style.font_weight = "bold" + + self._widget = HBox([self.autoscale_button, + self.center_scene_button, + self.panzoom_controller_button, + self.maintain_aspect_button]) def auto_scale(obj): - if maintain_aspect_tool.value: + if self.maintain_aspect_button.value: self.plot.auto_scale(maintain_aspect=True) else: self.plot.auto_scale() @@ -145,16 +149,12 @@ def center_scene(obj): self.plot.center_scene() def panzoom_control(obj): - if panzoom_tool.value: - # toggle pan zoom controller - self.plot.controller.enabled = False - else: - self.plot.controller.enabled = True + self.plot.controller.enabled = self.panzoom_controller_button.value - panzoom_tool.observe(panzoom_control, 'value') - auto_tool.on_click(auto_scale) - center_tool.on_click(center_scene) + self.panzoom_controller_button.observe(panzoom_control, 'value') + self.autoscale_button.on_click(auto_scale) + self.center_scene_button.on_click(center_scene) @property def toolbar(self): - return HBox(self._tools) + return self._widget From 1702938a50c01db3b33212364a5d0503bbf6534f Mon Sep 17 00:00:00 2001 From: Caitlin Lewis Date: Mon, 22 May 2023 09:54:28 -0400 Subject: [PATCH 07/12] updates to plot toolbar, still need to refactor into separate file --- fastplotlib/layouts/_toolbar.py | 1 + fastplotlib/plot.py | 62 ++++++++++++++++++--------------- 2 files changed, 35 insertions(+), 28 deletions(-) create mode 100644 fastplotlib/layouts/_toolbar.py diff --git a/fastplotlib/layouts/_toolbar.py b/fastplotlib/layouts/_toolbar.py new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/fastplotlib/layouts/_toolbar.py @@ -0,0 +1 @@ + diff --git a/fastplotlib/plot.py b/fastplotlib/plot.py index e46159a79..91001fa43 100644 --- a/fastplotlib/plot.py +++ b/fastplotlib/plot.py @@ -1,8 +1,9 @@ from typing import * -from ipywidgets import HBox, Layout, Button, ToggleButton, VBox import pygfx from wgpu.gui.auto import WgpuCanvas from .layouts._subplot import Subplot +from ipywidgets import HBox, Layout, Button, ToggleButton, VBox +from wgpu.gui.jupyter import JupyterWgpuCanvas class Plot(Subplot): @@ -84,6 +85,8 @@ def __init__( **kwargs ) + self.toolbar = None + def render(self): super(Plot, self).render() @@ -104,9 +107,15 @@ def show(self, autoscale: bool = True, toolbar: bool = True): if autoscale: self.auto_scale(maintain_aspect=True, zoom=0.95) - if toolbar: - tools = ToolBar(self).toolbar - return VBox([self.canvas, tools]) + # check if in jupyter notebook or not + if not isinstance(self.canvas, JupyterWgpuCanvas): + return self.canvas + + if toolbar and self.toolbar is None: + self.toolbar = ToolBar(self).widget + return VBox([self.canvas, self.toolbar]) + elif toolbar and self.toolbar is not None: + return VBox([self.canvas, self.toolbar]) else: return self.canvas @@ -124,37 +133,34 @@ def __init__(self, self.plot = plot self.autoscale_button = Button(value=False, disabled=False, icon='expand-arrows-alt', - layout=Layout(width='auto'), tooltip='Auto-scale the camera w.r.t to the scene') - self.center_scene_button = Button(value=False, disabled=False, icon='compress-arrows-alt', - layout=Layout(width='auto'), tooltip='Auto-center the scene, does not scale') + layout=Layout(width='auto'), tooltip='auto-scale scene') + self.center_scene_button = Button(value=False, disabled=False, icon='align-center', + layout=Layout(width='auto'), tooltip='auto-center scene') self.panzoom_controller_button = ToggleButton(value=True, disabled=False, icon='hand-pointer', - layout=Layout(width='auto'), tooltip='Toggle panzoom controller') + layout=Layout(width='auto'), tooltip='panzoom controller') self.maintain_aspect_button = ToggleButton(value=True, disabled=False, description="1:1", layout=Layout(width='auto'), - tooltip='Maintain camera aspect ratio for all dims') + tooltip='maintain aspect') self.maintain_aspect_button.style.font_weight = "bold" - self._widget = HBox([self.autoscale_button, - self.center_scene_button, - self.panzoom_controller_button, - self.maintain_aspect_button]) + self.widget = HBox([self.autoscale_button, + self.center_scene_button, + self.panzoom_controller_button, + self.maintain_aspect_button]) - def auto_scale(obj): - if self.maintain_aspect_button.value: - self.plot.auto_scale(maintain_aspect=True) - else: - self.plot.auto_scale() + self.panzoom_controller_button.observe(self.panzoom_control, 'value') + self.autoscale_button.on_click(self.auto_scale) + self.center_scene_button.on_click(self.center_scene) + self.maintain_aspect_button.observe(self.maintain_aspect, 'value') - def center_scene(obj): - self.plot.center_scene() + def auto_scale(self, obj): + self.plot.auto_scale(maintain_aspect=self.plot.camera.maintain_aspect) - def panzoom_control(obj): - self.plot.controller.enabled = self.panzoom_controller_button.value + def center_scene(self, obj): + self.plot.center_scene() - self.panzoom_controller_button.observe(panzoom_control, 'value') - self.autoscale_button.on_click(auto_scale) - self.center_scene_button.on_click(center_scene) + def panzoom_control(self, obj): + self.plot.controller.enabled = self.panzoom_controller_button.value - @property - def toolbar(self): - return self._widget + def maintain_aspect(self, obj): + self.plot.camera.maintain_aspect = self.maintain_aspect_button.value From 20ef9dd20a8696830a35e27b1199386b439e3521 Mon Sep 17 00:00:00 2001 From: Caitlin Lewis Date: Mon, 22 May 2023 10:45:06 -0400 Subject: [PATCH 08/12] fixed gridplot toolbar, still need to move to separate class --- examples/buttons.ipynb | 260 +++++++++++++++++++++---------- fastplotlib/layouts/_gridplot.py | 98 +++++++----- 2 files changed, 239 insertions(+), 119 deletions(-) diff --git a/examples/buttons.ipynb b/examples/buttons.ipynb index cdcc1b82f..3b4bf0f02 100644 --- a/examples/buttons.ipynb +++ b/examples/buttons.ipynb @@ -21,7 +21,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "257f4ccf14c54f1ba5a0642b2d7e82cc", + "model_id": "7c85c9cf530c4c9285c4d4eeabc56ad6", "version_major": 2, "version_minor": 0 }, @@ -43,7 +43,7 @@ { "data": { "text/plain": [ - "" + "" ] }, "execution_count": 2, @@ -66,7 +66,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "832b884047e74f45b9e9307b0ced48cb", + "model_id": "26f3abcd50ff466c88eeae34ba0ebc95", "version_major": 2, "version_minor": 0 }, @@ -85,7 +85,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 4, "id": "0bbb459c-cb49-448e-b0b8-c541e55da313", "metadata": {}, "outputs": [], @@ -96,14 +96,14 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 5, "id": "91a31531-818b-46a2-9587-5d9ef5b59b93", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "7fedb853c0c74d10bfbb900a988f6a98", + "model_id": "5f53d9e84b7d47a2807def025021626b", "version_major": 2, "version_minor": 0 }, @@ -118,192 +118,296 @@ "source": [ "gp = GridPlot(\n", " shape=(1,2),\n", - " names=[[\"plot1\", \"plot2\"]])" + " names=[[None, 'plot2']])" ] }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 6, "id": "e96bbda7-3693-42f2-bd52-f668f39134f6", "metadata": {}, + "outputs": [], + "source": [ + "for subplot in gp:\n", + " subplot.add_image(data=img)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "03b877ba-cf9c-47d9-a0e5-b3e694274a28", + "metadata": {}, "outputs": [ { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/clewis7/repos/fastplotlib/fastplotlib/graphics/features/_base.py:33: UserWarning: converting float64 array to float32\n", - " warn(f\"converting {array.dtype} array to float32\")\n" - ] + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "027c60bf624b427e94048758147b530c", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "VBox(children=(JupyterWgpuCanvas(), HBox(children=(Button(icon='expand-arrows-alt', layout=Layout(width='auto'…" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ - "for subplot in gp:\n", - " subplot.add_image(data=img)" + "gp.show()" ] }, { "cell_type": "code", - "execution_count": 15, - "id": "1ff67ee5-c942-4171-9058-9dd910c99d4b", + "execution_count": 8, + "id": "36f5e040-cc58-4b0a-beb1-1f66ea02ccb9", "metadata": {}, "outputs": [ { "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "f902391ceb614f2a812725371184ce81", + "version_major": 2, + "version_minor": 0 + }, "text/plain": [ - "['plot1', 'plot2']" + "RFBOutputContext()" ] }, - "execution_count": 15, "metadata": {}, - "output_type": "execute_result" + "output_type": "display_data" } ], "source": [ - "list(gp.names[0])" + "gp2 = GridPlot(shape=(1,2))" ] }, { "cell_type": "code", - "execution_count": 20, - "id": "95e7f25a-d936-42da-ae51-33550cf1f944", + "execution_count": 9, + "id": "c6753d45-a0ae-4c96-8ed5-7638c4cf24e3", "metadata": {}, "outputs": [], "source": [ - "names = list(gp.names[0])\n", - "\n", - "dropdown = Dropdown(options=[name for name in names], disabled=False, description='Plots:')" + "for subplot in gp2:\n", + " subplot.add_image(data=img)" ] }, { "cell_type": "code", - "execution_count": 21, - "id": "f0ec8d8c-fbc8-4e83-a764-a191b2977675", + "execution_count": 10, + "id": "5a769c0f-6d95-4969-ad9d-24636fc74b18", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "c7eea34a49bf44e8a353c2ffbe436986", + "model_id": "65a2a30036f44e22a479c6edd215e25a", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "Dropdown(description='Plots:', options=('plot1', 'plot2'), value='plot1')" + "VBox(children=(JupyterWgpuCanvas(), HBox(children=(Button(icon='expand-arrows-alt', layout=Layout(width='auto'…" ] }, - "execution_count": 21, + "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "dropdown" + "gp2.show()" ] }, { "cell_type": "code", - "execution_count": 27, - "id": "84e90ac1-8e35-4cb8-b9f2-a5ea6e0e742f", + "execution_count": 12, + "id": "d6d62cf4-405b-4598-8696-592613593a4f", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "[(0, 0), (0, 1)]" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "tools = list()" + "positions = [(i,j) for i, j in gp2._get_iterator()]\n", + "positions" ] }, { "cell_type": "code", - "execution_count": 28, - "id": "52fbc321-0daf-49d6-bb61-cb6a16e585c3", + "execution_count": 14, + "id": "b5fcb6b2-f8e8-4260-9d00-be2d3cdc1bc6", "metadata": {}, "outputs": [], "source": [ - "auto_tool = Button(value=False, disabled=False, icon='expand-arrows-alt', layout=Layout(width='auto'))\n", - "center_tool = Button(value=False, disabled=False, icon='compress-arrows-alt', layout=Layout(width='auto'))\n", - "panzoom_tool = ToggleButton(value=False, disabled=False, icon='hand-pointer', layout=Layout(width='auto'))\n", - "maintain_aspect_tool = ToggleButton(value=False, disabled=False, description=\"1:1\", layout=Layout(width='auto'))\n", - "maintain_aspect_tool.style.font_weight = \"bold\"" + "values = list()\n", + "for pos in positions:\n", + " if gp2[pos].name is not None:\n", + " values.append(gp2[pos].name)\n", + " else:\n", + " values.append(pos)" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "c95aebb8-e600-40e5-a91c-bb71f46c48ac", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[(0, 0), (0, 1)]" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "values" ] }, { "cell_type": "code", - "execution_count": 29, - "id": "cf9c4930-8043-4d66-9e99-9e4b5b7c90e4", + "execution_count": 26, + "id": "a591cc18-ba41-4b86-96c3-a2cf9a252d6d", "metadata": {}, "outputs": [], "source": [ - "tools.extend([auto_tool, center_tool, panzoom_tool, maintain_aspect_tool])\n", - "toolbar = HBox(tools)" + "drop = Dropdown(options=[str(pos) for pos in values], disabled=False, description='Plots:')" ] }, { "cell_type": "code", - "execution_count": 30, - "id": "f3699979-ff0a-4490-9d15-b368f22fe5b8", + "execution_count": 27, + "id": "3595493c-3f7d-4887-879a-28cf497e1336", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "0e7128ecae234179812d5e784930a131", + "model_id": "104cc0a4f38c4a81a74bef42cfcc7c75", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "VBox(children=(JupyterWgpuCanvas(frame_feedback={'index': 5018, 'timestamp': 1684520752.8116126, 'localtime': …" + "Dropdown(description='Plots:', options=('(0, 0)', '(0, 1)'), value='(0, 0)')" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "drop" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "6cf7069f-2992-41f5-92ff-020dcd925a1f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(0, 0)" ] }, - "execution_count": 30, + "execution_count": 31, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "VBox([gp.show(), HBox([toolbar, dropdown])])" + "eval(drop.value)" ] }, { "cell_type": "code", "execution_count": 32, - "id": "cb64a77a-651e-4efd-8127-3a0e2dc87239", + "id": "ebab126c-c77c-462c-a9fb-0f64dad81abf", "metadata": {}, "outputs": [], "source": [ - "def auto_scale(obj):\n", - " current = dropdown.value\n", - " if maintain_aspect_tool.value:\n", - " gp[current].auto_scale(maintain_aspect=True)\n", - " else:\n", - " gp[current].auto_scale()\n", - "\n", - "def center_scene(obj):\n", - " current = dropdown.value\n", - " gp[current].center_scene()\n", - "\n", - "def panzoom_control(obj):\n", - " current = dropdown.value\n", - " if panzoom_tool.value:\n", - " # toggle pan zoom controller\n", - " gp[current].controller.enabled = False\n", - " else:\n", - " gp[current].controller.enabled = True" + "values = ['plot1', (0, 1)]" ] }, { "cell_type": "code", "execution_count": 33, - "id": "52b779a2-17ad-4888-863f-da9883a2adb8", + "id": "b01c2079-e152-455a-82f6-97ad30cf8f9e", "metadata": {}, "outputs": [], "source": [ - "panzoom_tool.observe(panzoom_control, 'value')\n", - "auto_tool.on_click(auto_scale)\n", - "center_tool.on_click(center_scene)" + "drop = Dropdown(options=[str(pos) for pos in values], disabled=False, description='Plots:')" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "54465b3f-f7aa-4782-b164-88a3d88e824e", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "e758704e84144e919c673b6f9154f3fc", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Dropdown(description='Plots:', options=('plot1', '(0, 1)'), value='plot1')" + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "drop" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "id": "4d086ef3-1e32-4447-a46f-7793b26d0ce7", + "metadata": {}, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'plot1' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", + "Input \u001b[0;32mIn [35]\u001b[0m, in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[38;5;28;43meval\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mdrop\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mvalue\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[0;32m:1\u001b[0m, in \u001b[0;36m\u001b[0;34m\u001b[0m\n", + "\u001b[0;31mNameError\u001b[0m: name 'plot1' is not defined" + ] + } + ], + "source": [ + "eval(drop.value)" ] }, { "cell_type": "code", "execution_count": null, - "id": "36f5e040-cc58-4b0a-beb1-1f66ea02ccb9", + "id": "cd3e7b3c-f4d2-44c7-931c-d172c7bdad36", "metadata": {}, "outputs": [], "source": [] diff --git a/fastplotlib/layouts/_gridplot.py b/fastplotlib/layouts/_gridplot.py index 7042f94ca..999607eae 100644 --- a/fastplotlib/layouts/_gridplot.py +++ b/fastplotlib/layouts/_gridplot.py @@ -1,3 +1,4 @@ +import itertools from itertools import product import numpy as np from typing import * @@ -8,6 +9,7 @@ from ._defaults import create_controller from ._subplot import Subplot from ipywidgets import HBox, Layout, Button, ToggleButton, VBox, Dropdown +from wgpu.gui.jupyter import JupyterWgpuCanvas def to_array(a) -> np.ndarray: @@ -31,7 +33,6 @@ def __init__( controllers: Union[np.ndarray, str] = None, canvas: WgpuCanvas = None, renderer: pygfx.Renderer = None, - toolbar: bool = True, **kwargs ): """ @@ -66,7 +67,7 @@ def __init__( """ self.shape = shape - self.toolbar = toolbar + self.toolbar = None if isinstance(cameras, str): if cameras not in valid_cameras: @@ -254,7 +255,7 @@ def remove_animation(self, func): if func in self._animate_funcs_post: self._animate_funcs_post.remove(func) - def show(self): + def show(self, toolbar: bool = True): """ begins the rendering event loop and returns the canvas @@ -269,9 +270,15 @@ def show(self): for subplot in self: subplot.auto_scale(maintain_aspect=True, zoom=0.95) - if self.toolbar: - tools = GridPlotToolBar(self).toolbar - return VBox([self.canvas, tools]) + # check if in jupyter notebook or not + if not isinstance(self.canvas, JupyterWgpuCanvas): + return self.canvas + + if toolbar and self.toolbar is None: + self.toolbar = GridPlotToolBar(self).widget + return VBox([self.canvas, self.toolbar]) + elif toolbar and self.toolbar is not None: + return VBox([self.canvas, self.toolbar]) else: return self.canvas @@ -302,47 +309,56 @@ def __init__(self, """ self.plot = plot - self._tools = list() - - auto_tool = Button(value=False, disabled=False, icon='expand-arrows-alt', layout=Layout(width='auto')) - center_tool = Button(value=False, disabled=False, icon='compress-arrows-alt', layout=Layout(width='auto')) - panzoom_tool = ToggleButton(value=False, disabled=False, icon='hand-pointer', layout=Layout(width='auto')) - maintain_aspect_tool = ToggleButton(value=False, disabled=False, description="1:1", layout=Layout(width='auto')) - maintain_aspect_tool.style.font_weight = "bold" - self._tools.extend([auto_tool, center_tool, panzoom_tool, maintain_aspect_tool]) - - positions = [[i, j] for i, j in self.plot._get_iterator()] + self.autoscale_button = Button(value=False, disabled=False, icon='expand-arrows-alt', + layout=Layout(width='auto'), tooltip='auto-scale scene') + self.center_scene_button = Button(value=False, disabled=False, icon='align-center', + layout=Layout(width='auto'), tooltip='auto-center scene') + self.panzoom_controller_button = ToggleButton(value=True, disabled=False, icon='hand-pointer', + layout=Layout(width='auto'), tooltip='panzoom controller') + self.maintain_aspect_button = ToggleButton(value=True, disabled=False, description="1:1", + layout=Layout(width='auto'), tooltip='maintain aspect') + self.maintain_aspect_button.style.font_weight = "bold" + + positions = list(product(range(self.plot.shape[0]), range(self.plot.shape[1]))) values = list() for pos in positions: if self.plot[pos].name is not None: values.append(self.plot[pos].name) else: - values.append(tuple(pos)) - self._dropdown = Dropdown(options=values, disabled=False, description='Plots:') - - def auto_scale(obj): - current = self._dropdown.value - if maintain_aspect_tool.value: - self.plot[current].auto_scale(maintain_aspect=True) - else: - self.plot[current].auto_scale() + values.append(str(pos)) + self.dropdown = Dropdown(options=values, disabled=False, description='Subplots:') + + self.widget = HBox([self.autoscale_button, + self.center_scene_button, + self.panzoom_controller_button, + self.maintain_aspect_button, + self.dropdown]) + + self.panzoom_controller_button.observe(self.panzoom_control, 'value') + self.autoscale_button.on_click(self.auto_scale) + self.center_scene_button.on_click(self.center_scene) + self.maintain_aspect_button.observe(self.maintain_aspect, 'value') + + def parser(self) -> Subplot: + # parses dropdown value as plot name or position + current = self.dropdown.value + if current[0] == "(": + return self.plot[eval(current)] + else: + return self.plot[current] - def center_scene(obj): - current = self._dropdown.value - self.plot[current].center_scene() + def auto_scale(self, obj): + current = self.parser() + current.auto_scale(maintain_aspect=current.camera.maintain_aspect) - def panzoom_control(obj): - current = self._dropdown.value - if panzoom_tool.value: - # toggle pan zoom controller - self.plot[current].controller.enabled = False - else: - self.plot[current].controller.enabled = True + def center_scene(self, obj): + current = self.parser() + current.center_scene() - panzoom_tool.observe(panzoom_control, 'value') - auto_tool.on_click(auto_scale) - center_tool.on_click(center_scene) + def panzoom_control(self, obj): + current = self.parser() + current.controller.enabled = self.panzoom_controller_button.value - @property - def toolbar(self): - return HBox([HBox(self._tools), self._dropdown]) + def maintain_aspect(self, obj): + current = self.parser() + current.camera.maintain_aspect = self.maintain_aspect_button.value From 47612a4af9db4c9e63ad1eff985e67b28fe6db10 Mon Sep 17 00:00:00 2001 From: Caitlin Lewis Date: Mon, 22 May 2023 11:12:51 -0400 Subject: [PATCH 09/12] add flip button --- examples/buttons.ipynb | 212 +++---------------------------- fastplotlib/layouts/_gridplot.py | 8 ++ fastplotlib/plot.py | 9 +- 3 files changed, 32 insertions(+), 197 deletions(-) diff --git a/examples/buttons.ipynb b/examples/buttons.ipynb index 3b4bf0f02..ad276f251 100644 --- a/examples/buttons.ipynb +++ b/examples/buttons.ipynb @@ -21,7 +21,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "7c85c9cf530c4c9285c4d4eeabc56ad6", + "model_id": "5f0d926b705844dcbcfc83fce186e1c6", "version_major": 2, "version_minor": 0 }, @@ -43,7 +43,7 @@ { "data": { "text/plain": [ - "" + "" ] }, "execution_count": 2, @@ -53,8 +53,11 @@ ], "source": [ "plot = Plot()\n", - "img = np.random.rand(512, 512)\n", - "plot.add_image(img)" + "xs = np.linspace(-10, 10, 100)\n", + "# sine wave\n", + "ys = np.sin(xs)\n", + "sine = np.dstack([xs, ys])[0]\n", + "plot.add_line(sine)" ] }, { @@ -66,7 +69,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "26f3abcd50ff466c88eeae34ba0ebc95", + "model_id": "3b72d5b4fe2f49c2b35a5db98e591f08", "version_major": 2, "version_minor": 0 }, @@ -96,14 +99,14 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 6, "id": "91a31531-818b-46a2-9587-5d9ef5b59b93", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "5f53d9e84b7d47a2807def025021626b", + "model_id": "467b6f0979ab4130a6401272d680adc4", "version_major": 2, "version_minor": 0 }, @@ -118,30 +121,31 @@ "source": [ "gp = GridPlot(\n", " shape=(1,2),\n", - " names=[[None, 'plot2']])" + " names=[['plot1', 'plot2']])" ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 8, "id": "e96bbda7-3693-42f2-bd52-f668f39134f6", "metadata": {}, "outputs": [], "source": [ + "img = np.random.rand(512,512)\n", "for subplot in gp:\n", " subplot.add_image(data=img)" ] }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 9, "id": "03b877ba-cf9c-47d9-a0e5-b3e694274a28", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "027c60bf624b427e94048758147b530c", + "model_id": "391875b2bfce4dfc8e35e1c573e4b020", "version_major": 2, "version_minor": 0 }, @@ -149,7 +153,7 @@ "VBox(children=(JupyterWgpuCanvas(), HBox(children=(Button(icon='expand-arrows-alt', layout=Layout(width='auto'…" ] }, - "execution_count": 7, + "execution_count": 9, "metadata": {}, "output_type": "execute_result" } @@ -220,190 +224,6 @@ "gp2.show()" ] }, - { - "cell_type": "code", - "execution_count": 12, - "id": "d6d62cf4-405b-4598-8696-592613593a4f", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[(0, 0), (0, 1)]" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "positions = [(i,j) for i, j in gp2._get_iterator()]\n", - "positions" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "id": "b5fcb6b2-f8e8-4260-9d00-be2d3cdc1bc6", - "metadata": {}, - "outputs": [], - "source": [ - "values = list()\n", - "for pos in positions:\n", - " if gp2[pos].name is not None:\n", - " values.append(gp2[pos].name)\n", - " else:\n", - " values.append(pos)" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "id": "c95aebb8-e600-40e5-a91c-bb71f46c48ac", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[(0, 0), (0, 1)]" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "values" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "id": "a591cc18-ba41-4b86-96c3-a2cf9a252d6d", - "metadata": {}, - "outputs": [], - "source": [ - "drop = Dropdown(options=[str(pos) for pos in values], disabled=False, description='Plots:')" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "id": "3595493c-3f7d-4887-879a-28cf497e1336", - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "104cc0a4f38c4a81a74bef42cfcc7c75", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Dropdown(description='Plots:', options=('(0, 0)', '(0, 1)'), value='(0, 0)')" - ] - }, - "execution_count": 27, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "drop" - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "id": "6cf7069f-2992-41f5-92ff-020dcd925a1f", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(0, 0)" - ] - }, - "execution_count": 31, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "eval(drop.value)" - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "id": "ebab126c-c77c-462c-a9fb-0f64dad81abf", - "metadata": {}, - "outputs": [], - "source": [ - "values = ['plot1', (0, 1)]" - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "id": "b01c2079-e152-455a-82f6-97ad30cf8f9e", - "metadata": {}, - "outputs": [], - "source": [ - "drop = Dropdown(options=[str(pos) for pos in values], disabled=False, description='Plots:')" - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "id": "54465b3f-f7aa-4782-b164-88a3d88e824e", - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "e758704e84144e919c673b6f9154f3fc", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Dropdown(description='Plots:', options=('plot1', '(0, 1)'), value='plot1')" - ] - }, - "execution_count": 34, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "drop" - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "id": "4d086ef3-1e32-4447-a46f-7793b26d0ce7", - "metadata": {}, - "outputs": [ - { - "ename": "NameError", - "evalue": "name 'plot1' is not defined", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", - "Input \u001b[0;32mIn [35]\u001b[0m, in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[38;5;28;43meval\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mdrop\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mvalue\u001b[49m\u001b[43m)\u001b[49m\n", - "File \u001b[0;32m:1\u001b[0m, in \u001b[0;36m\u001b[0;34m\u001b[0m\n", - "\u001b[0;31mNameError\u001b[0m: name 'plot1' is not defined" - ] - } - ], - "source": [ - "eval(drop.value)" - ] - }, { "cell_type": "code", "execution_count": null, diff --git a/fastplotlib/layouts/_gridplot.py b/fastplotlib/layouts/_gridplot.py index 999607eae..051c124ce 100644 --- a/fastplotlib/layouts/_gridplot.py +++ b/fastplotlib/layouts/_gridplot.py @@ -318,6 +318,8 @@ def __init__(self, self.maintain_aspect_button = ToggleButton(value=True, disabled=False, description="1:1", layout=Layout(width='auto'), tooltip='maintain aspect') self.maintain_aspect_button.style.font_weight = "bold" + self.flip_camera_button = Button(value=False, disabled=False, icon='sync-alt', + layout=Layout(width='auto'), tooltip='rotate') positions = list(product(range(self.plot.shape[0]), range(self.plot.shape[1]))) values = list() @@ -332,12 +334,14 @@ def __init__(self, self.center_scene_button, self.panzoom_controller_button, self.maintain_aspect_button, + self.flip_camera_button, self.dropdown]) self.panzoom_controller_button.observe(self.panzoom_control, 'value') self.autoscale_button.on_click(self.auto_scale) self.center_scene_button.on_click(self.center_scene) self.maintain_aspect_button.observe(self.maintain_aspect, 'value') + self.flip_camera_button.on_click(self.flip_camera) def parser(self) -> Subplot: # parses dropdown value as plot name or position @@ -362,3 +366,7 @@ def panzoom_control(self, obj): def maintain_aspect(self, obj): current = self.parser() current.camera.maintain_aspect = self.maintain_aspect_button.value + + def flip_camera(self, obj): + current = self.parser() + current.camera.scale.y = -1 * current.camera.scale.y diff --git a/fastplotlib/plot.py b/fastplotlib/plot.py index 91001fa43..06f10b2cd 100644 --- a/fastplotlib/plot.py +++ b/fastplotlib/plot.py @@ -142,16 +142,20 @@ def __init__(self, layout=Layout(width='auto'), tooltip='maintain aspect') self.maintain_aspect_button.style.font_weight = "bold" + self.flip_camera_button = Button(value=False, disabled=False, icon='sync-alt', + layout=Layout(width='auto'), tooltip='rotate') self.widget = HBox([self.autoscale_button, self.center_scene_button, self.panzoom_controller_button, - self.maintain_aspect_button]) + self.maintain_aspect_button, + self.flip_camera_button]) self.panzoom_controller_button.observe(self.panzoom_control, 'value') self.autoscale_button.on_click(self.auto_scale) self.center_scene_button.on_click(self.center_scene) self.maintain_aspect_button.observe(self.maintain_aspect, 'value') + self.flip_camera_button.on_click(self.flip_camera) def auto_scale(self, obj): self.plot.auto_scale(maintain_aspect=self.plot.camera.maintain_aspect) @@ -164,3 +168,6 @@ def panzoom_control(self, obj): def maintain_aspect(self, obj): self.plot.camera.maintain_aspect = self.maintain_aspect_button.value + + def flip_camera(self, obj): + self.plot.camera.scale.y = -1 * self.plot.camera.scale.y From 14fda3c182f0e6d496b2b8cb3a95c598cc03549f Mon Sep 17 00:00:00 2001 From: Caitlin Lewis Date: Tue, 23 May 2023 08:44:04 -0400 Subject: [PATCH 10/12] adding click event handler for gp toolbar dropdown options --- examples/buttons.ipynb | 39 ++++++++++++++++++++++++-------- fastplotlib/layouts/_gridplot.py | 25 +++++++++++++++----- fastplotlib/plot.py | 2 +- 3 files changed, 50 insertions(+), 16 deletions(-) diff --git a/examples/buttons.ipynb b/examples/buttons.ipynb index ad276f251..850d503d6 100644 --- a/examples/buttons.ipynb +++ b/examples/buttons.ipynb @@ -21,7 +21,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "5f0d926b705844dcbcfc83fce186e1c6", + "model_id": "36a6b32ac78745138c0c788cd277c569", "version_major": 2, "version_minor": 0 }, @@ -43,7 +43,7 @@ { "data": { "text/plain": [ - "" + "" ] }, "execution_count": 2, @@ -69,7 +69,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "3b72d5b4fe2f49c2b35a5db98e591f08", + "model_id": "eeefdf04c3d1462e97da6c3106f8b112", "version_major": 2, "version_minor": 0 }, @@ -99,14 +99,14 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 5, "id": "91a31531-818b-46a2-9587-5d9ef5b59b93", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "467b6f0979ab4130a6401272d680adc4", + "model_id": "3d5e0cae2f7e44ddbd37c46963d7ece8", "version_major": 2, "version_minor": 0 }, @@ -126,7 +126,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 6, "id": "e96bbda7-3693-42f2-bd52-f668f39134f6", "metadata": {}, "outputs": [], @@ -138,14 +138,14 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 7, "id": "03b877ba-cf9c-47d9-a0e5-b3e694274a28", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "391875b2bfce4dfc8e35e1c573e4b020", + "model_id": "3c2b74b3cf764ebba51587be7b143df4", "version_major": 2, "version_minor": 0 }, @@ -153,7 +153,7 @@ "VBox(children=(JupyterWgpuCanvas(), HBox(children=(Button(icon='expand-arrows-alt', layout=Layout(width='auto'…" ] }, - "execution_count": 9, + "execution_count": 7, "metadata": {}, "output_type": "execute_result" } @@ -162,6 +162,27 @@ "gp.show()" ] }, + { + "cell_type": "code", + "execution_count": 9, + "id": "afc0cd52-fb24-4561-9876-50fbdf784502", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(0, 1)" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gp[0,1].position" + ] + }, { "cell_type": "code", "execution_count": 8, diff --git a/fastplotlib/layouts/_gridplot.py b/fastplotlib/layouts/_gridplot.py index 051c124ce..972e14337 100644 --- a/fastplotlib/layouts/_gridplot.py +++ b/fastplotlib/layouts/_gridplot.py @@ -319,7 +319,7 @@ def __init__(self, layout=Layout(width='auto'), tooltip='maintain aspect') self.maintain_aspect_button.style.font_weight = "bold" self.flip_camera_button = Button(value=False, disabled=False, icon='sync-alt', - layout=Layout(width='auto'), tooltip='rotate') + layout=Layout(width='auto'), tooltip='flip') positions = list(product(range(self.plot.shape[0]), range(self.plot.shape[1]))) values = list() @@ -343,6 +343,9 @@ def __init__(self, self.maintain_aspect_button.observe(self.maintain_aspect, 'value') self.flip_camera_button.on_click(self.flip_camera) + self.plot.renderer.add_event_handler(self.click_handler, "click") + + @property def parser(self) -> Subplot: # parses dropdown value as plot name or position current = self.dropdown.value @@ -352,21 +355,31 @@ def parser(self) -> Subplot: return self.plot[current] def auto_scale(self, obj): - current = self.parser() + current = self.parser current.auto_scale(maintain_aspect=current.camera.maintain_aspect) def center_scene(self, obj): - current = self.parser() + current = self.parser current.center_scene() def panzoom_control(self, obj): - current = self.parser() + current = self.parser current.controller.enabled = self.panzoom_controller_button.value def maintain_aspect(self, obj): - current = self.parser() + current = self.parser current.camera.maintain_aspect = self.maintain_aspect_button.value def flip_camera(self, obj): - current = self.parser() + current = self.parser current.camera.scale.y = -1 * current.camera.scale.y + + def click_handler(self, ev): + for subplot in self.plot: + pos = subplot.map_screen_to_world((ev.x, ev.y)) + if pos is not None: + # update self.dropdown + if subplot.name is None: + self.dropdown.value = str(subplot.position) + else: + self.dropdown.value = subplot.name diff --git a/fastplotlib/plot.py b/fastplotlib/plot.py index 06f10b2cd..d2722fcee 100644 --- a/fastplotlib/plot.py +++ b/fastplotlib/plot.py @@ -143,7 +143,7 @@ def __init__(self, tooltip='maintain aspect') self.maintain_aspect_button.style.font_weight = "bold" self.flip_camera_button = Button(value=False, disabled=False, icon='sync-alt', - layout=Layout(width='auto'), tooltip='rotate') + layout=Layout(width='auto'), tooltip='flip') self.widget = HBox([self.autoscale_button, self.center_scene_button, From 5bb9f00f998164c3713d44bdef152b5270d55cd0 Mon Sep 17 00:00:00 2001 From: Caitlin Lewis Date: Tue, 23 May 2023 08:52:33 -0400 Subject: [PATCH 11/12] keep new clicked plot up-to-date with toggle button values --- examples/buttons.ipynb | 10 +++++----- fastplotlib/layouts/_gridplot.py | 3 +++ 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/examples/buttons.ipynb b/examples/buttons.ipynb index 850d503d6..e37bc6cc3 100644 --- a/examples/buttons.ipynb +++ b/examples/buttons.ipynb @@ -21,7 +21,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "36a6b32ac78745138c0c788cd277c569", + "model_id": "118fecf8d6ee465f80606e17e9a695ae", "version_major": 2, "version_minor": 0 }, @@ -43,7 +43,7 @@ { "data": { "text/plain": [ - "" + "" ] }, "execution_count": 2, @@ -69,7 +69,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "eeefdf04c3d1462e97da6c3106f8b112", + "model_id": "211b5bf2a7b844b7ac0b29e0d5fb7a1b", "version_major": 2, "version_minor": 0 }, @@ -106,7 +106,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "3d5e0cae2f7e44ddbd37c46963d7ece8", + "model_id": "27218902a54040dba00dca49763030db", "version_major": 2, "version_minor": 0 }, @@ -145,7 +145,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "3c2b74b3cf764ebba51587be7b143df4", + "model_id": "4634dd4f6abf45e0b293a43064604e0f", "version_major": 2, "version_minor": 0 }, diff --git a/fastplotlib/layouts/_gridplot.py b/fastplotlib/layouts/_gridplot.py index 972e14337..b2d1c1dac 100644 --- a/fastplotlib/layouts/_gridplot.py +++ b/fastplotlib/layouts/_gridplot.py @@ -383,3 +383,6 @@ def click_handler(self, ev): self.dropdown.value = str(subplot.position) else: self.dropdown.value = subplot.name + subplot.controller.enabled = self.panzoom_controller_button.value + subplot.camera.maintain_aspect = self.maintain_aspect_button.value + From 561b4eba06609ea99d2fc47c88ea105f107c4482 Mon Sep 17 00:00:00 2001 From: Caitlin Lewis Date: Tue, 23 May 2023 11:37:04 -0400 Subject: [PATCH 12/12] requested changes --- examples/buttons.ipynb | 10 +++++----- fastplotlib/layouts/_gridplot.py | 20 ++++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/examples/buttons.ipynb b/examples/buttons.ipynb index e37bc6cc3..60419a229 100644 --- a/examples/buttons.ipynb +++ b/examples/buttons.ipynb @@ -21,7 +21,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "118fecf8d6ee465f80606e17e9a695ae", + "model_id": "95be7ab3326347359f783946ec8d9339", "version_major": 2, "version_minor": 0 }, @@ -43,7 +43,7 @@ { "data": { "text/plain": [ - "" + "" ] }, "execution_count": 2, @@ -69,7 +69,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "211b5bf2a7b844b7ac0b29e0d5fb7a1b", + "model_id": "cecbc6a1fec54d03876ac5a8609e5200", "version_major": 2, "version_minor": 0 }, @@ -106,7 +106,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "27218902a54040dba00dca49763030db", + "model_id": "05c6d9d2f62846e8ab6135a3d218964c", "version_major": 2, "version_minor": 0 }, @@ -145,7 +145,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "4634dd4f6abf45e0b293a43064604e0f", + "model_id": "9b792858ff24411db756810bb8eea00f", "version_major": 2, "version_minor": 0 }, diff --git a/fastplotlib/layouts/_gridplot.py b/fastplotlib/layouts/_gridplot.py index b2d1c1dac..1c16d2e80 100644 --- a/fastplotlib/layouts/_gridplot.py +++ b/fastplotlib/layouts/_gridplot.py @@ -343,10 +343,10 @@ def __init__(self, self.maintain_aspect_button.observe(self.maintain_aspect, 'value') self.flip_camera_button.on_click(self.flip_camera) - self.plot.renderer.add_event_handler(self.click_handler, "click") + self.plot.renderer.add_event_handler(self.update_current_subplot, "click") @property - def parser(self) -> Subplot: + def current_subplot(self) -> Subplot: # parses dropdown value as plot name or position current = self.dropdown.value if current[0] == "(": @@ -355,26 +355,26 @@ def parser(self) -> Subplot: return self.plot[current] def auto_scale(self, obj): - current = self.parser + current = self.current_subplot current.auto_scale(maintain_aspect=current.camera.maintain_aspect) def center_scene(self, obj): - current = self.parser + current = self.current_subplot current.center_scene() def panzoom_control(self, obj): - current = self.parser + current = self.current_subplot current.controller.enabled = self.panzoom_controller_button.value def maintain_aspect(self, obj): - current = self.parser + current = self.current_subplot current.camera.maintain_aspect = self.maintain_aspect_button.value def flip_camera(self, obj): - current = self.parser + current = self.current_subplot current.camera.scale.y = -1 * current.camera.scale.y - def click_handler(self, ev): + def update_current_subplot(self, ev): for subplot in self.plot: pos = subplot.map_screen_to_world((ev.x, ev.y)) if pos is not None: @@ -383,6 +383,6 @@ def click_handler(self, ev): self.dropdown.value = str(subplot.position) else: self.dropdown.value = subplot.name - subplot.controller.enabled = self.panzoom_controller_button.value - subplot.camera.maintain_aspect = self.maintain_aspect_button.value + self.panzoom_controller_button.value = subplot.controller.enabled + self.maintain_aspect_button.value = subplot.camera.maintain_aspect