widget_int.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. # Copyright (c) Jupyter Development Team.
  2. # Distributed under the terms of the Modified BSD License.
  3. """Int class.
  4. Represents an unbounded int using a widget.
  5. """
  6. from .widget_description import DescriptionWidget, DescriptionStyle
  7. from .valuewidget import ValueWidget
  8. from .widget import register, widget_serialization
  9. from .widget_core import CoreWidget
  10. from traitlets import Instance
  11. from .trait_types import Color, InstanceDict, NumberFormat
  12. from traitlets import (
  13. Unicode, CInt, Bool, CaselessStrEnum, Tuple, TraitError, default, validate
  14. )
  15. _int_doc_t = """
  16. Parameters
  17. ----------
  18. value: integer
  19. The initial value.
  20. """
  21. _bounded_int_doc_t = """
  22. Parameters
  23. ----------
  24. value: integer
  25. The initial value.
  26. min: integer
  27. The lower limit for the value.
  28. max: integer
  29. The upper limit for the value.
  30. step: integer
  31. The step between allowed values.
  32. """
  33. def _int_doc(cls):
  34. """Add int docstring template to class init."""
  35. def __init__(self, value=None, **kwargs):
  36. if value is not None:
  37. kwargs['value'] = value
  38. super(cls, self).__init__(**kwargs)
  39. __init__.__doc__ = _int_doc_t
  40. cls.__init__ = __init__
  41. return cls
  42. def _bounded_int_doc(cls):
  43. """Add bounded int docstring template to class init."""
  44. def __init__(self, value=None, min=None, max=None, step=None, **kwargs):
  45. if value is not None:
  46. kwargs['value'] = value
  47. if min is not None:
  48. kwargs['min'] = min
  49. if max is not None:
  50. kwargs['max'] = max
  51. if step is not None:
  52. kwargs['step'] = step
  53. super(cls, self).__init__(**kwargs)
  54. __init__.__doc__ = _bounded_int_doc_t
  55. cls.__init__ = __init__
  56. return cls
  57. class _Int(DescriptionWidget, ValueWidget, CoreWidget):
  58. """Base class for widgets that represent an integer."""
  59. value = CInt(0, help="Int value").tag(sync=True)
  60. def __init__(self, value=None, **kwargs):
  61. if value is not None:
  62. kwargs['value'] = value
  63. super(_Int, self).__init__(**kwargs)
  64. class _BoundedInt(_Int):
  65. """Base class for widgets that represent an integer bounded from above and below.
  66. """
  67. max = CInt(100, help="Max value").tag(sync=True)
  68. min = CInt(0, help="Min value").tag(sync=True)
  69. def __init__(self, value=None, min=None, max=None, step=None, **kwargs):
  70. if value is not None:
  71. kwargs['value'] = value
  72. if min is not None:
  73. kwargs['min'] = min
  74. if max is not None:
  75. kwargs['max'] = max
  76. if step is not None:
  77. kwargs['step'] = step
  78. super(_BoundedInt, self).__init__(**kwargs)
  79. @validate('value')
  80. def _validate_value(self, proposal):
  81. """Cap and floor value"""
  82. value = proposal['value']
  83. if self.min > value or self.max < value:
  84. value = min(max(value, self.min), self.max)
  85. return value
  86. @validate('min')
  87. def _validate_min(self, proposal):
  88. """Enforce min <= value <= max"""
  89. min = proposal['value']
  90. if min > self.max:
  91. raise TraitError('setting min > max')
  92. if min > self.value:
  93. self.value = min
  94. return min
  95. @validate('max')
  96. def _validate_max(self, proposal):
  97. """Enforce min <= value <= max"""
  98. max = proposal['value']
  99. if max < self.min:
  100. raise TraitError('setting max < min')
  101. if max < self.value:
  102. self.value = max
  103. return max
  104. @register
  105. @_int_doc
  106. class IntText(_Int):
  107. """Textbox widget that represents an integer."""
  108. _view_name = Unicode('IntTextView').tag(sync=True)
  109. _model_name = Unicode('IntTextModel').tag(sync=True)
  110. disabled = Bool(False, help="Enable or disable user changes").tag(sync=True)
  111. continuous_update = Bool(False, help="Update the value as the user types. If False, update on submission, e.g., pressing Enter or navigating away.").tag(sync=True)
  112. step = CInt(1, help="Minimum step to increment the value").tag(sync=True)
  113. @register
  114. @_bounded_int_doc
  115. class BoundedIntText(_BoundedInt):
  116. """Textbox widget that represents an integer bounded from above and below.
  117. """
  118. _view_name = Unicode('IntTextView').tag(sync=True)
  119. _model_name = Unicode('BoundedIntTextModel').tag(sync=True)
  120. disabled = Bool(False, help="Enable or disable user changes").tag(sync=True)
  121. continuous_update = Bool(False, help="Update the value as the user types. If False, update on submission, e.g., pressing Enter or navigating away.").tag(sync=True)
  122. step = CInt(1, help="Minimum step to increment the value").tag(sync=True)
  123. @register
  124. class SliderStyle(DescriptionStyle, CoreWidget):
  125. """Button style widget."""
  126. _model_name = Unicode('SliderStyleModel').tag(sync=True)
  127. handle_color = Color(None, allow_none=True, help="Color of the slider handle.").tag(sync=True)
  128. @register
  129. @_bounded_int_doc
  130. class IntSlider(_BoundedInt):
  131. """Slider widget that represents an integer bounded from above and below.
  132. """
  133. _view_name = Unicode('IntSliderView').tag(sync=True)
  134. _model_name = Unicode('IntSliderModel').tag(sync=True)
  135. step = CInt(1, help="Minimum step to increment the value").tag(sync=True)
  136. orientation = CaselessStrEnum(values=['horizontal', 'vertical'],
  137. default_value='horizontal', help="Vertical or horizontal.").tag(sync=True)
  138. readout = Bool(True, help="Display the current value of the slider next to it.").tag(sync=True)
  139. readout_format = NumberFormat(
  140. 'd', help="Format for the readout").tag(sync=True)
  141. continuous_update = Bool(True, help="Update the value of the widget as the user is holding the slider.").tag(sync=True)
  142. disabled = Bool(False, help="Enable or disable user changes").tag(sync=True)
  143. style = InstanceDict(SliderStyle).tag(sync=True, **widget_serialization)
  144. @register
  145. class ProgressStyle(DescriptionStyle, CoreWidget):
  146. """Button style widget."""
  147. _model_name = Unicode('ProgressStyleModel').tag(sync=True)
  148. bar_color = Color(None, allow_none=True, help="Color of the progress bar.").tag(sync=True)
  149. @register
  150. @_bounded_int_doc
  151. class IntProgress(_BoundedInt):
  152. """Progress bar that represents an integer bounded from above and below.
  153. """
  154. _view_name = Unicode('ProgressView').tag(sync=True)
  155. _model_name = Unicode('IntProgressModel').tag(sync=True)
  156. orientation = CaselessStrEnum(values=['horizontal', 'vertical'],
  157. default_value='horizontal', help="Vertical or horizontal.").tag(sync=True)
  158. bar_style = CaselessStrEnum(
  159. values=['success', 'info', 'warning', 'danger', ''], default_value='',
  160. help="""Use a predefined styling for the progess bar.""").tag(sync=True)
  161. style = InstanceDict(ProgressStyle).tag(sync=True, **widget_serialization)
  162. class _IntRange(_Int):
  163. value = Tuple(CInt(), CInt(), default_value=(0, 1),
  164. help="Tuple of (lower, upper) bounds").tag(sync=True)
  165. @property
  166. def lower(self):
  167. return self.value[0]
  168. @lower.setter
  169. def lower(self, lower):
  170. self.value = (lower, self.value[1])
  171. @property
  172. def upper(self):
  173. return self.value[1]
  174. @upper.setter
  175. def upper(self, upper):
  176. self.value = (self.value[0], upper)
  177. @validate('value')
  178. def _validate_value(self, proposal):
  179. lower, upper = proposal['value']
  180. if upper < lower:
  181. raise TraitError('setting lower > upper')
  182. return lower, upper
  183. @register
  184. class Play(_BoundedInt):
  185. """Play/repeat buttons to step through values automatically, and optionally loop.
  186. """
  187. interval = CInt(100, help="The maximum value for the play control.").tag(sync=True)
  188. step = CInt(1, help="Increment step").tag(sync=True)
  189. disabled = Bool(False, help="Enable or disable user changes").tag(sync=True)
  190. _view_name = Unicode('PlayView').tag(sync=True)
  191. _model_name = Unicode('PlayModel').tag(sync=True)
  192. _playing = Bool(help="Whether the control is currently playing.").tag(sync=True)
  193. _repeat = Bool(help="Whether the control will repeat in a continous loop.").tag(sync=True)
  194. show_repeat = Bool(True, help="Show the repeat toggle button in the widget.").tag(sync=True)
  195. class _BoundedIntRange(_IntRange):
  196. max = CInt(100, help="Max value").tag(sync=True)
  197. min = CInt(0, help="Min value").tag(sync=True)
  198. def __init__(self, *args, **kwargs):
  199. min, max = kwargs.get('min', 0), kwargs.get('max', 100)
  200. if not kwargs.get('value', None):
  201. kwargs['value'] = (0.75 * min + 0.25 * max,
  202. 0.25 * min + 0.75 * max)
  203. super(_BoundedIntRange, self).__init__(*args, **kwargs)
  204. @validate('min', 'max')
  205. def _validate_bounds(self, proposal):
  206. trait = proposal['trait']
  207. new = proposal['value']
  208. if trait.name == 'min' and new > self.max:
  209. raise TraitError('setting min > max')
  210. if trait.name == 'max' and new < self.min:
  211. raise TraitError('setting max < min')
  212. if trait.name == 'min':
  213. self.value = (max(new, self.value[0]), max(new, self.value[1]))
  214. if trait.name == 'max':
  215. self.value = (min(new, self.value[0]), min(new, self.value[1]))
  216. return new
  217. @validate('value')
  218. def _validate_value(self, proposal):
  219. lower, upper = super(_BoundedIntRange, self)._validate_value(proposal)
  220. lower, upper = min(lower, self.max), min(upper, self.max)
  221. lower, upper = max(lower, self.min), max(upper, self.min)
  222. return lower, upper
  223. @register
  224. class IntRangeSlider(_BoundedIntRange):
  225. """Slider/trackbar that represents a pair of ints bounded by minimum and maximum value.
  226. Parameters
  227. ----------
  228. value : int tuple
  229. The pair (`lower`, `upper`) of integers
  230. min : int
  231. The lowest allowed value for `lower`
  232. max : int
  233. The highest allowed value for `upper`
  234. """
  235. _view_name = Unicode('IntRangeSliderView').tag(sync=True)
  236. _model_name = Unicode('IntRangeSliderModel').tag(sync=True)
  237. step = CInt(1, help="Minimum step that the value can take").tag(sync=True)
  238. orientation = CaselessStrEnum(values=['horizontal', 'vertical'],
  239. default_value='horizontal', help="Vertical or horizontal.").tag(sync=True)
  240. readout = Bool(True, help="Display the current value of the slider next to it.").tag(sync=True)
  241. readout_format = NumberFormat(
  242. 'd', help="Format for the readout").tag(sync=True)
  243. continuous_update = Bool(True, help="Update the value of the widget as the user is sliding the slider.").tag(sync=True)
  244. style = InstanceDict(SliderStyle, help="Slider style customizations.").tag(sync=True, **widget_serialization)
  245. disabled = Bool(False, help="Enable or disable user changes").tag(sync=True)