Popups

Ipyvolume has the option to show a popup widgets when hovering above a mark. When hovering, the widget will be shown near the mouse position, and it’s value attribute will be set to the index of the mark hovered above (e.g. when you have 12 points, value will be between 0 and 11). Also, the description will be set to the description of the scatter object. These two attributes are used in the ipywidget IntText and thus can be used as a popop widget:

[1]:
import ipyvolume as ipv
import ipywidgets as widgets
f = ipv.figure()
scatter = ipv.examples.gaussian(show=False, description="Blob")
scatter.popup = widgets.IntText()
ipv.show()

While sufficient, ipyvolume also comes with a custom dedicated Popup widget, build using the ipyvuetify library. This popup will also show a nice icon (see https://materialdesignicons.com/) and the color used.

[2]:
import ipyvolume as ipv
import ipywidgets as widgets
f = ipv.figure()
scatter = ipv.examples.gaussian(show=False,
                                description="Blob",
                                description_color="#CC0000",
                                icon='mdi-star-four-points')
scatter.popup = ipv.ui.Popup()
ipv.show()

Note that while hovering, the scatter attributes hovered (a boolean indicates you are hovering above a mark) and hovered_index, which mark you are hovering above, are set, and can be linked to other widgets.

[3]:
widget_hovered = widgets.Valid(description="Hovering", readout="-")
widget_hovered_index = widgets.Text(description="Hovered on")
widgets.jsdlink((scatter, 'hovered'), (widget_hovered, 'value'))
widgets.jsdlink((scatter, 'hovered_index'), (widget_hovered_index, 'value'))
widgets.HBox([widget_hovered, widget_hovered_index])
[4]:
# workaround for vaex, which has special behaviour on read the docs
import os
key = "READTHEDOCS"
if key in os.environ:
    del os.environ[key]
[5]:
import ipyvolume as ipv
import vaex.ml
[6]:
df = vaex.ml.datasets.load_iris()
df
[6]:
# sepal_length sepal_width petal_length petal_width class_
0 5.9 3.0 4.2 1.5 1
1 6.1 3.0 4.6 1.4 1
2 6.6 2.9 4.6 1.3 1
3 6.7 3.3 5.7 2.1 2
4 5.5 4.2 1.4 0.2 0
... ... ... ... ... ...
1455.2 3.4 1.4 0.2 0
1465.1 3.8 1.6 0.2 0
1475.8 2.6 4.0 1.2 1
1485.7 3.8 1.7 0.3 0
1496.2 2.9 4.3 1.3 1
[7]:
import ipywidgets as widgets
int_widget = widgets.IntText(description="index", value=2)
int_widget
[8]:
import traitlets

# Custom popup showing a url to wikipedia
class MyPopup(ipv.ui.Popup):
    # the event handler will fill this in
    template_file = None # disable the loading from file
    url = traitlets.Unicode('').tag(sync=True)
    @traitlets.default("template")
    def _default_template(self):
        return """
    <template>
    <div>
        <div :style="{padding: '4px', 'background-color': color, color: 'white'}">
            <v-icon color="white">{{icon}}</v-icon>
            Iris-{{description}}(#<i>{{value}}</i>) <span v-if="extra_html" v-html="extra_html"></span>
            <p>
                <a :href="url" target="_black" style="color: white">Visit wikipedia</a>
            </p>

            More information:
            <ul v-if="record" style="margin-top: 0">
                <li v-for="(value, name) in record">{{name}}={{value}}</li>
            </ul>
        </div>
    </div>
    </template>
"""
[9]:
popup = MyPopup()
classes = ["Setosa", "Versicolour", "Virginica"]
urls = {
    "Setosa": "https://en.wikipedia.org/wiki/Iris_setosa",
    "Versicolour": "https://en.wikipedia.org/wiki/Iris_versicolor",
    "Virginica": "https://en.wikipedia.org/wiki/Iris_virginica"
}

colors = ["red", "green", "blue"]
features = ['sepal_length', 'sepal_width', 'petal_length', 'petal_width']

x, y, z = features[:3]
ipv.figure()

for class_index, name in enumerate(classes):
    dfc = df[df.class_==class_index]
    color = colors[class_index]
    s = ipv.scatter(dfc[x].to_numpy(), dfc[y].to_numpy(), dfc[z].to_numpy(),
                    color=color, description=name, marker='sphere')
    s.popup = popup
    def set_extra(index, class_index=class_index, name=name):
        dfc = df[df.class_==class_index]
        records = dfc[features].to_records()
        popup.record = records[index]
        popup.url = urls[name]
    set_extra(0)
    s.observe(set_extra, "hovered")
ipv.show()
[10]:
# while debugging/developing .vue files in the ipyvolume/vue directory,
# execute this to get hot reloading
# ipv.ui.watch()

screencapture