Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit ce5edd0

Browse filesBrowse files
authored
histogram lut widget (#344)
* move render and animation methods from Subplot to PlotBase * edge_thickness param for linear_region * histogram LUT widget basically works! * bidirectional vmin vmax with linear region and image graphic, start imagewidget integration * more hlut functionality, integrate in imagewidget * fix image widget example nb
1 parent 2cd6d52 commit ce5edd0
Copy full SHA for ce5edd0

File tree

7 files changed

+360
-179
lines changed
Filter options

7 files changed

+360
-179
lines changed

‎examples/notebooks/image_widget.ipynb

Copy file name to clipboardExpand all lines: examples/notebooks/image_widget.ipynb
+1-5Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@
4444
"source": [
4545
"iw = ImageWidget(\n",
4646
" data=a,\n",
47-
" vmin_vmax_sliders=True,\n",
4847
" cmap=\"viridis\"\n",
4948
")"
5049
]
@@ -113,7 +112,6 @@
113112
"iw = ImageWidget(\n",
114113
" data=a, \n",
115114
" slider_dims=[\"t\"],\n",
116-
" vmin_vmax_sliders=True,\n",
117115
" cmap=\"gnuplot2\"\n",
118116
")"
119117
]
@@ -247,7 +245,6 @@
247245
" data=data, \n",
248246
" slider_dims=[\"t\"], \n",
249247
" # dims_order=\"txy\", # you can set this manually if dim order is not the usual\n",
250-
" vmin_vmax_sliders=True,\n",
251248
" names=[\"zero\", \"one\", \"two\", \"three\"],\n",
252249
" window_funcs={\"t\": (np.mean, 5)},\n",
253250
" cmap=\"gnuplot2\", \n",
@@ -338,7 +335,6 @@
338335
" data=data, \n",
339336
" slider_dims=[\"t\", \"z\"], \n",
340337
" dims_order=\"xyzt\", # example of how you can set this for non-standard orders\n",
341-
" vmin_vmax_sliders=True,\n",
342338
" names=[\"zero\", \"one\", \"two\", \"three\"],\n",
343339
" # window_funcs={\"t\": (np.mean, 5)}, # window functions can be slow when indexing multiple dims\n",
344340
" cmap=\"gnuplot2\", \n",
@@ -402,7 +398,7 @@
402398
"name": "python",
403399
"nbconvert_exporter": "python",
404400
"pygments_lexer": "ipython3",
405-
"version": "3.11.3"
401+
"version": "3.11.2"
406402
}
407403
},
408404
"nbformat": 4,

‎fastplotlib/graphics/selectors/_base_selector.py

Copy file name to clipboardExpand all lines: fastplotlib/graphics/selectors/_base_selector.py
+11-2Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ def __init__(
8686
# sets to `True` on "pointer_down", sets to `False` on "pointer_up"
8787
self._moving = False #: indicates if the selector is currently being moved
8888

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

206+
self._initial_controller_state = self._plot_area.controller.enabled
207+
204208
def _move(self, ev):
205209
"""
206210
Called on pointer move events
@@ -235,15 +239,20 @@ def _move(self, ev):
235239
# update last position
236240
self._move_info.last_position = world_pos
237241

238-
self._plot_area.controller.enabled = True
242+
# restore the initial controller state
243+
# if it was disabled, keep it disabled
244+
self._plot_area.controller.enabled = self._initial_controller_state
239245

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

243249
def _move_end(self, ev):
244250
self._move_info = None
245251
self._moving = False
246-
self._plot_area.controller.enabled = True
252+
253+
# restore the initial controller state
254+
# if it was disabled, keep it disabled
255+
self._plot_area.controller.enabled = self._initial_controller_state
247256

248257
def _move_to_pointer(self, ev):
249258
"""

‎fastplotlib/graphics/selectors/_linear_region.py

Copy file name to clipboardExpand all lines: fastplotlib/graphics/selectors/_linear_region.py
+8-4Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ def __init__(
4444
resizable: bool = True,
4545
fill_color=(0, 0, 0.35),
4646
edge_color=(0.8, 0.8, 0),
47+
edge_thickness: int = 3,
4748
arrow_keys_modifier: str = "Shift",
4849
name: str = None,
4950
):
@@ -56,6 +57,9 @@ def __init__(
5657
Holding the right mouse button while dragging an edge will force the entire region selector to move. This is
5758
a when using transparent fill areas due to ``pygfx`` picking limitations.
5859
60+
**Note:** Events get very weird if the values of bounds, limits and origin are close to zero. If you need
61+
a linear selector with small data, we recommend scaling the data and then using the selector.
62+
5963
Parameters
6064
----------
6165
bounds: (int, int)
@@ -168,7 +172,7 @@ def __init__(
168172

169173
left_line = pygfx.Line(
170174
pygfx.Geometry(positions=left_line_data),
171-
pygfx.LineMaterial(thickness=3, color=edge_color),
175+
pygfx.LineMaterial(thickness=edge_thickness, color=edge_color),
172176
)
173177

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

182186
right_line = pygfx.Line(
183187
pygfx.Geometry(positions=right_line_data),
184-
pygfx.LineMaterial(thickness=3, color=edge_color),
188+
pygfx.LineMaterial(thickness=edge_thickness, color=edge_color),
185189
)
186190

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

198202
bottom_line = pygfx.Line(
199203
pygfx.Geometry(positions=bottom_line_data),
200-
pygfx.LineMaterial(thickness=3, color=edge_color),
204+
pygfx.LineMaterial(thickness=edge_thickness, color=edge_color),
201205
)
202206

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

211215
top_line = pygfx.Line(
212216
pygfx.Geometry(positions=top_line_data),
213-
pygfx.LineMaterial(thickness=3, color=edge_color),
217+
pygfx.LineMaterial(thickness=edge_thickness, color=edge_color),
214218
)
215219

216220
self.edges: Tuple[pygfx.Line, pygfx.Line] = (bottom_line, top_line)

‎fastplotlib/layouts/_base.py

Copy file name to clipboardExpand all lines: fastplotlib/layouts/_base.py
+84-1Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
from inspect import getfullargspec
12
from typing import *
23
import weakref
4+
from warnings import warn
35

46
import numpy as np
57

@@ -92,6 +94,9 @@ def __init__(
9294
self.viewport,
9395
)
9496

97+
self._animate_funcs_pre = list()
98+
self._animate_funcs_post = list()
99+
95100
self.renderer.add_event_handler(self.set_viewport_rect, "resize")
96101

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

226231
def render(self):
227-
# does not flush
232+
self._call_animate_functions(self._animate_funcs_pre)
233+
234+
# does not flush, flush must be implemented in user-facing Plot objects
228235
self.viewport.render(self.scene, self.camera)
229236

230237
for child in self.children:
231238
child.render()
232239

240+
self._call_animate_functions(self._animate_funcs_post)
241+
242+
def _call_animate_functions(self, funcs: Iterable[callable]):
243+
for fn in funcs:
244+
try:
245+
args = getfullargspec(fn).args
246+
247+
if len(args) > 0:
248+
if args[0] == "self" and not len(args) > 1:
249+
fn()
250+
else:
251+
fn(self)
252+
else:
253+
fn()
254+
except (ValueError, TypeError):
255+
warn(
256+
f"Could not resolve argspec of {self.__class__.__name__} animation function: {fn}, "
257+
f"calling it without arguments."
258+
)
259+
fn()
260+
261+
def add_animations(
262+
self,
263+
*funcs: Iterable[callable],
264+
pre_render: bool = True,
265+
post_render: bool = False,
266+
):
267+
"""
268+
Add function(s) that are called on every render cycle.
269+
These are called at the Subplot level.
270+
271+
Parameters
272+
----------
273+
*funcs: callable or iterable of callable
274+
function(s) that are called on each render cycle
275+
276+
pre_render: bool, default ``True``, optional keyword-only argument
277+
if true, these function(s) are called before a render cycle
278+
279+
post_render: bool, default ``False``, optional keyword-only argument
280+
if true, these function(s) are called after a render cycle
281+
282+
"""
283+
for f in funcs:
284+
if not callable(f):
285+
raise TypeError(
286+
f"all positional arguments to add_animations() must be callable types, you have passed a: {type(f)}"
287+
)
288+
if pre_render:
289+
self._animate_funcs_pre += funcs
290+
if post_render:
291+
self._animate_funcs_post += funcs
292+
293+
def remove_animation(self, func):
294+
"""
295+
Removes the passed animation function from both pre and post render.
296+
297+
Parameters
298+
----------
299+
func: callable
300+
The function to remove, raises a error if it's not registered as a pre or post animation function.
301+
302+
"""
303+
if func not in self._animate_funcs_pre and func not in self._animate_funcs_post:
304+
raise KeyError(
305+
f"The passed function: {func} is not registered as an animation function. These are the animation "
306+
f" functions that are currently registered:\n"
307+
f"pre: {self._animate_funcs_pre}\n\npost: {self._animate_funcs_post}"
308+
)
309+
310+
if func in self._animate_funcs_pre:
311+
self._animate_funcs_pre.remove(func)
312+
313+
if func in self._animate_funcs_post:
314+
self._animate_funcs_post.remove(func)
315+
233316
def add_graphic(self, graphic: Graphic, center: bool = True):
234317
"""
235318
Add a Graphic to the scene

‎fastplotlib/layouts/_subplot.py

Copy file name to clipboardExpand all lines: fastplotlib/layouts/_subplot.py
-86Lines changed: 0 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
from typing import *
2-
from inspect import getfullargspec
3-
from warnings import warn
42

53
import numpy as np
64

@@ -97,9 +95,6 @@ def __init__(
9795

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

100-
self._animate_funcs_pre = list()
101-
self._animate_funcs_post = list()
102-
10398
super(Subplot, self).__init__(
10499
parent=parent,
105100
position=position,
@@ -192,87 +187,6 @@ def get_rect(self):
192187

193188
return rect
194189

195-
def render(self):
196-
self._call_animate_functions(self._animate_funcs_pre)
197-
198-
super(Subplot, self).render()
199-
200-
self._call_animate_functions(self._animate_funcs_post)
201-
202-
def _call_animate_functions(self, funcs: Iterable[callable]):
203-
for fn in funcs:
204-
try:
205-
args = getfullargspec(fn).args
206-
207-
if len(args) > 0:
208-
if args[0] == "self" and not len(args) > 1:
209-
fn()
210-
else:
211-
fn(self)
212-
else:
213-
fn()
214-
except (ValueError, TypeError):
215-
warn(
216-
f"Could not resolve argspec of {self.__class__.__name__} animation function: {fn}, "
217-
f"calling it without arguments."
218-
)
219-
fn()
220-
221-
def add_animations(
222-
self,
223-
*funcs: Iterable[callable],
224-
pre_render: bool = True,
225-
post_render: bool = False,
226-
):
227-
"""
228-
Add function(s) that are called on every render cycle.
229-
These are called at the Subplot level.
230-
231-
Parameters
232-
----------
233-
*funcs: callable or iterable of callable
234-
function(s) that are called on each render cycle
235-
236-
pre_render: bool, default ``True``, optional keyword-only argument
237-
if true, these function(s) are called before a render cycle
238-
239-
post_render: bool, default ``False``, optional keyword-only argument
240-
if true, these function(s) are called after a render cycle
241-
242-
"""
243-
for f in funcs:
244-
if not callable(f):
245-
raise TypeError(
246-
f"all positional arguments to add_animations() must be callable types, you have passed a: {type(f)}"
247-
)
248-
if pre_render:
249-
self._animate_funcs_pre += funcs
250-
if post_render:
251-
self._animate_funcs_post += funcs
252-
253-
def remove_animation(self, func):
254-
"""
255-
Removes the passed animation function from both pre and post render.
256-
257-
Parameters
258-
----------
259-
func: callable
260-
The function to remove, raises a error if it's not registered as a pre or post animation function.
261-
262-
"""
263-
if func not in self._animate_funcs_pre and func not in self._animate_funcs_post:
264-
raise KeyError(
265-
f"The passed function: {func} is not registered as an animation function. These are the animation "
266-
f" functions that are currently registered:\n"
267-
f"pre: {self._animate_funcs_pre}\n\npost: {self._animate_funcs_post}"
268-
)
269-
270-
if func in self._animate_funcs_pre:
271-
self._animate_funcs_pre.remove(func)
272-
273-
if func in self._animate_funcs_post:
274-
self._animate_funcs_post.remove(func)
275-
276190
def set_axes_visibility(self, visible: bool):
277191
"""Toggles axes visibility."""
278192
if visible:

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.