Tooltip POC

A user is asking for a simple way to use the doc value of a Parameterized class as the tooltip for a widget.

Tooltip when hovering over parameter name based on Param parameter’s doc attribute · Issue #3441 · holoviz/panel (github.com)

Since providing this correctly would mean updating the underlying bokeh widgets and could take some time I started thinking about a short term alternative: the Tooltip wrapper.

You can use it like

button = pn.widgets.Button(name="A Widget with tooltip")
tooltip_button = Tooltip(button, title="Will send email when clicked!").servable()

class TestClass(param.Parameterized):
        run = param.Event(label="A Parameterized Widget with a tooltip", doc="Sends an email")
test_class = TestClass()
Tooltip(test_class.param.run).servable()

The full code is below.

import panel as pn
import param

class Tooltip(pn.reactive.ReactiveHTML):
    _title = param.String(None)
    _widget = param.Parameter()

    def __init__(self, object, title=None):
        super().__init__()
        self._update_object_and_title(object, title)
    
    @staticmethod
    def _to_widget_and_title(object, _title=None):
        if isinstance(object, str):
            return object, _title if _title else object
            
        if isinstance(object, pn.widgets.Widget):
            return object, _title if _title else object.name

        if isinstance(object, param.Parameter):
            return pn.Param(object, show_name=False)[0], object.doc

        raise NotImplementedError()

    def _update_object_and_title(self, object, title=None):
        widget, _title = self._to_widget_and_title(object, title)
        self.param.update(_widget=widget, _title=_title)

    _template = """
    <div id="pn-container" style="width:100%;height:100%">
    <div id="widget" title="${_title}">${_widget}</div>
    </div>
    """

class TestClass(param.Parameterized):
        run = param.Event(label="A Parameterized Widget with a tooltip", doc="Sends an email")

def test_to_widget_and_tooltip():
    Tooltip._to_widget_and_title("hello")==("hello", "hello")
    
    button = pn.widgets.Button(name="hello")
    Tooltip._to_widget_and_title(button)==(button, button.name)
    
    test_class = TestClass()
    widget, title = Tooltip._to_widget_and_title(test_class.param.run)
    assert isinstance(widget, pn.widgets.Button)
    assert title=="Sends an email"

button = pn.widgets.Button(name="A Widget with tooltip")
tooltip_button = Tooltip(button, title="Will send email when clicked!").servable()

test_class = TestClass()
Tooltip(test_class.param.run).servable()
3 Likes

Some improvements that could be nice would be

Support Parameterized class

Tooltip(test_class)

Would give the set of widgets as pn.Param, but with tooltips. Should enable provide parameters, widgets etc to pn.Param.

Support Panes and Layouts

Speaks for it self

Improve tooltips using some .js library

For example something like Popper - Tooltip & Popover Positioning Engine