From c78bcc079fac81aecfac5a9634de365fe09952e2 Mon Sep 17 00:00:00 2001 From: ArjunPutcha Date: Thu, 29 Jun 2023 06:16:40 -0400 Subject: [PATCH 1/7] sync subplots by name upon initialization; added example in gridplot_simple.ipynb --- examples/notebooks/gridplot_simple.ipynb | 177 +++++++++++------------ fastplotlib/layouts/_gridplot.py | 30 +++- 2 files changed, 109 insertions(+), 98 deletions(-) diff --git a/examples/notebooks/gridplot_simple.ipynb b/examples/notebooks/gridplot_simple.ipynb index f90c0b157..b05880b12 100644 --- a/examples/notebooks/gridplot_simple.ipynb +++ b/examples/notebooks/gridplot_simple.ipynb @@ -28,7 +28,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "5e4e0c5ca610425b8216db8e30cae997", + "model_id": "db8363be87c04ca4be69f1df9e40946c", "version_major": 2, "version_minor": 0 }, @@ -40,26 +40,30 @@ "output_type": "display_data" }, { - "data": { - "text/html": [ - "
initial snapshot
" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" + "name": "stdout", + "output_type": "stream", + "text": [ + "[['red' 'ir' 'violet']\n", + " ['magenta' 'cyan' 'maroon']]\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/aju/Desktop/fastplotlib/fastplotlib/graphics/_features/_base.py:34: 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": "1eeb8c42e1b24c4fb40e3b5daa63909a", + "model_id": "4cd7cc6685b64dee94d4ca71fd914348", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "JupyterWgpuCanvas()" + "VBox(children=(JupyterWgpuCanvas(), HBox(children=(Button(icon='expand-arrows-alt', layout=Layout(width='auto'…" ] }, "execution_count": 2, @@ -69,7 +73,17 @@ ], "source": [ "# GridPlot of shape 2 x 3 with all controllers synced\n", - "grid_plot = GridPlot(shape=(2, 3), controllers=\"sync\")\n", + "names = [\n", + " [\"red\", \"ir\", \"violet\"],\n", + " [\"magenta\", \"cyan\", \"maroon\"]\n", + "]\n", + "\n", + "controllers = [\n", + " [\"red\", \"ir\"],\n", + " [\"violet\", \"magenta\"]\n", + "]\n", + "\n", + "grid_plot = GridPlot(shape=(2, 3), controllers=controllers, names=names)\n", "\n", "# Make a random image graphic for each subplot\n", "for subplot in grid_plot:\n", @@ -103,24 +117,10 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "id": "17c6bc4a-5340-49f1-8597-f54528cfe915", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "unnamed: Subplot @ 0x7fd4cc9bf820\n", - " parent: None\n", - " Graphics:\n", - "\t'rand-img': ImageGraphic @ 0x7fd4f675a350" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# positional indexing\n", "# row 0 and col 0\n", @@ -137,21 +137,10 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "id": "34130f12-9ef6-43b0-b929-931de8b7da25", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "('rand-img': ImageGraphic @ 0x7fd4a03295a0,)" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "grid_plot[0, 1].graphics" ] @@ -166,7 +155,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "id": "ef8a29a6-b19c-4e6b-a2ba-fb4823c01451", "metadata": {}, "outputs": [], @@ -184,7 +173,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "id": "d6c2fa4b-c634-4dcf-8b61-f1986f7c4918", "metadata": {}, "outputs": [], @@ -195,45 +184,20 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "id": "2f6b549c-3165-496d-98aa-45b96c3de674", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "top-right-plot: Subplot @ 0x7fd4cca0ffd0\n", - " parent: None\n", - " Graphics:\n", - "\t'rand-img': ImageGraphic @ 0x7fd4a03716c0" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "grid_plot[\"top-right-plot\"]" ] }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "id": "be436e04-33a6-4597-8e6a-17e1e5225419", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(0, 2)" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# view its position\n", "grid_plot[\"top-right-plot\"].position" @@ -241,21 +205,10 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "id": "6699cda6-af86-4258-87f5-1832f989a564", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "True" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# these are really the same\n", "grid_plot[\"top-right-plot\"] is grid_plot[0, 2]" @@ -271,7 +224,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "id": "545b627b-d794-459a-a75a-3fde44f0ea95", "metadata": {}, "outputs": [], @@ -279,13 +232,55 @@ "grid_plot[\"top-right-plot\"][\"rand-img\"].vmin = 0.5" ] }, + { + "cell_type": "markdown", + "id": "8cb1db84-c2bb-4eaf-be55-1f45e27b2f93", + "metadata": {}, + "source": [ + "## Syncing subplots by name" + ] + }, { "cell_type": "code", "execution_count": null, - "id": "36432d5b-b76c-4a2a-a32c-097faf5ab269", + "id": "4ba1fab3-27ca-4b2f-8b41-4dd306fe7bd9", "metadata": {}, "outputs": [], - "source": [] + "source": [ + "# GridPlot of shape 2 x 3 with \n", + "names = [\n", + " [\"red\", \"ir\", \"violet\"],\n", + " [\"magenta\", \"cyan\", \"maroon\"]\n", + "]\n", + "\n", + "controllers = [\n", + " [\"red\", \"ir\"],\n", + " [\"violet\", \"magenta\"]\n", + "]\n", + "\n", + "grid_plot = GridPlot(shape=(2, 3), controllers=controllers, names=names)\n", + "\n", + "# Make a random image graphic for each subplot\n", + "for subplot in grid_plot:\n", + " # create image data\n", + " data = np.random.rand(512, 512)\n", + " # add an image to the subplot\n", + " subplot.add_image(data, name=\"rand-img\")\n", + "\n", + "# Define a function to update the image graphics with new data\n", + "# add_animations will pass the gridplot to the animation function\n", + "def update_data(gp):\n", + " for sp in gp:\n", + " new_data = np.random.rand(512, 512)\n", + " # index the image graphic by name and set the data\n", + " sp[\"rand-img\"].data = new_data\n", + " \n", + "# add the animation function\n", + "grid_plot.add_animations(update_data)\n", + "\n", + "# show the gridplot \n", + "grid_plot.show()" + ] } ], "metadata": { @@ -304,7 +299,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.5" + "version": "3.11.3" } }, "nbformat": 4, diff --git a/fastplotlib/layouts/_gridplot.py b/fastplotlib/layouts/_gridplot.py index 3e9a16aac..66aec1a2b 100644 --- a/fastplotlib/layouts/_gridplot.py +++ b/fastplotlib/layouts/_gridplot.py @@ -92,12 +92,35 @@ def __init__( self.shape ) + if "names" in kwargs.keys(): + self.names = to_array(kwargs["names"]) + print(self.names) + if self.names.shape != self.shape: + raise ValueError + else: + self.names = None + if isinstance(controllers, str): if controllers == "sync": controllers = np.zeros( self.shape[0] * self.shape[1], dtype=int ).reshape(self.shape) + if (self.names is not None) & all(isinstance(item, str) for item in controllers[0]): + idx_async_cont = np.shape(controllers)[0] + c = to_array(controllers) + controllers = np.zeros( + self.shape[0] * self.shape[1], dtype=int + ).reshape(self.shape) + for i in range(self.shape[0]): + for j in range(self.shape[1]): + item = np.argwhere(c == self.names[i][j]) + if len(item) != 0: + controllers[i, j] = item[0][0] + else: + controllers[i, j] = idx_async_cont + idx_async_cont += 1 + if controllers is None: controllers = np.arange(self.shape[0] * self.shape[1]).reshape(self.shape) @@ -144,13 +167,6 @@ def __init__( if renderer is None: renderer = pygfx.renderers.WgpuRenderer(canvas) - if "names" in kwargs.keys(): - self.names = to_array(kwargs["names"]) - if self.names.shape != self.shape: - raise ValueError - else: - self.names = None - self.canvas = canvas self.renderer = renderer From a64b8418c762e816992052b082063ae06a0281fa Mon Sep 17 00:00:00 2001 From: ArjunPutcha Date: Thu, 29 Jun 2023 08:23:59 -0400 Subject: [PATCH 2/7] clear output in nb --- examples/notebooks/gridplot_simple.ipynb | 52 ++---------------------- 1 file changed, 3 insertions(+), 49 deletions(-) diff --git a/examples/notebooks/gridplot_simple.ipynb b/examples/notebooks/gridplot_simple.ipynb index b05880b12..0f8e1c704 100644 --- a/examples/notebooks/gridplot_simple.ipynb +++ b/examples/notebooks/gridplot_simple.ipynb @@ -10,7 +10,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "id": "5171a06e-1bdc-4908-9726-3c1fd45dbb9d", "metadata": {}, "outputs": [], @@ -21,56 +21,10 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "id": "86a2488f-ae1c-4b98-a7c0-18eae8013af1", "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "db8363be87c04ca4be69f1df9e40946c", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "RFBOutputContext()" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[['red' 'ir' 'violet']\n", - " ['magenta' 'cyan' 'maroon']]\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/aju/Desktop/fastplotlib/fastplotlib/graphics/_features/_base.py:34: 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": "4cd7cc6685b64dee94d4ca71fd914348", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "VBox(children=(JupyterWgpuCanvas(), HBox(children=(Button(icon='expand-arrows-alt', layout=Layout(width='auto'…" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# GridPlot of shape 2 x 3 with all controllers synced\n", "names = [\n", From f70da8cc69effd54a173dc34a8d567f39c230360 Mon Sep 17 00:00:00 2001 From: ArjunPutcha Date: Thu, 29 Jun 2023 09:45:32 -0400 Subject: [PATCH 3/7] small changes. check for self.names and controllers before checking if controllers has list of strings --- fastplotlib/layouts/_gridplot.py | 63 ++++++++++++++++---------------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/fastplotlib/layouts/_gridplot.py b/fastplotlib/layouts/_gridplot.py index 66aec1a2b..675423e1f 100644 --- a/fastplotlib/layouts/_gridplot.py +++ b/fastplotlib/layouts/_gridplot.py @@ -34,14 +34,14 @@ def to_array(a) -> np.ndarray: class GridPlot(RecordMixin): def __init__( - self, - shape: Tuple[int, int], - cameras: Union[np.ndarray, str] = "2d", - controllers: Union[np.ndarray, str] = None, - canvas: Union[str, WgpuCanvas, pygfx.Texture] = None, - renderer: pygfx.WgpuRenderer = None, - size: Tuple[int, int] = (500, 300), - **kwargs, + self, + shape: Tuple[int, int], + cameras: Union[np.ndarray, str] = "2d", + controllers: Union[np.ndarray, str] = None, + canvas: Union[str, WgpuCanvas, pygfx.Texture] = None, + renderer: pygfx.WgpuRenderer = None, + size: Tuple[int, int] = (500, 300), + **kwargs, ): """ A grid of subplots. @@ -94,9 +94,8 @@ def __init__( if "names" in kwargs.keys(): self.names = to_array(kwargs["names"]) - print(self.names) if self.names.shape != self.shape: - raise ValueError + raise ValueError(f"subplot names: {self.names} must be in gridplot shape: {self.shape}") else: self.names = None @@ -106,20 +105,22 @@ def __init__( self.shape[0] * self.shape[1], dtype=int ).reshape(self.shape) - if (self.names is not None) & all(isinstance(item, str) for item in controllers[0]): - idx_async_cont = np.shape(controllers)[0] - c = to_array(controllers) - controllers = np.zeros( - self.shape[0] * self.shape[1], dtype=int - ).reshape(self.shape) - for i in range(self.shape[0]): - for j in range(self.shape[1]): - item = np.argwhere(c == self.names[i][j]) - if len(item) != 0: - controllers[i, j] = item[0][0] - else: - controllers[i, j] = idx_async_cont - idx_async_cont += 1 + # Check if 'names' and 'controllers' both have values, and if the first row values of controller are strings + if self.names and controllers is not None: + if all(isinstance(item, str) for item in controllers[0]): + idx_async_cont = np.shape(controllers)[0] + c = to_array(controllers) + controllers = np.zeros( + self.shape[0] * self.shape[1], dtype=int + ).reshape(self.shape) + for i in range(self.shape[0]): + for j in range(self.shape[1]): + item = np.argwhere(c == self.names[i][j]) + if len(item) != 0: + controllers[i, j] = item[0][0] + else: + controllers[i, j] = idx_async_cont + idx_async_cont += 1 if controllers is None: controllers = np.arange(self.shape[0] * self.shape[1]).reshape(self.shape) @@ -139,8 +140,8 @@ def __init__( # create controllers if the arguments were integers if np.issubdtype(controllers.dtype, np.integer): if not np.all( - np.sort(np.unique(controllers)) - == np.arange(np.unique(controllers).size) + np.sort(np.unique(controllers)) + == np.arange(np.unique(controllers).size) ): raise ValueError("controllers must be consecutive integers") @@ -242,10 +243,10 @@ def _call_animate_functions(self, funcs: Iterable[callable]): fn() def add_animations( - self, - *funcs: Iterable[callable], - pre_render: bool = True, - post_render: bool = False, + self, + *funcs: Iterable[callable], + pre_render: bool = True, + post_render: bool = False, ): """ Add function(s) that are called on every render cycle. @@ -297,7 +298,7 @@ def remove_animation(self, func): self._animate_funcs_post.remove(func) def show( - self, autoscale: bool = True, maintain_aspect: bool = None, toolbar: bool = True + self, autoscale: bool = True, maintain_aspect: bool = None, toolbar: bool = True ): """ Begins the rendering event loop and returns the canvas From ff7edfcdb3ccfaa9afedd7323c08e9264a28e96a Mon Sep 17 00:00:00 2001 From: ArjunPutcha Date: Thu, 29 Jun 2023 13:08:04 -0400 Subject: [PATCH 4/7] used itertools to remove nested for loop. also modified second if statement to only check if the first element is a str: converting to array should make all elements the same type, inefficient to check all indices --- examples/notebooks/gridplot_simple.ipynb | 22 ++++++++++----------- fastplotlib/layouts/_gridplot.py | 25 ++++++++++++------------ 2 files changed, 22 insertions(+), 25 deletions(-) diff --git a/examples/notebooks/gridplot_simple.ipynb b/examples/notebooks/gridplot_simple.ipynb index 0f8e1c704..9445dc22e 100644 --- a/examples/notebooks/gridplot_simple.ipynb +++ b/examples/notebooks/gridplot_simple.ipynb @@ -27,17 +27,7 @@ "outputs": [], "source": [ "# GridPlot of shape 2 x 3 with all controllers synced\n", - "names = [\n", - " [\"red\", \"ir\", \"violet\"],\n", - " [\"magenta\", \"cyan\", \"maroon\"]\n", - "]\n", - "\n", - "controllers = [\n", - " [\"red\", \"ir\"],\n", - " [\"violet\", \"magenta\"]\n", - "]\n", - "\n", - "grid_plot = GridPlot(shape=(2, 3), controllers=controllers, names=names)\n", + "grid_plot = GridPlot(shape=(2, 3), controllers=\"sync\")\n", "\n", "# Make a random image graphic for each subplot\n", "for subplot in grid_plot:\n", @@ -209,7 +199,7 @@ "\n", "controllers = [\n", " [\"red\", \"ir\"],\n", - " [\"violet\", \"magenta\"]\n", + " [\"violet\", 1]\n", "]\n", "\n", "grid_plot = GridPlot(shape=(2, 3), controllers=controllers, names=names)\n", @@ -235,6 +225,14 @@ "# show the gridplot \n", "grid_plot.show()" ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ee7c802e-ebaf-4fd2-97df-cd79ba048580", + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { diff --git a/fastplotlib/layouts/_gridplot.py b/fastplotlib/layouts/_gridplot.py index 675423e1f..9785a1506 100644 --- a/fastplotlib/layouts/_gridplot.py +++ b/fastplotlib/layouts/_gridplot.py @@ -105,22 +105,21 @@ def __init__( self.shape[0] * self.shape[1], dtype=int ).reshape(self.shape) - # Check if 'names' and 'controllers' both have values, and if the first row values of controller are strings - if self.names and controllers is not None: - if all(isinstance(item, str) for item in controllers[0]): - idx_async_cont = np.shape(controllers)[0] - c = to_array(controllers) + # Check if 'names' and 'controllers' both have values; check if values in controller array are strings + if (self.names is not None) and (controllers is not None): + c = to_array(controllers) + if isinstance(c[0, 0], str): + idx_async_controllers = np.shape(c)[0] controllers = np.zeros( self.shape[0] * self.shape[1], dtype=int ).reshape(self.shape) - for i in range(self.shape[0]): - for j in range(self.shape[1]): - item = np.argwhere(c == self.names[i][j]) - if len(item) != 0: - controllers[i, j] = item[0][0] - else: - controllers[i, j] = idx_async_cont - idx_async_cont += 1 + for positions in product(range(self.shape[0]), range(self.shape[1])): + item = np.argwhere(c == self.names[positions]) + if len(item) != 0: + controllers[positions] = item[0][0] + else: + controllers[positions] = idx_async_controllers + idx_async_controllers += 1 if controllers is None: controllers = np.arange(self.shape[0] * self.shape[1]).reshape(self.shape) From 3fce3da6444c35487e0b077050f31b8af2501db8 Mon Sep 17 00:00:00 2001 From: ArjunPutcha Date: Mon, 3 Jul 2023 06:17:11 -0400 Subject: [PATCH 5/7] spacing fix --- fastplotlib/layouts/_gridplot.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/fastplotlib/layouts/_gridplot.py b/fastplotlib/layouts/_gridplot.py index 9785a1506..6161aab75 100644 --- a/fastplotlib/layouts/_gridplot.py +++ b/fastplotlib/layouts/_gridplot.py @@ -34,14 +34,14 @@ def to_array(a) -> np.ndarray: class GridPlot(RecordMixin): def __init__( - self, - shape: Tuple[int, int], - cameras: Union[np.ndarray, str] = "2d", - controllers: Union[np.ndarray, str] = None, - canvas: Union[str, WgpuCanvas, pygfx.Texture] = None, - renderer: pygfx.WgpuRenderer = None, - size: Tuple[int, int] = (500, 300), - **kwargs, + self, + shape: Tuple[int, int], + cameras: Union[np.ndarray, str] = "2d", + controllers: Union[np.ndarray, str] = None, + canvas: Union[str, WgpuCanvas, pygfx.Texture] = None, + renderer: pygfx.WgpuRenderer = None, + size: Tuple[int, int] = (500, 300), + **kwargs, ): """ A grid of subplots. @@ -139,8 +139,8 @@ def __init__( # create controllers if the arguments were integers if np.issubdtype(controllers.dtype, np.integer): if not np.all( - np.sort(np.unique(controllers)) - == np.arange(np.unique(controllers).size) + np.sort(np.unique(controllers)) + == np.arange(np.unique(controllers).size) ): raise ValueError("controllers must be consecutive integers") @@ -242,10 +242,10 @@ def _call_animate_functions(self, funcs: Iterable[callable]): fn() def add_animations( - self, - *funcs: Iterable[callable], - pre_render: bool = True, - post_render: bool = False, + self, + *funcs: Iterable[callable], + pre_render: bool = True, + post_render: bool = False, ): """ Add function(s) that are called on every render cycle. @@ -297,7 +297,7 @@ def remove_animation(self, func): self._animate_funcs_post.remove(func) def show( - self, autoscale: bool = True, maintain_aspect: bool = None, toolbar: bool = True + self, autoscale: bool = True, maintain_aspect: bool = None, toolbar: bool = True ): """ Begins the rendering event loop and returns the canvas From 5f6225899d393c603978b4467c4b5baf10690daf Mon Sep 17 00:00:00 2001 From: ArjunPutcha Date: Wed, 5 Jul 2023 08:28:46 -0400 Subject: [PATCH 6/7] names is now a default kwarg with value None. Controllers must have same number of elements as gridplot shape. --- examples/notebooks/gridplot_simple.ipynb | 3 +- fastplotlib/layouts/_gridplot.py | 56 ++++++++++++++---------- 2 files changed, 34 insertions(+), 25 deletions(-) diff --git a/examples/notebooks/gridplot_simple.ipynb b/examples/notebooks/gridplot_simple.ipynb index 9445dc22e..788689807 100644 --- a/examples/notebooks/gridplot_simple.ipynb +++ b/examples/notebooks/gridplot_simple.ipynb @@ -199,7 +199,8 @@ "\n", "controllers = [\n", " [\"red\", \"ir\"],\n", - " [\"violet\", 1]\n", + " [\"violet\", \"magenta\"],\n", + " [\"cyan\", \"maroon\"]\n", "]\n", "\n", "grid_plot = GridPlot(shape=(2, 3), controllers=controllers, names=names)\n", diff --git a/fastplotlib/layouts/_gridplot.py b/fastplotlib/layouts/_gridplot.py index 6161aab75..af3124bb2 100644 --- a/fastplotlib/layouts/_gridplot.py +++ b/fastplotlib/layouts/_gridplot.py @@ -26,7 +26,7 @@ def to_array(a) -> np.ndarray: if not isinstance(a, list): raise TypeError("must pass list or numpy array") - return np.array(a) + return np.array(a, dtype=object) valid_cameras = ["2d", "2d-big", "3d", "3d-big"] @@ -38,6 +38,7 @@ def __init__( shape: Tuple[int, int], cameras: Union[np.ndarray, str] = "2d", controllers: Union[np.ndarray, str] = None, + names: Union[np.ndarray, str] = None, canvas: Union[str, WgpuCanvas, pygfx.Texture] = None, renderer: pygfx.WgpuRenderer = None, size: Tuple[int, int] = (500, 300), @@ -92,34 +93,41 @@ def __init__( self.shape ) - if "names" in kwargs.keys(): - self.names = to_array(kwargs["names"]) + if names is not None: + self.names = to_array(names) if self.names.shape != self.shape: raise ValueError(f"subplot names: {self.names} must be in gridplot shape: {self.shape}") + if not all(isinstance(self.names[i], str) for i in product(range(self.shape[0]), range(self.shape[1]))): + raise ValueError(f"subplot names: {self.names} must all be strings") else: self.names = None - if isinstance(controllers, str): - if controllers == "sync": - controllers = np.zeros( - self.shape[0] * self.shape[1], dtype=int - ).reshape(self.shape) - - # Check if 'names' and 'controllers' both have values; check if values in controller array are strings - if (self.names is not None) and (controllers is not None): - c = to_array(controllers) - if isinstance(c[0, 0], str): - idx_async_controllers = np.shape(c)[0] - controllers = np.zeros( - self.shape[0] * self.shape[1], dtype=int - ).reshape(self.shape) - for positions in product(range(self.shape[0]), range(self.shape[1])): - item = np.argwhere(c == self.names[positions]) - if len(item) != 0: - controllers[positions] = item[0][0] - else: - controllers[positions] = idx_async_controllers - idx_async_controllers += 1 + if controllers is not None: + if isinstance(controllers, str): + if controllers == "sync": + controllers = np.zeros( + self.shape[0] * self.shape[1], dtype=int + ).reshape(self.shape) + else: + c = to_array(controllers) + if (c.shape[0]*c.shape[1]) != (self.shape[0]*self.shape[1]): + raise ValueError(f"number of controllers: {controllers} must be the same as number of elements " + f"in gridplot shape {self.shape}") + if any(isinstance(c[i], str) for i in product(range(c.shape[0]), range(c.shape[1]))) and \ + any(not isinstance(c[i], str) for i in product(range(c.shape[0]), range(c.shape[1]))): + raise ValueError(f"controllers: {controllers} must all be the same type") + if self.names is not None: + if all(isinstance(c[i], str) for i in product(range(c.shape[0]), range(c.shape[1]))): + controllers = np.zeros( + self.shape[0] * self.shape[1], dtype=int + ).reshape(self.shape) + for positions in product(range(self.shape[0]), range(self.shape[1])): + controller_idx = np.argwhere(c == self.names[positions]) + if len(controller_idx) != 0: + controllers[positions] = controller_idx[0][0] + else: + raise ValueError(f"string names in controllers: {c} must be the same as " + f"string names in names: {self.names}") if controllers is None: controllers = np.arange(self.shape[0] * self.shape[1]).reshape(self.shape) From 96b82476bf6a0e2a71f1ca286fb48f8e0591ff78 Mon Sep 17 00:00:00 2001 From: ArjunPutcha Date: Thu, 6 Jul 2023 05:06:21 -0400 Subject: [PATCH 7/7] fixed raise ValueError statement. Added comments --- fastplotlib/layouts/_gridplot.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/fastplotlib/layouts/_gridplot.py b/fastplotlib/layouts/_gridplot.py index af3124bb2..66e2b72e3 100644 --- a/fastplotlib/layouts/_gridplot.py +++ b/fastplotlib/layouts/_gridplot.py @@ -95,14 +95,17 @@ def __init__( if names is not None: self.names = to_array(names) + # Check if shape of names kwarg is same as gridplot shape if self.names.shape != self.shape: raise ValueError(f"subplot names: {self.names} must be in gridplot shape: {self.shape}") + # Check if all elements in names array are strings if not all(isinstance(self.names[i], str) for i in product(range(self.shape[0]), range(self.shape[1]))): raise ValueError(f"subplot names: {self.names} must all be strings") else: self.names = None if controllers is not None: + # If value in controllers is 'sync', set all subplots to same controller if isinstance(controllers, str): if controllers == "sync": controllers = np.zeros( @@ -110,19 +113,24 @@ def __init__( ).reshape(self.shape) else: c = to_array(controllers) + # Confirm number of elements in controller array is same as number in gridplot shape if (c.shape[0]*c.shape[1]) != (self.shape[0]*self.shape[1]): - raise ValueError(f"number of controllers: {controllers} must be the same as number of elements " - f"in gridplot shape {self.shape}") + raise ValueError(f"number of controllers: {len(controllers)} must be the same as number of elements" + f" in gridplot shape {self.shape}") + # Confirm controllers array doesn't have multiple dtypes if any(isinstance(c[i], str) for i in product(range(c.shape[0]), range(c.shape[1]))) and \ any(not isinstance(c[i], str) for i in product(range(c.shape[0]), range(c.shape[1]))): raise ValueError(f"controllers: {controllers} must all be the same type") + # Check if names kwarg is given, and if all elements in controller array are strings if self.names is not None: if all(isinstance(c[i], str) for i in product(range(c.shape[0]), range(c.shape[1]))): controllers = np.zeros( self.shape[0] * self.shape[1], dtype=int ).reshape(self.shape) + # For each value in names array, find corresponding index in controller array for positions in product(range(self.shape[0]), range(self.shape[1])): controller_idx = np.argwhere(c == self.names[positions]) + # If name exists in controller array, set controller for item to the row number if len(controller_idx) != 0: controllers[positions] = controller_idx[0][0] else: