diff --git a/docs/source/api/graphics/HeatmapGraphic.rst b/docs/source/api/graphics/HeatmapGraphic.rst index 6da6f6531..3bd2f2baa 100644 --- a/docs/source/api/graphics/HeatmapGraphic.rst +++ b/docs/source/api/graphics/HeatmapGraphic.rst @@ -26,8 +26,6 @@ Properties HeatmapGraphic.position_y HeatmapGraphic.position_z HeatmapGraphic.visible - HeatmapGraphic.vmax - HeatmapGraphic.vmin HeatmapGraphic.world_object Methods diff --git a/docs/source/api/layouts/gridplot.rst b/docs/source/api/layouts/gridplot.rst index 63f1516cf..b5b03bfa4 100644 --- a/docs/source/api/layouts/gridplot.rst +++ b/docs/source/api/layouts/gridplot.rst @@ -22,6 +22,8 @@ Properties GridPlot.canvas GridPlot.renderer + GridPlot.toolbar + GridPlot.widget Methods ~~~~~~~ @@ -36,4 +38,5 @@ Methods GridPlot.remove_animation GridPlot.render GridPlot.show + GridPlot.start_render diff --git a/docs/source/api/layouts/plot.rst b/docs/source/api/layouts/plot.rst index a0be9287b..bd38720b4 100644 --- a/docs/source/api/layouts/plot.rst +++ b/docs/source/api/layouts/plot.rst @@ -31,7 +31,9 @@ Properties Plot.renderer Plot.scene Plot.selectors + Plot.toolbar Plot.viewport + Plot.widget Methods ~~~~~~~ @@ -67,4 +69,5 @@ Methods Plot.set_title Plot.set_viewport_rect Plot.show + Plot.start_render diff --git a/docs/source/api/selectors/Synchronizer.rst b/docs/source/api/selectors/Synchronizer.rst index d0fa0c2a8..2b28fe351 100644 --- a/docs/source/api/selectors/Synchronizer.rst +++ b/docs/source/api/selectors/Synchronizer.rst @@ -28,5 +28,6 @@ Methods :toctree: Synchronizer_api Synchronizer.add + Synchronizer.clear Synchronizer.remove diff --git a/docs/source/api/widgets/ImageWidget.rst b/docs/source/api/widgets/ImageWidget.rst index 4e779f20b..08bce8d7a 100644 --- a/docs/source/api/widgets/ImageWidget.rst +++ b/docs/source/api/widgets/ImageWidget.rst @@ -29,6 +29,7 @@ Properties ImageWidget.ndim ImageWidget.slider_dims ImageWidget.sliders + ImageWidget.widget ImageWidget.window_funcs Methods @@ -38,6 +39,7 @@ Methods ImageWidget.close ImageWidget.reset_vmin_vmax + ImageWidget.reset_vmin_vmax_frame ImageWidget.set_data ImageWidget.show diff --git a/examples/desktop/gridplot/gridplot_non_square.py b/examples/desktop/gridplot/gridplot_non_square.py new file mode 100644 index 000000000..a41bcd9f4 --- /dev/null +++ b/examples/desktop/gridplot/gridplot_non_square.py @@ -0,0 +1,34 @@ +""" +GridPlot Simple +============ +Example showing simple 2x2 GridPlot with Standard images from imageio. +""" + +# test_example = true + +import fastplotlib as fpl +import imageio.v3 as iio + + +plot = fpl.GridPlot(shape=(2, 2), controllers="sync") +# to force a specific framework such as glfw: +# plot = fpl.GridPlot(canvas="glfw") + +im = iio.imread("imageio:clock.png") +im2 = iio.imread("imageio:astronaut.png") +im3 = iio.imread("imageio:coffee.png") + +plot[0, 0].add_image(data=im) +plot[0, 1].add_image(data=im2) +plot[1, 0].add_image(data=im3) + +plot.show() + +plot.canvas.set_logical_size(800, 800) + +for subplot in plot: + subplot.auto_scale() + +if __name__ == "__main__": + print(__doc__) + fpl.run() diff --git a/examples/desktop/heatmap/__init__.py b/examples/desktop/heatmap/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/examples/desktop/heatmap/heatmap.py b/examples/desktop/heatmap/heatmap.py new file mode 100644 index 000000000..45c340cbd --- /dev/null +++ b/examples/desktop/heatmap/heatmap.py @@ -0,0 +1,37 @@ +""" +Simple Heatmap +============== +Example showing how to plot a heatmap +""" + +# test_example = true + +import fastplotlib as fpl +import numpy as np + +plot = fpl.Plot() +# to force a specific framework such as glfw: +# plot = fpl.Plot(canvas="glfw") + +xs = np.linspace(0, 1_000, 10_000) + +sine = np.sin(xs) +cosine = np.cos(xs) + +# alternating sines and cosines +data = np.zeros((10_000, 10_000), dtype=np.float32) +data[::2] = sine +data[1::2] = cosine + +# plot the image data +heatmap_graphic = plot.add_heatmap(data=data, name="heatmap") + +plot.show() + +plot.canvas.set_logical_size(1500, 1500) + +plot.auto_scale() + +if __name__ == "__main__": + print(__doc__) + fpl.run() diff --git a/examples/desktop/heatmap/heatmap_cmap.py b/examples/desktop/heatmap/heatmap_cmap.py new file mode 100644 index 000000000..afc67f5b8 --- /dev/null +++ b/examples/desktop/heatmap/heatmap_cmap.py @@ -0,0 +1,39 @@ +""" +Heatmap change cmap +=================== +Change the cmap of a heatmap +""" + +# test_example = true + +import fastplotlib as fpl +import numpy as np + +plot = fpl.Plot() +# to force a specific framework such as glfw: +# plot = fpl.Plot(canvas="glfw") + +xs = np.linspace(0, 1_000, 10_000) + +sine = np.sin(xs) +cosine = np.cos(xs) + +# alternating sines and cosines +data = np.zeros((10_000, 10_000), dtype=np.float32) +data[::2] = sine +data[1::2] = cosine + +# plot the image data +heatmap_graphic = plot.add_heatmap(data=data, name="heatmap") + +plot.show() + +plot.canvas.set_logical_size(1500, 1500) + +plot.auto_scale() + +heatmap_graphic.cmap = "viridis" + +if __name__ == "__main__": + print(__doc__) + fpl.run() diff --git a/examples/desktop/heatmap/heatmap_data.py b/examples/desktop/heatmap/heatmap_data.py new file mode 100644 index 000000000..78e819ab8 --- /dev/null +++ b/examples/desktop/heatmap/heatmap_data.py @@ -0,0 +1,41 @@ +""" +Heatmap change data +=================== +Change the data of a heatmap +""" + +# test_example = true + +import fastplotlib as fpl +import numpy as np + +plot = fpl.Plot() +# to force a specific framework such as glfw: +# plot = fpl.Plot(canvas="glfw") + +xs = np.linspace(0, 1_000, 10_000) + +sine = np.sin(xs) +cosine = np.cos(xs) + +# alternating sines and cosines +data = np.zeros((10_000, 10_000), dtype=np.float32) +data[::2] = sine +data[1::2] = cosine + +# plot the image data +heatmap_graphic = plot.add_heatmap(data=data, name="heatmap") + +plot.show() + +plot.canvas.set_logical_size(1500, 1500) + +plot.auto_scale() + +heatmap_graphic.data[:5_000] = sine +heatmap_graphic.data[5_000:] = cosine + + +if __name__ == "__main__": + print(__doc__) + fpl.run() diff --git a/examples/desktop/heatmap/heatmap_vmin_vmax.py b/examples/desktop/heatmap/heatmap_vmin_vmax.py new file mode 100644 index 000000000..7aae1d6d3 --- /dev/null +++ b/examples/desktop/heatmap/heatmap_vmin_vmax.py @@ -0,0 +1,40 @@ +""" +Heatmap change vmin vmax +======================== +Change the vmin vmax of a heatmap +""" + +# test_example = true + +import fastplotlib as fpl +import numpy as np + +plot = fpl.Plot() +# to force a specific framework such as glfw: +# plot = fpl.Plot(canvas="glfw") + +xs = np.linspace(0, 1_000, 10_000) + +sine = np.sin(xs) +cosine = np.cos(xs) + +# alternating sines and cosines +data = np.zeros((10_000, 10_000), dtype=np.float32) +data[::2] = sine +data[1::2] = cosine + +# plot the image data +heatmap_graphic = plot.add_heatmap(data=data, name="heatmap") + +plot.show() + +plot.canvas.set_logical_size(1500, 1500) + +plot.auto_scale() + +heatmap_graphic.cmap.vmin = -0.5 +heatmap_graphic.cmap.vmax = 0.5 + +if __name__ == "__main__": + print(__doc__) + fpl.run() diff --git a/examples/desktop/screenshots/gridplot_non_square.png b/examples/desktop/screenshots/gridplot_non_square.png new file mode 100644 index 000000000..7b534aef9 --- /dev/null +++ b/examples/desktop/screenshots/gridplot_non_square.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:abf936904d1b5e2018a72311c510108925f2972dfdf59166580ad27876f9e2be +size 220140 diff --git a/examples/desktop/screenshots/heatmap.png b/examples/desktop/screenshots/heatmap.png new file mode 100644 index 000000000..d0df1510a --- /dev/null +++ b/examples/desktop/screenshots/heatmap.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6c19d6454e79d92074bac01175dfbb8506e882ea55b626c0b2357960ed6e294f +size 163655 diff --git a/examples/desktop/screenshots/heatmap_cmap.png b/examples/desktop/screenshots/heatmap_cmap.png new file mode 100644 index 000000000..db3038dee --- /dev/null +++ b/examples/desktop/screenshots/heatmap_cmap.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0328eec32f13042e3e2f243793317e180bba1353fe961604ecad3f38463b8809 +size 156419 diff --git a/examples/desktop/screenshots/heatmap_data.png b/examples/desktop/screenshots/heatmap_data.png new file mode 100644 index 000000000..96169ec77 --- /dev/null +++ b/examples/desktop/screenshots/heatmap_data.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4a376c24fa123088be69f807ec5212cb5ed5680b146ce9d62df584790c632845 +size 20838 diff --git a/examples/desktop/screenshots/heatmap_vmin_vmax.png b/examples/desktop/screenshots/heatmap_vmin_vmax.png new file mode 100644 index 000000000..2a809d545 --- /dev/null +++ b/examples/desktop/screenshots/heatmap_vmin_vmax.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3eb12ad590aa8260f2cf722abadf5b51bcb7d5a8d8d1cb05b7711b50331da07a +size 165937 diff --git a/examples/tests/testutils.py b/examples/tests/testutils.py index 6155f8763..5f6772fb7 100644 --- a/examples/tests/testutils.py +++ b/examples/tests/testutils.py @@ -16,6 +16,7 @@ # examples live in themed sub-folders example_globs = [ "image/*.py", + "heatmap/*.py", "scatter/*.py", "line/*.py", "line_collection/*.py", diff --git a/fastplotlib/VERSION b/fastplotlib/VERSION index 2952df5a6..a0ad4fd5d 100644 --- a/fastplotlib/VERSION +++ b/fastplotlib/VERSION @@ -1 +1 @@ -0.1.0.a14 +0.1.0.a15 diff --git a/fastplotlib/graphics/_features/_colors.py b/fastplotlib/graphics/_features/_colors.py index 85014155d..b6723b34b 100644 --- a/fastplotlib/graphics/_features/_colors.py +++ b/fastplotlib/graphics/_features/_colors.py @@ -401,8 +401,29 @@ class HeatmapCmapFeature(ImageCmapFeature): """ def _set(self, cmap_name: str): + # in heatmap we use one material for all ImageTiles self._parent._material.map.data[:] = make_colors(256, cmap_name) self._parent._material.map.update_range((0, 0, 0), size=(256, 1, 1)) - self.name = cmap_name + self._name = cmap_name self._feature_changed(key=None, new_data=self.name) + + @property + def vmin(self) -> float: + """Minimum contrast limit.""" + return self._parent._material.clim[0] + + @vmin.setter + def vmin(self, value: float): + """Minimum contrast limit.""" + self._parent._material.clim = (value, self._parent._material.clim[1]) + + @property + def vmax(self) -> float: + """Maximum contrast limit.""" + return self._parent._material.clim[1] + + @vmax.setter + def vmax(self, value: float): + """Maximum contrast limit.""" + self._parent._material.clim = (self._parent._material.clim[0], value) \ No newline at end of file diff --git a/fastplotlib/graphics/image.py b/fastplotlib/graphics/image.py index 12ac9e41d..10f09eefb 100644 --- a/fastplotlib/graphics/image.py +++ b/fastplotlib/graphics/image.py @@ -480,26 +480,6 @@ def __init__( # set it with the actual data self.data = data - @property - def vmin(self) -> float: - """Minimum contrast limit.""" - return self._material.clim[0] - - @vmin.setter - def vmin(self, value: float): - """Minimum contrast limit.""" - self._material.clim = (value, self._material.clim[1]) - - @property - def vmax(self) -> float: - """Maximum contrast limit.""" - return self._material.clim[1] - - @vmax.setter - def vmax(self, value: float): - """Maximum contrast limit.""" - self._material.clim = (self._material.clim[0], value) - def set_feature(self, feature: str, new_data: Any, indices: Any): pass diff --git a/fastplotlib/graphics/selectors/_base_selector.py b/fastplotlib/graphics/selectors/_base_selector.py index e892ca32d..e6796f270 100644 --- a/fastplotlib/graphics/selectors/_base_selector.py +++ b/fastplotlib/graphics/selectors/_base_selector.py @@ -252,7 +252,8 @@ def _move_end(self, ev): # restore the initial controller state # if it was disabled, keep it disabled - self._plot_area.controller.enabled = self._initial_controller_state + if self._initial_controller_state is not None: + self._plot_area.controller.enabled = self._initial_controller_state def _move_to_pointer(self, ev): """ diff --git a/fastplotlib/layouts/_plot_area.py b/fastplotlib/layouts/_plot_area.py index 2060850c2..7590bad10 100644 --- a/fastplotlib/layouts/_plot_area.py +++ b/fastplotlib/layouts/_plot_area.py @@ -495,7 +495,9 @@ def auto_scale(self, maintain_aspect: bool = False, zoom: float = 0.8): zoom value for the camera after auto-scaling, if zoom = 1.0 then the graphics in the scene will fill the entire canvas. """ - # hacky workaround for now until we decided if we want to put selectors in their own scene + if not len(self.scene.children) > 0: + return + # hacky workaround for now until we decide if we want to put selectors in their own scene # remove all selectors from a scene to calculate scene bbox for selector in self.selectors: self.scene.remove(selector.world_object) diff --git a/fastplotlib/utils/functions.py b/fastplotlib/utils/functions.py index 3013559d5..8d1e8694f 100644 --- a/fastplotlib/utils/functions.py +++ b/fastplotlib/utils/functions.py @@ -26,6 +26,23 @@ def get_cmap(name: str, alpha: float = 1.0) -> np.ndarray: + """ + Get a colormap as numpy array + + Parameters + ---------- + name: str + name of colormap + alpha: float + alpha, 0.0 - 1.0 + + Returns + ------- + np.ndarray + [n_colors, 4], i.e. [n_colors, RGBA] + + """ + cmap_path = Path(__file__).absolute().parent.joinpath("colormaps", name) if cmap_path.is_file(): cmap = np.loadtxt(cmap_path) @@ -214,6 +231,9 @@ def calculate_gridshape(n_subplots: int) -> Tuple[int, int]: def normalize_min_max(a): """normalize an array between 0 - 1""" + if np.unique(a).size == 1: + return np.zeros(a.size) + return (a - np.min(a)) / (np.max(a - np.min(a))) diff --git a/fastplotlib/widgets/image.py b/fastplotlib/widgets/image.py index a9ebfafb4..da256207f 100644 --- a/fastplotlib/widgets/image.py +++ b/fastplotlib/widgets/image.py @@ -225,6 +225,7 @@ def __init__( grid_shape: Tuple[int, int] = None, names: List[str] = None, grid_plot_kwargs: dict = None, + histogram_widget: bool = True, **kwargs, ): """ @@ -288,6 +289,9 @@ def __init__( names: Optional[str] gives names to the subplots + histogram_widget: bool, default False + make histogram LUT widget for each subplot + kwargs: Any passed to fastplotlib.graphics.Image @@ -556,16 +560,17 @@ def __init__( subplot.name = name subplot.set_title(name) - hlut = HistogramLUT( - data=d, - image_graphic=ig, - name="histogram_lut" - ) + if histogram_widget: + hlut = HistogramLUT( + data=d, + image_graphic=ig, + name="histogram_lut" + ) - subplot.docks["right"].add_graphic(hlut) - subplot.docks["right"].size = 80 - subplot.docks["right"].auto_scale(maintain_aspect=False) - subplot.docks["right"].controller.enabled = False + subplot.docks["right"].add_graphic(hlut) + subplot.docks["right"].size = 80 + subplot.docks["right"].auto_scale(maintain_aspect=False) + subplot.docks["right"].controller.enabled = False self.block_sliders = False self._image_widget_toolbar = None @@ -869,9 +874,6 @@ def set_data( # force graphics to update self.current_index = self.current_index - # if reset_vmin_vmax: - # self.reset_vmin_vmax() - def show(self, toolbar: bool = True, sidecar: bool = False, sidecar_kwargs: dict = None): """ Show the widget pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy