Thanks to visit codestin.com
Credit goes to github.com

Skip to content

histogram lut widget #344

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Oct 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions examples/notebooks/image_widget.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@
"source": [
"iw = ImageWidget(\n",
" data=a,\n",
" vmin_vmax_sliders=True,\n",
" cmap=\"viridis\"\n",
")"
]
Expand Down Expand Up @@ -113,7 +112,6 @@
"iw = ImageWidget(\n",
" data=a, \n",
" slider_dims=[\"t\"],\n",
" vmin_vmax_sliders=True,\n",
" cmap=\"gnuplot2\"\n",
")"
]
Expand Down Expand Up @@ -247,7 +245,6 @@
" data=data, \n",
" slider_dims=[\"t\"], \n",
" # dims_order=\"txy\", # you can set this manually if dim order is not the usual\n",
" vmin_vmax_sliders=True,\n",
" names=[\"zero\", \"one\", \"two\", \"three\"],\n",
" window_funcs={\"t\": (np.mean, 5)},\n",
" cmap=\"gnuplot2\", \n",
Expand Down Expand Up @@ -338,7 +335,6 @@
" data=data, \n",
" slider_dims=[\"t\", \"z\"], \n",
" dims_order=\"xyzt\", # example of how you can set this for non-standard orders\n",
" vmin_vmax_sliders=True,\n",
" names=[\"zero\", \"one\", \"two\", \"three\"],\n",
" # window_funcs={\"t\": (np.mean, 5)}, # window functions can be slow when indexing multiple dims\n",
" cmap=\"gnuplot2\", \n",
Expand Down Expand Up @@ -402,7 +398,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.3"
"version": "3.11.2"
}
},
"nbformat": 4,
Expand Down
13 changes: 11 additions & 2 deletions fastplotlib/graphics/selectors/_base_selector.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ def __init__(
# sets to `True` on "pointer_down", sets to `False` on "pointer_up"
self._moving = False #: indicates if the selector is currently being moved

self._initial_controller_state: bool = None

# used to disable fill area events if the edge is being actively hovered
# otherwise annoying and requires too much accuracy to move just an edge
self._edge_hovered: bool = False
Expand Down Expand Up @@ -201,6 +203,8 @@ def _move_start(self, event_source: WorldObject, ev):
self._move_info = MoveInfo(last_position=last_position, source=event_source)
self._moving = True

self._initial_controller_state = self._plot_area.controller.enabled

def _move(self, ev):
"""
Called on pointer move events
Expand Down Expand Up @@ -235,15 +239,20 @@ def _move(self, ev):
# update last position
self._move_info.last_position = world_pos

self._plot_area.controller.enabled = True
# restore the initial controller state
# if it was disabled, keep it disabled
self._plot_area.controller.enabled = self._initial_controller_state

def _move_graphic(self, delta: np.ndarray):
raise NotImplementedError("Must be implemented in subclass")

def _move_end(self, ev):
self._move_info = None
self._moving = False
self._plot_area.controller.enabled = True

# restore the initial controller state
# if it was disabled, keep it disabled
self._plot_area.controller.enabled = self._initial_controller_state

def _move_to_pointer(self, ev):
"""
Expand Down
12 changes: 8 additions & 4 deletions fastplotlib/graphics/selectors/_linear_region.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ def __init__(
resizable: bool = True,
fill_color=(0, 0, 0.35),
edge_color=(0.8, 0.8, 0),
edge_thickness: int = 3,
arrow_keys_modifier: str = "Shift",
name: str = None,
):
Expand All @@ -56,6 +57,9 @@ def __init__(
Holding the right mouse button while dragging an edge will force the entire region selector to move. This is
a when using transparent fill areas due to ``pygfx`` picking limitations.

**Note:** Events get very weird if the values of bounds, limits and origin are close to zero. If you need
a linear selector with small data, we recommend scaling the data and then using the selector.

Parameters
----------
bounds: (int, int)
Expand Down Expand Up @@ -168,7 +172,7 @@ def __init__(

left_line = pygfx.Line(
pygfx.Geometry(positions=left_line_data),
pygfx.LineMaterial(thickness=3, color=edge_color),
pygfx.LineMaterial(thickness=edge_thickness, color=edge_color),
)

# position data for the right edge line
Expand All @@ -181,7 +185,7 @@ def __init__(

right_line = pygfx.Line(
pygfx.Geometry(positions=right_line_data),
pygfx.LineMaterial(thickness=3, color=edge_color),
pygfx.LineMaterial(thickness=edge_thickness, color=edge_color),
)

self.edges: Tuple[pygfx.Line, pygfx.Line] = (left_line, right_line)
Expand All @@ -197,7 +201,7 @@ def __init__(

bottom_line = pygfx.Line(
pygfx.Geometry(positions=bottom_line_data),
pygfx.LineMaterial(thickness=3, color=edge_color),
pygfx.LineMaterial(thickness=edge_thickness, color=edge_color),
)

# position data for the right edge line
Expand All @@ -210,7 +214,7 @@ def __init__(

top_line = pygfx.Line(
pygfx.Geometry(positions=top_line_data),
pygfx.LineMaterial(thickness=3, color=edge_color),
pygfx.LineMaterial(thickness=edge_thickness, color=edge_color),
)

self.edges: Tuple[pygfx.Line, pygfx.Line] = (bottom_line, top_line)
Expand Down
85 changes: 84 additions & 1 deletion fastplotlib/layouts/_base.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from inspect import getfullargspec
from typing import *
import weakref
from warnings import warn

import numpy as np

Expand Down Expand Up @@ -92,6 +94,9 @@ def __init__(
self.viewport,
)

self._animate_funcs_pre = list()
self._animate_funcs_post = list()

self.renderer.add_event_handler(self.set_viewport_rect, "resize")

# list of hex id strings for all graphics managed by this PlotArea
Expand Down Expand Up @@ -224,12 +229,90 @@ def set_viewport_rect(self, *args):
self.viewport.rect = self.get_rect()

def render(self):
# does not flush
self._call_animate_functions(self._animate_funcs_pre)

# does not flush, flush must be implemented in user-facing Plot objects
self.viewport.render(self.scene, self.camera)

for child in self.children:
child.render()

self._call_animate_functions(self._animate_funcs_post)

def _call_animate_functions(self, funcs: Iterable[callable]):
for fn in funcs:
try:
args = getfullargspec(fn).args

if len(args) > 0:
if args[0] == "self" and not len(args) > 1:
fn()
else:
fn(self)
else:
fn()
except (ValueError, TypeError):
warn(
f"Could not resolve argspec of {self.__class__.__name__} animation function: {fn}, "
f"calling it without arguments."
)
fn()

def add_animations(
self,
*funcs: Iterable[callable],
pre_render: bool = True,
post_render: bool = False,
):
"""
Add function(s) that are called on every render cycle.
These are called at the Subplot level.

Parameters
----------
*funcs: callable or iterable of callable
function(s) that are called on each render cycle

pre_render: bool, default ``True``, optional keyword-only argument
if true, these function(s) are called before a render cycle

post_render: bool, default ``False``, optional keyword-only argument
if true, these function(s) are called after a render cycle

"""
for f in funcs:
if not callable(f):
raise TypeError(
f"all positional arguments to add_animations() must be callable types, you have passed a: {type(f)}"
)
if pre_render:
self._animate_funcs_pre += funcs
if post_render:
self._animate_funcs_post += funcs

def remove_animation(self, func):
"""
Removes the passed animation function from both pre and post render.

Parameters
----------
func: callable
The function to remove, raises a error if it's not registered as a pre or post animation function.

"""
if func not in self._animate_funcs_pre and func not in self._animate_funcs_post:
raise KeyError(
f"The passed function: {func} is not registered as an animation function. These are the animation "
f" functions that are currently registered:\n"
f"pre: {self._animate_funcs_pre}\n\npost: {self._animate_funcs_post}"
)

if func in self._animate_funcs_pre:
self._animate_funcs_pre.remove(func)

if func in self._animate_funcs_post:
self._animate_funcs_post.remove(func)

def add_graphic(self, graphic: Graphic, center: bool = True):
"""
Add a Graphic to the scene
Expand Down
86 changes: 0 additions & 86 deletions fastplotlib/layouts/_subplot.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
from typing import *
from inspect import getfullargspec
from warnings import warn

import numpy as np

Expand Down Expand Up @@ -97,9 +95,6 @@ def __init__(

self._grid: GridHelper = GridHelper(size=100, thickness=1)

self._animate_funcs_pre = list()
self._animate_funcs_post = list()

super(Subplot, self).__init__(
parent=parent,
position=position,
Expand Down Expand Up @@ -192,87 +187,6 @@ def get_rect(self):

return rect

def render(self):
self._call_animate_functions(self._animate_funcs_pre)

super(Subplot, self).render()

self._call_animate_functions(self._animate_funcs_post)

def _call_animate_functions(self, funcs: Iterable[callable]):
for fn in funcs:
try:
args = getfullargspec(fn).args

if len(args) > 0:
if args[0] == "self" and not len(args) > 1:
fn()
else:
fn(self)
else:
fn()
except (ValueError, TypeError):
warn(
f"Could not resolve argspec of {self.__class__.__name__} animation function: {fn}, "
f"calling it without arguments."
)
fn()

def add_animations(
self,
*funcs: Iterable[callable],
pre_render: bool = True,
post_render: bool = False,
):
"""
Add function(s) that are called on every render cycle.
These are called at the Subplot level.

Parameters
----------
*funcs: callable or iterable of callable
function(s) that are called on each render cycle

pre_render: bool, default ``True``, optional keyword-only argument
if true, these function(s) are called before a render cycle

post_render: bool, default ``False``, optional keyword-only argument
if true, these function(s) are called after a render cycle

"""
for f in funcs:
if not callable(f):
raise TypeError(
f"all positional arguments to add_animations() must be callable types, you have passed a: {type(f)}"
)
if pre_render:
self._animate_funcs_pre += funcs
if post_render:
self._animate_funcs_post += funcs

def remove_animation(self, func):
"""
Removes the passed animation function from both pre and post render.

Parameters
----------
func: callable
The function to remove, raises a error if it's not registered as a pre or post animation function.

"""
if func not in self._animate_funcs_pre and func not in self._animate_funcs_post:
raise KeyError(
f"The passed function: {func} is not registered as an animation function. These are the animation "
f" functions that are currently registered:\n"
f"pre: {self._animate_funcs_pre}\n\npost: {self._animate_funcs_post}"
)

if func in self._animate_funcs_pre:
self._animate_funcs_pre.remove(func)

if func in self._animate_funcs_post:
self._animate_funcs_post.remove(func)

def set_axes_visibility(self, visible: bool):
"""Toggles axes visibility."""
if visible:
Expand Down
Loading