22from plotly .graph_objects import Figure
33from dash import Dash
44from dash .dependencies import Input , Output , State , ALL
5- from dash_core_components import Graph , Slider , Store
5+ from dash_core_components import Graph , Slider , Store , Interval
66
77from .utils import img_array_to_uri , get_thumbnail_size , shape3d_to_size2d
88
@@ -290,6 +290,9 @@ def _create_dash_components(self):
290290 self ._overlay_data = Store (id = self ._subid ("overlay" ), data = [])
291291 self ._img_traces = Store (id = self ._subid ("img-traces" ), data = [])
292292 self ._indicator_traces = Store (id = self ._subid ("indicator-traces" ), data = [])
293+ self ._interval = Interval (
294+ id = self ._subid ("interval" ), interval = 100 , disabled = True
295+ )
293296 self ._stores = [
294297 self ._info ,
295298 self ._position ,
@@ -300,6 +303,7 @@ def _create_dash_components(self):
300303 self ._overlay_data ,
301304 self ._img_traces ,
302305 self ._indicator_traces ,
306+ self ._interval ,
303307 ]
304308
305309 def _create_server_callbacks (self ):
@@ -378,9 +382,11 @@ def _create_client_callbacks(self):
378382 function update_position(index, info) {
379383 return info.origin[2] + index * info.spacing[2];
380384 }
381- """ ,
385+ """ .replace (
386+ "{{ID}}" , self ._context_id
387+ ),
382388 Output (self ._position .id , "data" ),
383- [Input (self ._slider .id , "value " )],
389+ [Input (self ._requested_index .id , "data " )],
384390 [State (self ._info .id , "data" )],
385391 )
386392
@@ -391,26 +397,61 @@ def _create_client_callbacks(self):
391397
392398 app .clientside_callback (
393399 """
394- function update_request(index) {
400+ function rate_limit_index(index, _, interval) {
401+ if (!window._slicer_{{ID}}) window._slicer_{{ID}} = {};
402+ let slicer_info = window._slicer_{{ID}};
403+ let now = window.performance.now();
395404
396- // Clear the cache?
397- if (!window.slicecache_for_{{ID}}) { window.slicecache_for_{{ID}} = {}; }
398- let slice_cache = window.slicecache_for_{{ID}};
405+ // Get whether the slider was moved
406+ let slider_was_moved = false;
407+ for (let trigger of dash_clientside.callback_context.triggered) {
408+ if (trigger.prop_id.indexOf('slider') >= 0) slider_was_moved = true;
409+ }
399410
400- // Request a new slice (or not)
401- let request_index = index;
402- if (slice_cache[index]) {
403- return window.dash_clientside.no_update;
404- } else {
405- console.log('requesting slice ' + index);
406- return index;
411+ // Initialize return values
412+ let req_index = dash_clientside.no_update;
413+ let disable_interval = false;
414+
415+ // If the slider moved, remember the time when this happened
416+ slicer_info.new_time = slicer_info.new_time || 0;
417+
418+ if (slider_was_moved) {
419+ slicer_info.new_time = now;
420+ }
421+
422+ // We can either update the rate-limited index interval ms after
423+ // the real index changed, or interval ms after it stopped
424+ // changing. The former makes the indicators come along while
425+ // dragging the slider, the latter is better for a smooth
426+ // experience, and the interval can be set much lower.
427+ if (index != slicer_info.req_index) {
428+ if (now - slicer_info.new_time >= interval) {
429+ req_index = slicer_info.req_index = index;
430+ disable_interval = true;
431+
432+ // Get cache
433+ // todo: _requested_index is now our rate-limited index, so we need to always apply
434+ //if (!window.slicecache_for_{{ID}}) { window.slicecache_for_{{ID}} = {}; }
435+ //let slice_cache = window.slicecache_for_{{ID}};
436+ //if (slice_cache[req_index]) {
437+ // req_index = dash_clientside.no_update;
438+ //} else {
439+ console.log('requesting slice ' + req_index);
440+ //}
441+ }
407442 }
443+
444+ return [req_index, disable_interval];
408445 }
409446 """ .replace (
410447 "{{ID}}" , self ._context_id
411448 ),
412- Output (self ._requested_index .id , "data" ),
413- [Input (self .slider .id , "value" )],
449+ [
450+ Output (self ._requested_index .id , "data" ),
451+ Output (self ._interval .id , "disabled" ),
452+ ],
453+ [Input (self ._slider .id , "value" ), Input (self ._interval .id , "n_intervals" )],
454+ [State (self ._interval .id , "interval" )],
414455 )
415456
416457 # ----------------------------------------------------------------------
@@ -473,7 +514,7 @@ def _create_client_callbacks(self):
473514 ),
474515 Output (self ._img_traces .id , "data" ),
475516 [
476- Input (self .slider .id , "value" ),
517+ Input (self ._slider .id , "value" ),
477518 Input (self ._request_data .id , "data" ),
478519 Input (self ._overlay_data .id , "data" ),
479520 ],
0 commit comments