16
16
from ..features ._base import FeatureEvent
17
17
18
18
19
- class SliderValueFeature (GraphicFeature ):
19
+ class LinearSelectionFeature (GraphicFeature ):
20
20
# A bit much to have a class for this but this allows it to integrate with the fastplotlib callback system
21
21
"""
22
- Manages the slider value and callbacks
22
+ Manages the slider selection and callbacks
23
23
24
24
**pick info**
25
25
26
26
================== ================================================================
27
- key value
27
+ key selection
28
28
================== ================================================================
29
- "new_data " the new slider position in world coordinates
29
+ "graphic " the selection graphic
30
30
"selected_index" the graphic data index that corresponds to the slider position
31
- "world_object" parent world object
31
+ "new_data" the new slider position in world coordinates
32
+ "delta" the delta vector of the graphic in NDC
32
33
================== ================================================================
33
34
34
35
"""
35
36
def __init__ (self , parent , axis : str , value : float ):
36
- super (SliderValueFeature , self ).__init__ (parent , data = value )
37
+ super (LinearSelectionFeature , self ).__init__ (parent , data = value )
37
38
38
39
self .axis = axis
39
40
@@ -55,13 +56,19 @@ def _feature_changed(self, key: Union[int, slice, Tuple[slice]], new_data: Any):
55
56
else :
56
57
g_ix = None
57
58
59
+ # get pygfx event and reset it
60
+ pygfx_ev = self ._parent ._pygfx_event
61
+ self ._parent ._pygfx_event = None
62
+
58
63
pick_info = {
59
64
"index" : None ,
60
65
"collection-index" : self ._collection_index ,
61
66
"world_object" : self ._parent .world_object ,
62
67
"new_data" : new_data ,
63
68
"selected_index" : g_ix ,
64
- "graphic" : self ._parent
69
+ "graphic" : self ._parent ,
70
+ "delta" : self ._parent .delta ,
71
+ "pygfx_event" : pygfx_ev
65
72
}
66
73
67
74
event_data = FeatureEvent (type = "slider" , pick_info = pick_info )
@@ -70,9 +77,11 @@ def _feature_changed(self, key: Union[int, slice, Tuple[slice]], new_data: Any):
70
77
71
78
72
79
class LinearSelector (Graphic ):
80
+ feature_events = ("selection" )
81
+
73
82
def __init__ (
74
83
self ,
75
- value : int ,
84
+ selection : int ,
76
85
limits : Tuple [int , int ],
77
86
axis : str = "x" ,
78
87
parent : Graphic = None ,
@@ -87,12 +96,12 @@ def __init__(
87
96
88
97
Parameters
89
98
----------
99
+ selection: int
100
+ initial x or y selected value for the slider
101
+
90
102
axis: str, default "x"
91
103
"x" | "y", the axis which the slider can move along
92
104
93
- origin: int
94
- the initial position of the slider, x or y value depending on "axis" argument
95
-
96
105
end_points: (int, int)
97
106
set length of slider by bounding it between two x-pos or two y-pos
98
107
@@ -103,23 +112,23 @@ def __init__(
103
112
thickness of the slider
104
113
105
114
color: Any, default "w"
106
- value to set the color of the slider
115
+ selection to set the color of the slider
107
116
108
117
name: str, optional
109
118
name of line slider
110
119
111
120
Features
112
121
--------
113
122
114
- value : :class:`SliderValueFeature `
115
- ``value ()`` returns the current slider position in world coordinates
116
- use ``value .add_event_handler()`` to add callback functions that are
117
- called when the LinearSelector value changes. See feaure class for event pick_info table
123
+ selection : :class:`LinearSelectionFeature `
124
+ ``selection ()`` returns the current slider position in world coordinates
125
+ use ``selection .add_event_handler()`` to add callback functions that are
126
+ called when the LinearSelector selection changes. See feaure class for event pick_info table
118
127
119
128
"""
120
129
121
130
self .limits = tuple (map (round , limits ))
122
- value = round (value )
131
+ selection = round (selection )
123
132
124
133
if axis == "x" :
125
134
xs = np .zeros (2 )
@@ -171,27 +180,31 @@ def __init__(
171
180
self ._set_world_object (world_object )
172
181
173
182
# set x or y position
174
- pos = getattr (self .position , axis )
175
- pos = value
183
+ if axis == "x" :
184
+ self .position .x = selection
185
+ else :
186
+ self .position .y = selection
176
187
177
- self .value = SliderValueFeature (self , axis = axis , value = value )
188
+ self .selection = LinearSelectionFeature (self , axis = axis , value = selection )
178
189
179
190
self .ipywidget_slider = ipywidget_slider
180
191
181
192
if self .ipywidget_slider is not None :
182
193
self ._setup_ipywidget_slider (ipywidget_slider )
183
194
184
195
self ._move_info : dict = None
196
+ self .delta = None
197
+ self ._pygfx_event = None
185
198
186
199
self .parent = parent
187
200
188
201
self ._block_ipywidget_call = False
189
202
190
203
def _setup_ipywidget_slider (self , widget ):
191
204
# setup ipywidget slider with callbacks to this LinearSelector
192
- widget .value = int (self .value ())
205
+ widget .value = int (self .selection ())
193
206
widget .observe (self ._ipywidget_callback , "value" )
194
- self .value .add_event_handler (self ._update_ipywidget )
207
+ self .selection .add_event_handler (self ._update_ipywidget )
195
208
self ._plot_area .renderer .add_event_handler (self ._set_slider_layout , "resize" )
196
209
197
210
def _update_ipywidget (self , ev ):
@@ -205,7 +218,7 @@ def _ipywidget_callback(self, change):
205
218
if self ._block_ipywidget_call :
206
219
return
207
220
208
- self .value = change ["new" ]
221
+ self .selection = change ["new" ]
209
222
210
223
def _set_slider_layout (self , * args ):
211
224
w , h = self ._plot_area .renderer .logical_size
@@ -240,7 +253,7 @@ def make_ipywidget_slider(self, kind: str = "IntSlider", **kwargs):
240
253
slider = cls (
241
254
min = self .limits [0 ],
242
255
max = self .limits [1 ],
243
- value = int (self .value ()),
256
+ value = int (self .selection ()),
244
257
step = 1 ,
245
258
** kwargs
246
259
)
@@ -274,7 +287,7 @@ def get_selected_index(self, graphic: Graphic = None) -> int:
274
287
to_search = graphic .data ()[:, 1 ]
275
288
offset = getattr (graphic .position , self .axis )
276
289
277
- find_value = self .value () - offset
290
+ find_value = self .selection () - offset
278
291
279
292
# get closest data index to the world space position of the slider
280
293
idx = np .searchsorted (to_search , find_value , side = "left" )
@@ -327,9 +340,9 @@ def _move_to_pointer(self, ev):
327
340
return
328
341
329
342
if self .axis == "x" :
330
- self .value = world_pos .x
343
+ self .selection = world_pos .x
331
344
else :
332
- self .value = world_pos .y
345
+ self .selection = world_pos .y
333
346
334
347
def _move_start (self , ev ):
335
348
self ._move_info = {"last_pos" : (ev .x , ev .y )}
@@ -346,13 +359,30 @@ def _move(self, ev):
346
359
# pointer move events are in viewport or canvas space
347
360
delta = Vector3 (ev .x - last [0 ], ev .y - last [1 ])
348
361
362
+ self ._pygfx_event = ev
363
+
364
+ self ._move_graphic (delta )
365
+
349
366
self ._move_info = {"last_pos" : (ev .x , ev .y )}
367
+ self ._plot_area .controller .enabled = True
368
+
369
+ def _move_graphic (self , delta : Vector3 ):
370
+ """
371
+ Moves the graphic, updates SelectionFeature
372
+
373
+ Parameters
374
+ ----------
375
+ delta_ndc: Vector3
376
+ the delta by which to move this Graphic, in screen coordinates
377
+
378
+ """
379
+ self .delta = delta .clone ()
350
380
351
381
viewport_size = self ._plot_area .viewport .logical_size
352
382
353
383
# convert delta to NDC coordinates using viewport size
354
384
# also since these are just deltas we don't have to calculate positions relative to the viewport
355
- delta_ndc = delta .multiply (
385
+ delta_ndc = delta .clone (). multiply (
356
386
Vector3 (
357
387
2 / viewport_size [0 ],
358
388
- 2 / viewport_size [1 ],
@@ -373,8 +403,8 @@ def _move(self, ev):
373
403
if new_value < self .limits [0 ] or new_value > self .limits [1 ]:
374
404
return
375
405
376
- self .value = new_value
377
- self ._plot_area . controller . enabled = True
406
+ self .selection = new_value
407
+ self .delta = None
378
408
379
409
def _move_end (self , ev ):
380
410
self ._move_info = None
0 commit comments