-
Notifications
You must be signed in to change notification settings - Fork 482
Expand file tree
/
Copy pathmarks.py
More file actions
1879 lines (1636 loc) · 75.5 KB
/
Copy pathmarks.py
File metadata and controls
1879 lines (1636 loc) · 75.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# Copyright 2015 Bloomberg Finance L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
import json
from warnings import warn
import ipywidgets as widgets
from ipywidgets import (Widget, DOMWidget, CallbackDispatcher,
Color, widget_serialization)
from traitlets import (Int, Unicode, List, Enum, Dict, Bool, Float,
Instance, TraitError, validate)
from traittypes import Array
from numpy import histogram
import numpy as np
from bqscales import Scale, OrdinalScale, LinearScale
from .traits import (Date, array_serialization,
array_squeeze, array_dimension_bounds, array_supported_kinds)
from ._version import __frontend_version__
from .colorschemes import CATEGORY10
# Returns a decorator registering a mark class in the mark type registry.
# If no key is provided, the class name is used as a key. A key is provided
# for each core bqplot mark so that the frontend can use
# this key regardless of the kernel language.
def register_mark(key=None):
def wrap(mark):
name = key if key is not None else mark.__module__ + mark.__name__
Mark.mark_types[name] = mark
return mark
return wrap
# Shape constraint for array-types
def shape(*dimensions):
def validator(trait, value):
err_msg_tmpl = 'Expected an array of shape {} ' + \
'but got an array of shape {}'
if value.shape != dimensions:
raise TraitError(err_msg_tmpl.format(dimensions, value.shape))
else:
return value
return validator
class Mark(Widget):
"""The base mark class.
Traitlet mark attributes may be decorated with metadata.
!!! note "Data Attribute Decoration"
Data attributes are decorated with the following values:
- **scaled: bool**
Indicates whether the considered attribute is a data attribute which
must be associated with a scale in order to be taken into account.
- **rtype: string**
Range type of the associated scale.
- **atype: string**
Key in bqplot's axis registry of the recommended axis type to represent
this scale. When not specified, the default is 'bqplot.Axis'.
Style Attributes
----------------
Attributes
----------
display_name: string
Holds a user-friendly name for the trait attribute.
mark_types: dict (class-level attribute)
A registry of existing mark types.
scales: Dict of scales (default: {})
A dictionary of scales holding scales for each data attribute.
- If a mark holds a scaled attribute named 'x', the scales dictionary
must have a corresponding scale for the key 'x'.
- The scale's range type should be equal to the scaled attribute's
range type (rtype).
scales_metadata: Dict (default: {})
A dictionary of dictionaries holding metadata on the way scales are
used by the mark. For example, a linear scale may be used to count
pixels horizontally or vertically. The content of this dictionary
may change dynamically. It is an instance-level attribute.
preserve_domain: dict (default: {})
Indicates if this mark affects the domain(s) of the specified scale(s).
The keys of this dictionary are the same as the ones of the "scales"
attribute, and values are boolean. If a key is missing, it is
considered as False.
display_legend: bool (default: False)
Display toggle for the mark legend in the general figure legend
labels: list of unicode strings (default: [])
Labels of the items of the mark. This attribute has different meanings
depending on the type of mark.
apply_clip: bool (default: True)
Indicates whether the items that are beyond the limits of the chart
should be clipped.
visible: bool (default: True)
Visibility toggle for the mark.
selected_style: dict (default: {})
CSS style to be applied to selected items in the mark.
unselected_style: dict (default: {})
CSS style to be applied to items that are not selected in the mark,
when a selection exists.
selected: list of integers or None (default: None)
Indices of the selected items in the mark.
tooltip: DOMWidget or None (default: None)
Widget to be displayed as tooltip when elements of the scatter are
hovered on
tooltip_style: Dictionary (default: {'opacity': 0.9})
Styles to be applied to the tooltip widget
enable_hover: Bool (default: True)
Boolean attribute to control the hover interaction for the scatter. If
this is false, the on_hover custom mssg is not sent back to the python
side
interactions: Dictionary (default: {'hover': 'tooltip'})
Dictionary listing the different interactions for each mark. The key is
the event which triggers the interaction and the value is the kind of
interactions. Keys and values can only take strings from separate enums
for each mark.
tooltip_location : {'mouse', 'center'} (default: 'mouse')
Enum specifying the location of the tooltip. 'mouse' places the tooltip
at the location of the mouse when the tooltip is activated and 'center'
places the tooltip at the center of the figure. If tooltip is linked to
a click event, 'mouse' places the tooltip at the location of the click
that triggered the tooltip to be visible.
Methods
-------
Attributes
----------
on_hover(callback, remove=False)
Register a callback that will be triggered on hover.
on_click(callback, remove=False)
Register a callback that will be triggered on click.
on_legend_hover(callback, remove=False)
Register a callback that will be triggered on legend hover.
on_legend_click(callback, remove=False)
Register a callback that will be triggered on legend click.
on_element_click(callback, remove=False)
Register a callback that will be triggered on element click.
on_background_click(callback, remove=False)
Register a callback that will be triggered on background click.
"""
mark_types = {}
scales = Dict(value_trait=Instance(Scale)).tag(sync=True, **widget_serialization)
scales_metadata = Dict().tag(sync=True)
preserve_domain = Dict().tag(sync=True)
display_legend = Bool().tag(sync=True, display_name='Display legend')
labels = List(trait=Unicode()).tag(sync=True, display_name='Labels')
apply_clip = Bool(True).tag(sync=True)
visible = Bool(True).tag(sync=True)
selected_style = Dict().tag(sync=True)
unselected_style = Dict().tag(sync=True)
selected = Array(None, allow_none=True).tag(sync=True, **array_serialization)
enable_hover = Bool(True).tag(sync=True)
tooltip = Instance(DOMWidget, allow_none=True, default_value=None)\
.tag(sync=True, **widget_serialization)
tooltip_style = Dict({'opacity': 0.9}).tag(sync=True)
interactions = Dict({'hover': 'tooltip'}).tag(sync=True)
tooltip_location = Enum(['mouse', 'center'], default_value='mouse')\
.tag(sync=True)
_model_name = Unicode('MarkModel').tag(sync=True)
_model_module = Unicode('bqplot').tag(sync=True)
_view_module = Unicode('bqplot').tag(sync=True)
_view_module_version = Unicode(__frontend_version__).tag(sync=True)
_model_module_version = Unicode(__frontend_version__).tag(sync=True)
# We cannot display a marks outside of a figure
# for ipywidgets <=7
_ipython_display_ = None
# for ipywidgets >=8
def _repr_mimebundle_(self, **kwargs):
return {'text/plain': str(self)}
def _get_dimension_scales(self, dimension, preserve_domain=False):
"""
Return the list of scales corresponding to a given dimension.
The preserve_domain optional argument specifies whether one should
filter out the scales for which preserve_domain is set to True.
"""
if preserve_domain:
return [
self.scales[k] for k in self.scales if (
k in self.scales_metadata and
self.scales_metadata[k].get('dimension') == dimension and
not self.preserve_domain.get(k)
)
]
else:
return [
self.scales[k] for k in self.scales if (
k in self.scales_metadata and
self.scales_metadata[k].get('dimension') == dimension
)
]
@validate('scales')
def _validate_scales(self, proposal):
"""
Validates the `scales` based on the mark's scaled attributes metadata.
First checks for missing scale and then for 'rtype' compatibility.
"""
# Validate scales' 'rtype' versus data attribute 'rtype' decoration
# At this stage it is already validated that all values in self.scales
# are instances of Scale.
scales = proposal.value
for name in self.trait_names(scaled=True):
trait = self.traits()[name]
if name not in scales:
# Check for missing scale
if not trait.allow_none:
raise TraitError("Missing scale for data attribute %s." %
name)
else:
# Check scale range type compatibility
if scales[name].rtype != trait.metadata['rtype']:
raise TraitError("Range type mismatch for scale %s." %
name)
return scales
def __init__(self, **kwargs):
super(Mark, self).__init__(**kwargs)
self._hover_handlers = CallbackDispatcher()
self._click_handlers = CallbackDispatcher()
self._legend_click_handlers = CallbackDispatcher()
self._legend_hover_handlers = CallbackDispatcher()
self._element_click_handlers = CallbackDispatcher()
self._bg_click_handlers = CallbackDispatcher()
self._name_to_handler = {
'hover': self._hover_handlers,
'click': self._click_handlers,
'legend_click': self._legend_click_handlers,
'legend_hover': self._legend_hover_handlers,
'element_click': self._element_click_handlers,
'background_click': self._bg_click_handlers
}
self.on_msg(self._handle_custom_msgs)
def on_hover(self, callback, remove=False):
self._hover_handlers.register_callback(callback, remove=remove)
def on_click(self, callback, remove=False):
self._click_handlers.register_callback(callback, remove=remove)
def on_legend_click(self, callback, remove=False):
self._legend_click_handlers.register_callback(callback, remove=remove)
def on_legend_hover(self, callback, remove=False):
self._legend_hover_handlers.register_callback(callback, remove=remove)
def on_element_click(self, callback, remove=False):
self._element_click_handlers.register_callback(callback, remove=remove)
def on_background_click(self, callback, remove=False):
self._bg_click_handlers.register_callback(callback, remove=remove)
def _handle_custom_msgs(self, _, content, buffers=None):
try:
handler = self._name_to_handler[content['event']]
except KeyError:
return
handler(self, content)
@register_mark('bqplot.Lines')
class Lines(Mark):
"""Lines mark.
!!! warning
In the case of the Lines mark, scales for 'x' and 'y' **must** be provided.
Data Attributes
---------------
Attributes
----------
x: numpy.ndarray (default: [])
abscissas of the data points (1d or 2d array)
y: numpy.ndarray (default: [])
ordinates of the data points (1d or 2d array)
color: numpy.ndarray (default: None)
colors of the different lines based on data. If it is [], then the
colors from the colors attribute are used. Each line has a single color
and if the size of colors is less than the number of lines, the
remaining lines are given the default colors.
Style Attributes
----------------
Attributes
----------
icon: string (class-level attribute)
Font-awesome icon for the respective mark
name: string (class-level attribute)
User-friendly name of the mark
colors: list of colors (default: CATEGORY10)
List of colors of the Lines. If the list is shorter than the number
of lines, the colors are reused.
close_path: bool (default: False)
Whether to close the paths or not.
fill: {'none', 'bottom', 'top', 'inside', 'between'}
Fill in the area defined by the curves
fill_colors: list of colors (default: [])
Fill colors for the areas. Defaults to stroke-colors when no
color provided
opacities: list of floats (default: [])
Opacity for the lines and patches. Defaults to 1 when the list is too
short, or the element of the list is set to None.
fill_opacities: list of floats (default: [])
Opacity for the areas. Defaults to 1 when the list is too
short, or the element of the list is set to None.
stroke_width: float (default: 2)
Stroke width of the Lines
labels_visibility: {'none', 'label'}
Visibility of the curve labels
curves_subset: list of integers or None (default: [])
If set to None, all the lines are displayed. Otherwise, only the items
in the list will have full opacity, while others will be faded.
line_style: {'solid', 'dashed', 'dotted', 'dash_dotted'}
Line style.
interpolation: {'linear', 'basis', 'basis-open', 'basis-closed', 'bundle', 'cardinal', 'cardinal-open',
'cardinal-closed', 'monotone', 'step-before', 'step-after'}
Interpolation scheme used for interpolation between the data points
provided. Please refer to the svg interpolate documentation for details
about the different interpolation schemes.
marker: {'circle', 'cross', 'diamond', 'square', 'triangle-down', 'triangle-up', 'arrow', 'rectangle', 'ellipse', 'plus', 'crosshair', 'point'}
Marker shape
marker_size: nonnegative int (default: 64)
Default marker size in pixels
!!! note
- The fields which can be passed to the default tooltip are:
- **name**: label of the line
- **index**: index of the line being hovered on
- **color**: data attribute for the color of the line
- The following are the events which can trigger interactions:
- **click**: left click of the mouse
- **hover**: mouse-over an element
- The following are the interactions which can be linked to the above events:
- **tooltip**: display tooltip
"""
# Mark decoration
icon = 'fa-line-chart'
name = 'Lines'
# Scaled attributes
x = Array([]).tag(sync=True, scaled=True,
rtype='Number', atype='bqplot.Axis',
**array_serialization)\
.valid(array_squeeze, array_dimension_bounds(1, 2), array_supported_kinds())
y = Array([]).tag(sync=True, scaled=True,
rtype='Number', atype='bqplot.Axis',
**array_serialization)\
.valid(array_squeeze, array_dimension_bounds(1, 2), array_supported_kinds())
color = Array(None, allow_none=True).tag(sync=True,
scaled=True,
rtype='Color',
atype='bqplot.ColorAxis',
**array_serialization)\
.valid(array_squeeze, array_dimension_bounds(1, 1))
# Other attributes
scales_metadata = Dict({
'x': {'orientation': 'horizontal', 'dimension': 'x'},
'y': {'orientation': 'vertical', 'dimension': 'y'},
'color': {'dimension': 'color'}
}).tag(sync=True)
colors = List(trait=Color(default_value=None, allow_none=True),
default_value=CATEGORY10)\
.tag(sync=True, display_name='Colors')
fill_colors = List(trait=Color(default_value=None, allow_none=True))\
.tag(sync=True, display_name='Fill colors')
stroke_width = Float(2.0).tag(sync=True, display_name='Stroke width')
labels_visibility = Enum(['none', 'label'], default_value='none')\
.tag(sync=True, display_name='Labels visibility')
curves_subset = List().tag(sync=True)
line_style = Enum(['solid', 'dashed', 'dotted', 'dash_dotted'],
default_value='solid')\
.tag(sync=True, display_name='Line style')
# TODO: Only Lines have interpolation but we can extend for other types of graphs
interpolation = Enum(['linear', 'basis', 'basis-open',
'basis-closed', 'bundle',
'cardinal', 'cardinal-open',
'cardinal-closed', 'monotone', 'step-before',
'step-after'],
default_value='linear')\
.tag(sync=True, display_name='Interpolation')
close_path = Bool().tag(sync=True, display_name='Close path')
fill = Enum(['none', 'bottom', 'top', 'inside', 'between'],
default_value='none')\
.tag(sync=True, display_name='Fill')
marker = Enum(['circle', 'cross', 'diamond', 'square', 'triangle-down',
'triangle-up', 'arrow', 'rectangle', 'ellipse', 'plus',
'crosshair', 'point'],
default_value=None, allow_none=True)\
.tag(sync=True, display_name='Marker')
marker_size = Int(64).tag(sync=True, display_name='Default size')
opacities = List().tag(sync=True, display_name='Opacity')
fill_opacities = List().tag(sync=True, display_name='Fill Opacity')
_view_name = Unicode('Lines').tag(sync=True)
_model_name = Unicode('LinesModel').tag(sync=True)
@register_mark('bqplot.FlexLine')
class FlexLine(Mark):
"""Lines mark with the possibility to change the line width and color for each segment.
!!! warning
In the case of the FlexLines mark, scales for 'x' and 'y' **must** be provided.
Scales for the color and width data attributes are optional. In the case
where another data attribute than 'x' or 'y' is provided but the
corresponding scale is missing, the data attribute is ignored.
Data Attributes
---------------
Attributes
----------
x: numpy.ndarray (default: [])
abscissas of the data points (1d array)
y: numpy.ndarray (default: [])
ordinates of the data points (1d array)
color: numpy.ndarray or None (default: None)
Array controlling the color of the data points
width: numpy.ndarray or None (default: None)
Array controlling the widths of the Lines.
Style Attributes
----------------
Attributes
----------
name: string (class-level attributes)
user-friendly name of the mark
colors: list of colors (default: CATEGORY10)
List of colors for the Lines
stroke_width: float (default: 1.5)
Default stroke width of the Lines
"""
# Mark decoration
icon = 'fa-line-chart'
name = 'Flexible lines'
# Scaled attributes
x = Array([]).tag(sync=True, scaled=True, rtype='Number',
atype='bqplot.Axis',
**array_serialization)\
.valid(array_squeeze, array_dimension_bounds(1, 1))
y = Array([]).tag(sync=True, scaled=True,
rtype='Number',
atype='bqplot.Axis',
**array_serialization)\
.valid(array_squeeze, array_dimension_bounds(1, 1))
color = Array(None, allow_none=True)\
.tag(sync=True, scaled=True, rtype='Color',
atype='bqplot.ColorAxis',
**array_serialization).valid(array_squeeze)
width = Array(None, allow_none=True)\
.tag(sync=True, scaled=True, rtype='Number',
**array_serialization).valid(array_squeeze)
# Other attributes
scales_metadata = Dict({
'x': {'orientation': 'horizontal', 'dimension': 'x'},
'y': {'orientation': 'vertical', 'dimension': 'y'},
'color': {'dimension': 'color'}
}).tag(sync=True)
stroke_width = Float(1.5).tag(sync=True, display_name='Stroke width')
colors = List(trait=Color(default_value=None, allow_none=True),
default_value=CATEGORY10).tag(sync=True)
_view_name = Unicode('FlexLine').tag(sync=True)
_model_name = Unicode('FlexLineModel').tag(sync=True)
class _ScatterBase(Mark):
"""
Base Mark for Label and Scatter
"""
# Scaled attributes
x = Array([], allow_none=True).tag(sync=True, scaled=True,
rtype='Number',
atype='bqplot.Axis',
**array_serialization)\
.valid(array_dimension_bounds(1, 1))
y = Array([], allow_none=True).tag(sync=True, scaled=True,
rtype='Number',
atype='bqplot.Axis',
**array_serialization)\
.valid(array_dimension_bounds(1, 1))
color = Array(None, allow_none=True).tag(sync=True,
scaled=True,
rtype='Color',
atype='bqplot.ColorAxis',
**array_serialization)\
.valid(array_squeeze, array_dimension_bounds(1, 1))
opacity = Array(None, allow_none=True).tag(sync=True,
scaled=True,
rtype='Number',
**array_serialization)\
.valid(array_squeeze, array_dimension_bounds(1, 1))
size = Array(None, allow_none=True).tag(sync=True, scaled=True,
rtype='Number',
**array_serialization)\
.valid(array_squeeze, array_dimension_bounds(1, 1))
rotation = Array(None, allow_none=True).tag(sync=True, scaled=True,
rtype='Number',
**array_serialization)\
.valid(array_squeeze, array_dimension_bounds(1, 1))
# Other attributes
scales_metadata = Dict({
'x': {'orientation': 'horizontal', 'dimension': 'x'},
'y': {'orientation': 'vertical', 'dimension': 'y'},
'color': {'dimension': 'color'},
'size': {'dimension': 'size'},
'opacity': {'dimension': 'opacity'},
'rotation': {'dimension': 'rotation'}
}).tag(sync=True)
opacities = Array([1.0])\
.tag(sync=True, display_name='Opacities', **array_serialization)\
.valid(array_squeeze, array_dimension_bounds(1, 1))
hovered_style = Dict().tag(sync=True)
unhovered_style = Dict().tag(sync=True)
hovered_point = Int(None, allow_none=True).tag(sync=True)
enable_move = Bool().tag(sync=True)
enable_delete = Bool().tag(sync=True)
restrict_x = Bool().tag(sync=True)
restrict_y = Bool().tag(sync=True)
update_on_move = Bool().tag(sync=True)
def __init__(self, **kwargs):
self._drag_start_handlers = CallbackDispatcher()
self._drag_handlers = CallbackDispatcher()
self._drag_end_handlers = CallbackDispatcher()
super(_ScatterBase, self).__init__(**kwargs)
self._name_to_handler.update({
'drag_start': self._drag_start_handlers,
'drag_end': self._drag_end_handlers,
'drag': self._drag_handlers
})
def on_drag_start(self, callback, remove=False):
self._drag_start_handlers.register_callback(callback, remove=remove)
def on_drag(self, callback, remove=False):
self._drag_handlers.register_callback(callback, remove=remove)
def on_drag_end(self, callback, remove=False):
self._drag_end_handlers.register_callback(callback, remove=remove)
@register_mark('bqplot.Scatter')
class Scatter(_ScatterBase):
"""Scatter mark.
!!! warning
In the case of the Scatter mark, scales for 'x' and 'y' **must** be provided.
The scales of other data attributes are optional. In the case where another
data attribute than 'x' or 'y' is provided but the corresponding scale is
missing, the data attribute is ignored.
Data Attributes
---------------
Attributes
----------
x: numpy.ndarray (default: [])
abscissas of the data points (1d array)
y: numpy.ndarray (default: [])
ordinates of the data points (1d array)
color: numpy.ndarray or None (default: None)
color of the data points (1d array). Defaults to default_color when not
provided or when a value is NaN
opacity: numpy.ndarray or None (default: None)
opacity of the data points (1d array). Defaults to default_opacity when
not provided or when a value is NaN
size: numpy.ndarray or None (default: None)
size of the data points. Defaults to default_size when not provided or
when a value is NaN
skew: numpy.ndarray or None (default: None)
skewness of the markers representing the data points. Defaults to
default_skew when not provided or when a value is NaN
rotation: numpy.ndarray or None (default: None)
orientation of the markers representing the data points.
The rotation scale's range is [0, 180]
Defaults to 0 when not provided or when a value is NaN.
Style Attributes
----------------
Attributes
----------
icon: string (class-level attribute)
Font-awesome icon for that mark
name: string (class-level attribute)
User-friendly name of the mark
marker: {'circle', 'cross', 'diamond', 'square', 'triangle-down', 'triangle-up', 'arrow', 'rectangle', 'ellipse', 'plus', 'crosshair', 'point'}
Marker shape
colors: list of colors (default: ['steelblue'])
List of colors of the markers. If the list is shorter than the number
of points, the colors are reused.
default_colors: Deprecated
Same as `colors`, deprecated as of version 0.8.4
fill: Bool (default: True)
Whether to fill the markers or not
stroke: Color or None (default: None)
Stroke color of the marker
stroke_width: Float (default: 1.5)
Stroke width of the marker
opacities: list of floats (default: [1.0])
Default opacities of the markers. If the list is shorter than
the number
of points, the opacities are reused.
default_skew: float (default: 0.5)
Default skew of the marker.
This number is validated to be between 0 and 1.
default_size: nonnegative int (default: 64)
Default marker size in pixel.
If size data is provided with a scale, default_size stands for the
maximal marker size (i.e. the maximum value for the 'size' scale range)
drag_size: nonnegative float (default: 5.)
Ratio of the size of the dragged scatter size to the default
scatter size.
names: numpy.ndarray (default: None)
Labels for the points of the chart
display_names: bool (default: True)
Controls whether names are displayed for points in the scatter
label_display_horizontal_offset: float (default: None)
Adds an offset, in pixels, to the horizontal positioning of the 'names'
label above each data point
label_display_vertical_offset: float (default: None)
Adds an offset, in pixels, to the vertical positioning of the 'names'
label above each data point
enable_move: bool (default: False)
Controls whether points can be moved by dragging. Refer to restrict_x,
restrict_y for more options.
restrict_x: bool (default: False)
Restricts movement of the point to only along the x axis. This is valid
only when enable_move is set to True. If both restrict_x and restrict_y
are set to True, the point cannot be moved.
restrict_y: bool (default: False)
Restricts movement of the point to only along the y axis. This is valid
only when enable_move is set to True. If both restrict_x and restrict_y
are set to True, the point cannot be moved.
!!! Note
- The fields which can be passed to the default tooltip are:
- All the data attributes (x, y, color, opacity, size, skew, rotation)
- **index**: index of the marker being hovered on
- The following are the events which can trigger interactions:
- **click**: left click of the mouse
- **hover**: mouse-over an element
- The following are the interactions which can be linked to the above events:
- **tooltip**: display tooltip
- **add**: add new points to the scatter (can only linked to click)
"""
# Mark decoration
icon = 'fa-cloud'
name = 'Scatter'
# Scaled attributes
skew = Array(None, allow_none=True).tag(sync=True, scaled=True,
rtype='Number',
**array_serialization)\
.valid(array_squeeze, array_dimension_bounds(1, 1))
# Other attributes
marker = Enum(['circle', 'cross', 'diamond', 'square', 'triangle-down',
'triangle-up', 'arrow', 'rectangle', 'ellipse', 'plus',
'crosshair', 'point'],
default_value='circle').tag(sync=True, display_name='Marker')
colors = List(trait=Color(default_value=None, allow_none=True),
default_value=['steelblue'])\
.tag(sync=True, display_name='Colors')
scales_metadata = Dict({
'x': {'orientation': 'horizontal', 'dimension': 'x'},
'y': {'orientation': 'vertical', 'dimension': 'y'},
'color': {'dimension': 'color'},
'size': {'dimension': 'size'},
'opacity': {'dimension': 'opacity'},
'rotation': {'dimension': 'rotation'},
'skew': {'dimension': 'skew'}
}).tag(sync=True)
@property
def default_colors(self):
return self.colors
@default_colors.setter
def default_colors(self, value):
warn("default_colors is deprecated, use colors instead.",
DeprecationWarning)
self.colors = value
@property
def default_opacities(self):
return self.opacities
@default_opacities.setter
def default_opacities(self, value):
warn("default_opacities is deprecated, use opacities instead.",
DeprecationWarning)
self.opacities = value
stroke = Color(None, allow_none=True).tag(sync=True,
display_name='Stroke color')
stroke_width = Float(1.5).tag(sync=True, display_name='Stroke width')
default_skew = Float(0.5, min=0, max=1).tag(sync=True)
default_size = Int(64).tag(sync=True, display_name='Default size')
names = Array(None, allow_none=True)\
.tag(sync=True, **array_serialization).valid(array_squeeze)
display_names = Bool(True).tag(sync=True, display_name='Display names')
label_display_horizontal_offset = Float(allow_none=True).tag(sync=True)
label_display_vertical_offset = Float(allow_none=True).tag(sync=True)
fill = Bool(True).tag(sync=True)
drag_color = Color(None, allow_none=True).tag(sync=True)
drag_size = Float(5.).tag(sync=True)
names_unique = Bool(True).tag(sync=True)
_view_name = Unicode('Scatter').tag(sync=True)
_model_name = Unicode('ScatterModel').tag(sync=True)
@register_mark('bqplot.Label')
class Label(_ScatterBase):
"""Label mark.
Data Attributes
---------------
Attributes
----------
x: numpy.ndarray (default: [])
horizontal position of the labels, in data coordinates or in
figure coordinates
y: numpy.ndarray (default: [])
vertical position of the labels, in data coordinates or in
figure coordinates
color: numpy.ndarray or None (default: None)
label colors
size: numpy.ndarray or None (default: None)
label sizes
rotation: numpy.ndarray or None (default: None)
label rotations
opacity: numpy.ndarray or None (default: None)
label opacities
Style Attributes
----------------
Attributes
----------
x_offset: int (default: 0)
horizontal offset in pixels from the stated x location
y_offset: int (default: 0)
vertical offset in pixels from the stated y location
text: string (default: '')
text to be displayed
default_size: string (default: '14px')
font size in px, em or ex
font_weight: {'bold', 'normal', 'bolder'}
font weight of the caption
drag_size: nonnegative float (default: 1.)
Ratio of the size of the dragged label font size to the default
label font size.
align: {'start', 'middle', 'end'}
alignment of the text with respect to the provided location
enable_move: Bool (default: False)
Enable the label to be moved by dragging. Refer to restrict_x,
restrict_y for more options.
restrict_x: bool (default: False)
Restricts movement of the label to only along the x axis. This is valid
only when enable_move is set to True. If both restrict_x and restrict_y
are set to True, the label cannot be moved.
restrict_y: bool (default: False)
Restricts movement of the label to only along the y axis. This is valid
only when enable_move is set to True. If both restrict_x and restrict_y
are set to True, the label cannot be moved.
"""
# Mark decoration
icon = 'fa-font'
name = 'Labels'
# Other attributes
x_offset = Int(0).tag(sync=True)
y_offset = Int(0).tag(sync=True)
colors = List(trait=Color(default_value=None,
allow_none=True),
default_value=CATEGORY10)\
.tag(sync=True, display_name='Colors')
rotate_angle = Float(0.0).tag(sync=True)
text = Array(None, allow_none=True)\
.tag(sync=True, **array_serialization).valid(array_squeeze)
default_size = Float(16.).tag(sync=True)
drag_size = Float(1.).tag(sync=True)
font_unit = Enum(['px', 'em', 'pt', '%'],
default_value='px').tag(sync=True)
font_weight = Enum(['bold', 'normal', 'bolder'],
default_value='bold').tag(sync=True)
align = Enum(['start', 'middle', 'end'],
default_value='start').tag(sync=True)
_view_name = Unicode('Label').tag(sync=True)
_model_name = Unicode('LabelModel').tag(sync=True)
@register_mark('bqplot.Hist')
class Hist(Mark):
"""Histogram mark.
!!! warning
In the case of the Hist mark, scales for 'sample' and 'count' **must** be provided.
Data Attributes
---------------
Attributes
----------
sample: numpy.ndarray (default: [])
sample of which the histogram must be computed.
count: numpy.ndarray (read-only)
number of sample points per bin. It is a read-only attribute.
Style Attributes
----------------
Attributes
----------
icon: string (class-level attribute)
font-awesome icon for that mark
name: string (class-level attribute)
user-friendly name of the mark
bins: nonnegative int (default: 10)
number of bins in the histogram
normalized: bool (default: False)
Boolean attribute to return normalized values which
sum to 1 or direct counts for the `count` attribute. The scale of
`count` attribute is determined by the value of this flag.
colors: list of colors (default: ['steelblue'])
List of colors of the Histogram. If the list is shorter than the number
of bins, the colors are reused.
stroke: Color or None (default: None)
Stroke color of the histogram
opacities: list of floats (default: [])
Opacity for the bins of the histogram. Defaults to 1 when the list
is too short, or the element of the list is set to None.
midpoints: list (default: [])
midpoints of the bins of the histogram. It is a read-only attribute.
!!! Note
- The fields which can be passed to the default tooltip are:
- **midpoint**: mid-point of the bin related to the rectangle hovered on
- **count**: number of elements in the bin hovered on
- **bin_start**: start point of the bin
- **bin_end**: end point of the bin
- **index**: index of the bin
"""
# Mark decoration
icon = 'fa-signal'
name = 'Histogram'
# Scaled attributes
sample = Array([]).tag(sync=True, display_name='Sample',
scaled=True, rtype='Number',
atype='bqplot.Axis',
**array_serialization)\
.valid(array_squeeze, array_dimension_bounds(1, 1))
count = Array([], read_only=True).tag(sync=True,
display_name='Count',
scaled=True,
rtype='Number',
atype='bqplot.Axis',
**array_serialization)\
.valid(array_squeeze)
normalized = Bool().tag(sync=True)
# Other attributes
scales_metadata = Dict({
'sample': {'orientation': 'horizontal', 'dimension': 'x'},
'count': {'orientation': 'vertical', 'dimension': 'y'}
}).tag(sync=True)
bins = Int(10).tag(sync=True, display_name='Number of bins')
midpoints = List(read_only=True).tag(sync=True, display_name='Mid points')
# midpoints is a read-only attribute that is set when the mark is drawn
colors = List(trait=Color(default_value=None, allow_none=True),
default_value=['steelblue'])\
.tag(sync=True, display_name='Colors')
stroke = Color(None, allow_none=True).tag(sync=True)
opacities = List(trait=Float(1.0, min=0, max=1, allow_none=True))\
.tag(sync=True, display_name='Opacities')
_view_name = Unicode('Hist').tag(sync=True)
_model_name = Unicode('HistModel').tag(sync=True)
@register_mark('bqplot.Boxplot')
class Boxplot(Mark):
"""Boxplot marks.
Data Attributes
---------------
Attributes
----------
: numpy.ndarray (default: [])
abscissas of the data points (1d array)
y: numpy.ndarray (default: [[]])
Sample data points (2d array)
Style Attributes
----------------
Attributes
----------
stroke: Color or None
stroke color of the marker
color: Color
fill color of the box
opacities: list of floats (default: [])
Opacities for the markers of the boxplot. Defaults to 1 when the
list is too short, or the element of the list is set to None.
outlier-color: color
color for the outlier
box_width: int (default: None)
width of the box in pixels. The minimum value is 5.
If set to None, box_with is auto calculated
auto_detect_outliers: bool (default: True)
Flag to toggle outlier auto-detection
"""
# Mark decoration
icon = 'fa-birthday-cake'
name = 'Boxplot chart'
# Scaled attributes
x = Array([]).tag(sync=True, scaled=True, rtype='Number',
atype='bqplot.Axis', **array_serialization)\
.valid(array_squeeze, array_dimension_bounds(1, 1))
# Second dimension must contain OHLC data, otherwise the behavior
# is undefined.
y = Array([[]]).tag(sync=True, scaled=True, rtype='Number',
atype='bqplot.Axis', **array_serialization)\
.valid(array_dimension_bounds(1, 2), array_supported_kinds())
# Other attributes
scales_metadata = Dict({
'x': {'orientation': 'horizontal', 'dimension': 'x'},
'y': {'orientation': 'vertical', 'dimension': 'y'}
}).tag(sync=True)
stroke = Color(None, allow_none=True)\
.tag(sync=True, display_name='Stroke color')
box_fill_color = Color('steelblue')\
.tag(sync=True, display_name='Fill color for the box')
outlier_fill_color = Color('gray').tag(sync=True,
display_name='Outlier fill color')
opacities = List(trait=Float(1.0, min=0, max=1, allow_none=True))\
.tag(sync=True, display_name='Opacities')
box_width = Int(None, min=5, allow_none=True).tag(sync=True, display_name='Box Width')
auto_detect_outliers = Bool(True).tag(sync=True, display_name='Auto-detect Outliers')
_view_name = Unicode('Boxplot').tag(sync=True)
_model_name = Unicode('BoxplotModel').tag(sync=True)