Skip to content

Commit

Permalink
Add spin counter to Icon
Browse files Browse the repository at this point in the history
Add a spin counter, which a bokeh-server script can listen to see
if the icon's spin state has updated. A script will then be able
to update the icon's spin state, receive a notification of the spin
update, and then perform some other actions, as in the added
example.
  • Loading branch information
azjps committed Aug 5, 2015
1 parent f592160 commit 0f593d2
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 2 deletions.
7 changes: 6 additions & 1 deletion bokeh/models/widgets/icons.py
Expand Up @@ -3,7 +3,7 @@
"""
from __future__ import absolute_import

from ...properties import Bool, Float, Enum
from ...properties import Bool, Float, Enum, Int
from ...enums import NamedIcon
from ..widget import Widget

Expand Down Expand Up @@ -35,3 +35,8 @@ class Icon(AbstractIcon):
Indicates a spinning (animated) icon. This value is ignored for
icons that do not support spinning.
""")

spin_updates = Int(0, help="""
This is a dummy field for generated a second callback if the spin state has
been updated.
""")
29 changes: 28 additions & 1 deletion bokehjs/src/coffee/widget/icon.coffee
Expand Up @@ -4,14 +4,19 @@ HasParent = require "../common/has_parent"

class IconView extends ContinuumView
tagName: "i"
events:
"change input": "change_input"

initialize: (options) ->
super(options)
@prev_spin_state = @mget("spin")
@render()
@listenTo(@model, 'change', @render)

render: () ->
# Reset modified html properties
@$el.empty()
@$el.removeClass()

@$el.addClass("bk-fa")
@$el.addClass("bk-fa-" + @mget("name"))
Expand All @@ -25,8 +30,29 @@ class IconView extends ContinuumView
if @mget("spin")
@$el.addClass("bk-fa-spin")

if @prev_spin_state != @mget("spin")
# This is a hack to add an additional request/response cycle when updating
# the icon's spin attribute. Suppose we want to load a large amount of data
# or perform a large computation, but want to notify the user that this is
# happening by adding a spin animation to this icon. Since the bokeh-server
# only provides a single transaction for the initial action, we can't first
# update the spin animation and then load the data. Instead, we only update
# the spin animation, send back that the spin_updates counter has incremented,
# and then have the downstream script listen to changes in spin_updates to
# perform its larger actions.
#
# Try for example: bokeh-server --script examples/app/spinning_icon/spin_app.py
@prev_spin_state = @mget("spin")
@change_input()

return @

change_input: () ->
# Increment counter of number of changes of spin
@mset('spin_updates', @mget('spin_updates') + 1)
@model.save()
@mget('callback')?.execute(@model)

class Icon extends HasParent
type: "Icon"
default_view: IconView
Expand All @@ -37,8 +63,9 @@ class Icon extends HasParent
size: null
flip: null
spin: false
spin_updates: 0
}

module.exports =
Model: Icon
View: IconView
View: IconView
67 changes: 67 additions & 0 deletions examples/app/spinning_icon/spin_app.py
@@ -0,0 +1,67 @@
"""
Demonstrate a simple app that in response to a button click,
updates an icon to spin and then does additional updates.
"""

from __future__ import print_function

import time

from bokeh.models import Plot
from bokeh.models.widgets import VBox, Icon, Button
from bokeh.plotting import figure, curdoc
from bokeh.properties import Instance
from bokeh.server.app import bokeh_app
from bokeh.server.utils.plugins import object_page
import numpy as np

class SpinApp(VBox):
extra_generated_classes = [["SpinApp", "SpinApp", "VBox"]]
jsmodel = "VBox"

icon = Instance(Icon)
button = Instance(Button)
plot = Instance(Plot)

@classmethod
def create(cls):
obj = cls()
obj.icon = Icon(name="refresh")
obj.button = Button(label="Load", type="primary", icon=obj.icon)
obj.plot = figure(title="random data")
obj.set_children()
return obj

def set_children(self):
self.children = [self.button, self.plot]

def setup_events(self):
if self.icon:
self.icon.on_change('spin_updates', self, 'on_spin_change')
if self.button:
self.button.on_change('clicks', self, 'on_button_click')

def on_button_click(self, obj, attrname, old, new):
self.icon.spin = True

def on_spin_change(self, obj, attrname, old, new):
"""On html spin update"""
print("SpinApp: Received spin update", attrname, old, new, self.icon.spin)
if self.icon.spin:
time.sleep(5)
self.load_plot()
self.icon.spin = False
self.set_children()
curdoc().add(self)

def load_plot(self):
p = figure(title="random data")
data_length = 100
p.circle(np.arange(data_length), np.random.rand(data_length), size=5)
self.plot = p

# The following code adds a "/bokeh/spin/" url to the bokeh-server.
@bokeh_app.route("/bokeh/spin/")
@object_page("spin")
def make_spin_app():
return SpinApp.create()

0 comments on commit 0f593d2

Please sign in to comment.