holoviz / panel
1
"""
2
Defines Button and button-like widgets which allow triggering events
3
or merely toggling between on-off states.
4
"""
5 7
from functools import partial
6

7 7
import param
8

9 7
from bokeh.models import (
10
    Button as _BkButton, Toggle as _BkToggle, Dropdown as _BkDropdown
11
)
12

13 7
from bokeh.events import (MenuItemClick, ButtonClick)
14

15 7
from .base import Widget
16

17

18 7
BUTTON_TYPES = ['default', 'primary', 'success', 'warning', 'danger']
19

20 7
class _ButtonBase(Widget):
21

22 7
    button_type = param.ObjectSelector(default='default', objects=BUTTON_TYPES)
23

24 7
    _rename = {'name': 'label'}
25

26 7
    __abstract = True
27

28

29 7
class _ClickButton(_ButtonBase):
30

31 7
    __abstract = True
32

33 7
    _event = 'button_click'
34

35 7
    def _get_model(self, doc, root=None, parent=None, comm=None):
36 7
        model = super()._get_model(doc, root, parent, comm)
37 7
        ref = (root or model).ref['id']
38 7
        model.on_click(partial(self._server_click, doc, ref))
39 7
        return model
40

41 7
    def js_on_click(self, args={}, code=""):
42
        """
43
        Allows defining a JS callback to be triggered when the button
44
        is clicked.
45

46
        Arguments
47
        ----------
48
        args: dict
49
          A mapping of objects to make available to the JS callback
50
        code: str
51
          The Javascript code to execute when the button is clicked.
52

53
        Returns
54
        -------
55
        callback: Callback
56
          The Callback which can be used to disable the callback.
57
        """
58 7
        from ..links import Callback
59 7
        return Callback(self, code={'event:'+self._event: code}, args=args)
60

61 7
    def jscallback(self, args={}, **callbacks):
62
        """
63
        Allows defining a JS callback to be triggered when a property
64
        changes on the source object. The keyword arguments define the
65
        properties that trigger a callback and the JS code that gets
66
        executed.
67

68
        Arguments
69
        ----------
70
        args: dict
71
          A mapping of objects to make available to the JS callback
72
        **callbacks: dict
73
          A mapping between properties on the source model and the code
74
          to execute when that property changes
75

76
        Returns
77
        -------
78
        callback: Callback
79
          The Callback which can be used to disable the callback.
80
        """
81 7
        from ..links import Callback
82 7
        for k, v in list(callbacks.items()):
83 7
            if k == 'clicks':
84 7
                k = 'event:'+self._event
85 7
            callbacks[k] = self._rename.get(v, v)
86 7
        return Callback(self, code=callbacks, args=args)
87

88

89 7
class Button(_ClickButton):
90

91 7
    clicks = param.Integer(default=0)
92

93 7
    value = param.Event()
94

95 7
    _rename = {'clicks': None, 'name': 'label', 'value': None}
96

97 7
    _target_transforms = {'event:button_click': None, 'value': None}
98

99 7
    _widget_type = _BkButton
100

101 7
    @property
102 2
    def _linkable_params(self):
103 7
        return super()._linkable_params + ['value']
104

105 7
    def jslink(self, target, code=None, args=None, bidirectional=False, **links):
106 7
        links = {'event:'+self._event if p == 'value' else p: v for p, v in links.items()}
107 7
        super().jslink(target, code, args, bidirectional, **links)
108

109 7
    jslink.__doc__ = Widget.jslink.__doc__
110

111 7
    def _server_click(self, doc, ref, event):
112 7
        processing = bool(self._events)
113 7
        self._events.update({"clicks": self.clicks+1})
114 7
        self.param.trigger('value')
115 7
        if not processing:
116 7
            if doc.session_context:
117 0
                doc.add_timeout_callback(partial(self._change_coroutine, doc), self._debounce)
118
            else:
119 7
                self._change_event(doc)
120

121 7
    def _process_property_change(self, msg):
122 7
        msg = super()._process_property_change(msg)
123 7
        if 'clicks' in msg:
124 7
            msg['clicks'] = self.clicks + 1
125 7
        return msg
126

127 7
    def on_click(self, callback):
128 7
        self.param.watch(callback, 'clicks', onlychanged=False)
129

130

131 7
class Toggle(_ButtonBase):
132

133 7
    value = param.Boolean(default=False, doc="""
134
        Whether the button is currently toggled.""")
135

136 7
    _rename = {'value': 'active', 'name': 'label'}
137

138 7
    _supports_embed = True
139

140 7
    _widget_type = _BkToggle
141

142 7
    def _get_embed_state(self, root, values=None, max_opts=3):
143 0
        return (self, self._models[root.ref['id']][0], [False, True],
144
                lambda x: x.active, 'active', 'cb_obj.active')
145

146

147 7
class MenuButton(_ClickButton):
148

149 7
    clicked = param.String(default=None, doc="""
150
      Last menu item that was clicked.""")
151

152 7
    items = param.List(default=[], doc="""
153
      Menu items in the dropdown. Allows strings, tuples of the form
154
      (title, value) or Nones to separate groups of items.""")
155

156 7
    split = param.Boolean(default=False, doc="""
157
      Whether to add separate dropdown area to button.""")
158

159 7
    _widget_type = _BkDropdown
160

161 7
    _rename = {'name': 'label', 'items': 'menu', 'clicked': None}
162

163 7
    _event = 'menu_item_click'
164

165 7
    def on_click(self, callback):
166 0
        self.param.watch(callback, 'clicked', onlychanged=False)
167

168 7
    def _server_click(self, doc, ref, event):
169 0
        processing = bool(self._events)
170 0
        if isinstance(event, MenuItemClick):
171 0
            self._events.update({"clicked": event.item})
172 0
        elif isinstance(event, ButtonClick):
173 0
            self._events.update({"clicked": self.name})
174 0
        if not processing:
175 0
            if doc.session_context:
176 0
                doc.add_timeout_callback(partial(self._change_coroutine, doc), self._debounce)
177
            else:
178 0
                self._change_event(doc)

Read our documentation on viewing source code .

Loading