widget_float.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. """Float class.
  2. Represents an unbounded float using a widget.
  3. """
  4. # Copyright (c) Jupyter Development Team.
  5. # Distributed under the terms of the Modified BSD License.
  6. from traitlets import (
  7. Instance, Unicode, CFloat, Bool, CaselessStrEnum, Tuple, TraitError, validate, default
  8. )
  9. from .widget_description import DescriptionWidget
  10. from .trait_types import InstanceDict, NumberFormat
  11. from .valuewidget import ValueWidget
  12. from .widget import register, widget_serialization
  13. from .widget_core import CoreWidget
  14. from .widget_int import ProgressStyle, SliderStyle
  15. class _Float(DescriptionWidget, ValueWidget, CoreWidget):
  16. value = CFloat(0.0, help="Float value").tag(sync=True)
  17. def __init__(self, value=None, **kwargs):
  18. if value is not None:
  19. kwargs['value'] = value
  20. super(_Float, self).__init__(**kwargs)
  21. class _BoundedFloat(_Float):
  22. max = CFloat(100.0, help="Max value").tag(sync=True)
  23. min = CFloat(0.0, help="Min value").tag(sync=True)
  24. @validate('value')
  25. def _validate_value(self, proposal):
  26. """Cap and floor value"""
  27. value = proposal['value']
  28. if self.min > value or self.max < value:
  29. value = min(max(value, self.min), self.max)
  30. return value
  31. @validate('min')
  32. def _validate_min(self, proposal):
  33. """Enforce min <= value <= max"""
  34. min = proposal['value']
  35. if min > self.max:
  36. raise TraitError('Setting min > max')
  37. if min > self.value:
  38. self.value = min
  39. return min
  40. @validate('max')
  41. def _validate_max(self, proposal):
  42. """Enforce min <= value <= max"""
  43. max = proposal['value']
  44. if max < self.min:
  45. raise TraitError('setting max < min')
  46. if max < self.value:
  47. self.value = max
  48. return max
  49. class _BoundedLogFloat(_Float):
  50. max = CFloat(4.0, help="Max value for the exponent").tag(sync=True)
  51. min = CFloat(0.0, help="Min value for the exponent").tag(sync=True)
  52. base = CFloat(10.0, help="Base of value").tag(sync=True)
  53. value = CFloat(1.0, help="Float value").tag(sync=True)
  54. @validate('value')
  55. def _validate_value(self, proposal):
  56. """Cap and floor value"""
  57. value = proposal['value']
  58. if self.base ** self.min > value or self.base ** self.max < value:
  59. value = min(max(value, self.base ** self.min), self.base ** self.max)
  60. return value
  61. @validate('min')
  62. def _validate_min(self, proposal):
  63. """Enforce base ** min <= value <= base ** max"""
  64. min = proposal['value']
  65. if min > self.max:
  66. raise TraitError('Setting min > max')
  67. if self.base ** min > self.value:
  68. self.value = self.base ** min
  69. return min
  70. @validate('max')
  71. def _validate_max(self, proposal):
  72. """Enforce base ** min <= value <= base ** max"""
  73. max = proposal['value']
  74. if max < self.min:
  75. raise TraitError('setting max < min')
  76. if self.base ** max < self.value:
  77. self.value = self.base ** max
  78. return max
  79. @register
  80. class FloatText(_Float):
  81. """ Displays a float value within a textbox. For a textbox in
  82. which the value must be within a specific range, use BoundedFloatText.
  83. Parameters
  84. ----------
  85. value : float
  86. value displayed
  87. step : float
  88. step of the increment (if None, any step is allowed)
  89. description : str
  90. description displayed next to the text box
  91. """
  92. _view_name = Unicode('FloatTextView').tag(sync=True)
  93. _model_name = Unicode('FloatTextModel').tag(sync=True)
  94. disabled = Bool(False, help="Enable or disable user changes").tag(sync=True)
  95. 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)
  96. step = CFloat(None, allow_none=True, help="Minimum step to increment the value").tag(sync=True)
  97. @register
  98. class BoundedFloatText(_BoundedFloat):
  99. """ Displays a float value within a textbox. Value must be within the range specified.
  100. For a textbox in which the value doesn't need to be within a specific range, use FloatText.
  101. Parameters
  102. ----------
  103. value : float
  104. value displayed
  105. min : float
  106. minimal value of the range of possible values displayed
  107. max : float
  108. maximal value of the range of possible values displayed
  109. step : float
  110. step of the increment (if None, any step is allowed)
  111. description : str
  112. description displayed next to the textbox
  113. """
  114. _view_name = Unicode('FloatTextView').tag(sync=True)
  115. _model_name = Unicode('BoundedFloatTextModel').tag(sync=True)
  116. disabled = Bool(False, help="Enable or disable user changes").tag(sync=True)
  117. 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)
  118. step = CFloat(None, allow_none=True, help="Minimum step to increment the value").tag(sync=True)
  119. @register
  120. class FloatSlider(_BoundedFloat):
  121. """ Slider/trackbar of floating values with the specified range.
  122. Parameters
  123. ----------
  124. value : float
  125. position of the slider
  126. min : float
  127. minimal position of the slider
  128. max : float
  129. maximal position of the slider
  130. step : float
  131. step of the trackbar
  132. description : str
  133. name of the slider
  134. orientation : {'horizontal', 'vertical'}
  135. default is 'horizontal', orientation of the slider
  136. readout : {True, False}
  137. default is True, display the current value of the slider next to it
  138. readout_format : str
  139. default is '.2f', specifier for the format function used to represent
  140. slider value for human consumption, modeled after Python 3's format
  141. specification mini-language (PEP 3101).
  142. """
  143. _view_name = Unicode('FloatSliderView').tag(sync=True)
  144. _model_name = Unicode('FloatSliderModel').tag(sync=True)
  145. step = CFloat(0.1, help="Minimum step to increment the value").tag(sync=True)
  146. orientation = CaselessStrEnum(values=['horizontal', 'vertical'],
  147. default_value='horizontal', help="Vertical or horizontal.").tag(sync=True)
  148. readout = Bool(True, help="Display the current value of the slider next to it.").tag(sync=True)
  149. readout_format = NumberFormat(
  150. '.2f', help="Format for the readout").tag(sync=True)
  151. continuous_update = Bool(True, help="Update the value of the widget as the user is holding the slider.").tag(sync=True)
  152. disabled = Bool(False, help="Enable or disable user changes").tag(sync=True)
  153. style = InstanceDict(SliderStyle).tag(sync=True, **widget_serialization)
  154. @register
  155. class FloatLogSlider(_BoundedLogFloat):
  156. """ Slider/trackbar of logarithmic floating values with the specified range.
  157. Parameters
  158. ----------
  159. value : float
  160. position of the slider
  161. base : float
  162. base of the logarithmic scale. Default is 10
  163. min : float
  164. minimal position of the slider in log scale, i.e., actual minimum is base ** min
  165. max : float
  166. maximal position of the slider in log scale, i.e., actual maximum is base ** max
  167. step : float
  168. step of the trackbar, denotes steps for the exponent, not the actual value
  169. description : str
  170. name of the slider
  171. orientation : {'horizontal', 'vertical'}
  172. default is 'horizontal', orientation of the slider
  173. readout : {True, False}
  174. default is True, display the current value of the slider next to it
  175. readout_format : str
  176. default is '.3g', specifier for the format function used to represent
  177. slider value for human consumption, modeled after Python 3's format
  178. specification mini-language (PEP 3101).
  179. """
  180. _view_name = Unicode('FloatLogSliderView').tag(sync=True)
  181. _model_name = Unicode('FloatLogSliderModel').tag(sync=True)
  182. step = CFloat(0.1, help="Minimum step in the exponent to increment the value").tag(sync=True)
  183. orientation = CaselessStrEnum(values=['horizontal', 'vertical'],
  184. default_value='horizontal', help="Vertical or horizontal.").tag(sync=True)
  185. readout = Bool(True, help="Display the current value of the slider next to it.").tag(sync=True)
  186. readout_format = NumberFormat(
  187. '.3g', help="Format for the readout").tag(sync=True)
  188. continuous_update = Bool(True, help="Update the value of the widget as the user is holding the slider.").tag(sync=True)
  189. disabled = Bool(False, help="Enable or disable user changes").tag(sync=True)
  190. base = CFloat(10., help="Base for the logarithm").tag(sync=True)
  191. style = InstanceDict(SliderStyle).tag(sync=True, **widget_serialization)
  192. @register
  193. class FloatProgress(_BoundedFloat):
  194. """ Displays a progress bar.
  195. Parameters
  196. -----------
  197. value : float
  198. position within the range of the progress bar
  199. min : float
  200. minimal position of the slider
  201. max : float
  202. maximal position of the slider
  203. description : str
  204. name of the progress bar
  205. orientation : {'horizontal', 'vertical'}
  206. default is 'horizontal', orientation of the progress bar
  207. bar_style: {'success', 'info', 'warning', 'danger', ''}
  208. color of the progress bar, default is '' (blue)
  209. colors are: 'success'-green, 'info'-light blue, 'warning'-orange, 'danger'-red
  210. """
  211. _view_name = Unicode('ProgressView').tag(sync=True)
  212. _model_name = Unicode('FloatProgressModel').tag(sync=True)
  213. orientation = CaselessStrEnum(values=['horizontal', 'vertical'],
  214. default_value='horizontal', help="Vertical or horizontal.").tag(sync=True)
  215. bar_style = CaselessStrEnum(
  216. values=['success', 'info', 'warning', 'danger', ''],
  217. default_value='', allow_none=True,
  218. help="Use a predefined styling for the progess bar.").tag(sync=True)
  219. style = InstanceDict(ProgressStyle).tag(sync=True, **widget_serialization)
  220. class _FloatRange(_Float):
  221. value = Tuple(CFloat(), CFloat(), default_value=(0.0, 1.0),
  222. help="Tuple of (lower, upper) bounds").tag(sync=True)
  223. @property
  224. def lower(self):
  225. return self.value[0]
  226. @lower.setter
  227. def lower(self, lower):
  228. self.value = (lower, self.value[1])
  229. @property
  230. def upper(self):
  231. return self.value[1]
  232. @upper.setter
  233. def upper(self, upper):
  234. self.value = (self.value[0], upper)
  235. @validate('value')
  236. def _validate_value(self, proposal):
  237. lower, upper = proposal['value']
  238. if upper < lower:
  239. raise TraitError('setting lower > upper')
  240. return lower, upper
  241. class _BoundedFloatRange(_FloatRange):
  242. step = CFloat(1.0, help="Minimum step that the value can take (ignored by some views)").tag(sync=True)
  243. max = CFloat(100.0, help="Max value").tag(sync=True)
  244. min = CFloat(0.0, help="Min value").tag(sync=True)
  245. def __init__(self, *args, **kwargs):
  246. min, max = kwargs.get('min', 0.0), kwargs.get('max', 100.0)
  247. if kwargs.get('value', None) is None:
  248. kwargs['value'] = (0.75 * min + 0.25 * max,
  249. 0.25 * min + 0.75 * max)
  250. super(_BoundedFloatRange, self).__init__(*args, **kwargs)
  251. @validate('min', 'max')
  252. def _validate_bounds(self, proposal):
  253. trait = proposal['trait']
  254. new = proposal['value']
  255. if trait.name == 'min' and new > self.max:
  256. raise TraitError('setting min > max')
  257. if trait.name == 'max' and new < self.min:
  258. raise TraitError('setting max < min')
  259. if trait.name == 'min':
  260. self.value = (max(new, self.value[0]), max(new, self.value[1]))
  261. if trait.name == 'max':
  262. self.value = (min(new, self.value[0]), min(new, self.value[1]))
  263. return new
  264. @validate('value')
  265. def _validate_value(self, proposal):
  266. lower, upper = super(_BoundedFloatRange, self)._validate_value(proposal)
  267. lower, upper = min(lower, self.max), min(upper, self.max)
  268. lower, upper = max(lower, self.min), max(upper, self.min)
  269. return lower, upper
  270. @register
  271. class FloatRangeSlider(_BoundedFloatRange):
  272. """ Slider/trackbar that represents a pair of floats bounded by minimum and maximum value.
  273. Parameters
  274. ----------
  275. value : float tuple
  276. range of the slider displayed
  277. min : float
  278. minimal position of the slider
  279. max : float
  280. maximal position of the slider
  281. step : float
  282. step of the trackbar
  283. description : str
  284. name of the slider
  285. orientation : {'horizontal', 'vertical'}
  286. default is 'horizontal'
  287. readout : {True, False}
  288. default is True, display the current value of the slider next to it
  289. readout_format : str
  290. default is '.2f', specifier for the format function used to represent
  291. slider value for human consumption, modeled after Python 3's format
  292. specification mini-language (PEP 3101).
  293. """
  294. _view_name = Unicode('FloatRangeSliderView').tag(sync=True)
  295. _model_name = Unicode('FloatRangeSliderModel').tag(sync=True)
  296. step = CFloat(0.1, help="Minimum step to increment the value").tag(sync=True)
  297. orientation = CaselessStrEnum(values=['horizontal', 'vertical'],
  298. default_value='horizontal', help="Vertical or horizontal.").tag(sync=True)
  299. readout = Bool(True, help="Display the current value of the slider next to it.").tag(sync=True)
  300. readout_format = NumberFormat(
  301. '.2f', help="Format for the readout").tag(sync=True)
  302. continuous_update = Bool(True, help="Update the value of the widget as the user is sliding the slider.").tag(sync=True)
  303. disabled = Bool(False, help="Enable or disable user changes").tag(sync=True)
  304. style = InstanceDict(SliderStyle).tag(sync=True, **widget_serialization)