如何使用 Kivy 制作工具提示?

How to make ToolTip using Kivy?

当鼠标指针悬停在 ActionBar 中的图标上时,我想看到 Qt 中的工具提示。
是的,我可以使用 mode='spinner',但图标更好。

一个您可以改进和扩展的简单示例:

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.widget import Widget
from kivy.core.window import Window
from kivy.uix.actionbar import ActionButton
from kivy.uix.label import Label
from kivy.clock import Clock

Builder.load_string("""
<Tooltip>:
    size_hint: None, None
    size: self.texture_size[0]+5, self.texture_size[1]+5
    canvas.before:
        Color:
            rgb: 0.2, 0.2, 0.2
        Rectangle:
            size: self.size
            pos: self.pos

<MyWidget>
    ActionBar:
        ActionView:
            MyActionButton:
                icon: 'atlas://data/images/defaulttheme/audio-volume-high'
            MyActionButton:
                icon: 'atlas://data/images/defaulttheme/audio-volume-high'                
""")

class Tooltip(Label):
    pass

class MyActionButton(ActionButton):
    tooltip = Tooltip(text='Hello world')

    def __init__(self, **kwargs):
        Window.bind(mouse_pos=self.on_mouse_pos)
        super(ActionButton, self).__init__(**kwargs)

    def on_mouse_pos(self, *args):
        if not self.get_root_window():
            return
        pos = args[1]
        self.tooltip.pos = pos
        Clock.unschedule(self.display_tooltip) # cancel scheduled event since I moved the cursor
        self.close_tooltip() # close if it's opened
        if self.collide_point(*self.to_widget(*pos)):
            Clock.schedule_once(self.display_tooltip, 1)

    def close_tooltip(self, *args):
        Window.remove_widget(self.tooltip)

    def display_tooltip(self, *args):
        Window.add_widget(self.tooltip)


class MyWidget(Widget):
    pass

class ClientApp(App):
    def build(self):
        return MyWidget()

if __name__ == '__main__':
    ClientApp().run()

首先,我将 on_mouse_pos 方法绑定到 Window.mouse_pos 事件,这样我就可以检测鼠标光标何时悬停在我的 ActionButton 子类上。这是基于 this snippet。然后我用 Clock.schedule_once() 安排了一个动作,如果我不移动光标,我的工具箱就可见。为了显示,我只是将 Label 的子类添加到小部件堆栈中。您可以用更复杂的方法替换 display_tooltip()close_tooltip() 方法。


编辑:相应地将代码更新为

带工具提示的扩展微调器

好的,我稍微扩展了一下。
现在可以用 KV 语言设置工具提示文本。所以每个 object/instance 都会有自己的工具提示。
Tooltip 及其文本 tooltip_txtToolTipSpinner.

的属性
from kivy.app import App
from kivy.core.window import Window
Window.minimum_width, Window.minimum_height = 800, 600
from kivy.clock import Clock
from kivy.compat import string_types
from kivy.uix.spinner import Spinner
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty
from kivy.properties import ObjectProperty
from kivy.factory import Factory
from kivy.lang import Builder

Builder.load_string("""
<ToolTipSpinner>:
<Tooltip>:
    size_hint: None, None
    size: self.texture_size[0]+5, self.texture_size[1]+5
    canvas.before:
        Color:
            rgb: 0.2, 0.2, 0.2
        Rectangle:
            size: self.size
            pos: self.pos

<MyBar>:
    orientation: 'horizontal'
    padding: 2
    spacing: 2
    canvas.before:
        Color:
            rgba: 1, 1, 1, 1
        Line:
            width: 1.
            rectangle: (self.x+1, self.y-1, self.width, self.height)
    BoxLayout:
        orientation: 'horizontal'
        padding: 2,2,2,2
        spacing: 2
        size_hint: None, 1
        width: 110
        ToolTipSpinner:
            id:  _spinner_type_1
            tooltip_txt: 'Tooltip T1'
            text:  'Type 1'
            values:   ['0', '1', '2', '3']
            size_hint:  None, .45
            on_text:  self.on_spinner_select(self.text)
        ToolTipSpinner:
            id:  _spinner_type_2
            tooltip_txt: 'Tooltip T2\nwith newline'
            text:  'Type 2'
            values:   ['4', '5', '6', '7', '8', '9']
            size_hint:  None, .45
            on_text:  self.on_spinner_select(self.text)
""")


class Tooltip(Label):
    pass


class MyBar(BoxLayout):
    pass


class ToolTipSpinner(Spinner):
    tooltip_txt = StringProperty('')
    tooltip_cls = ObjectProperty(Tooltip)
    
    def __init__(self, **kwargs):
        self._tooltip = None
        super(ToolTipSpinner, self).__init__(**kwargs)
        fbind = self.fbind
        fbind('tooltip_cls', self._build_tooltip)
        fbind('tooltip_txt', self._update_tooltip)
        Window.bind(mouse_pos=self.on_mouse_pos)
        self._build_tooltip()
    
    def _build_tooltip(self, *largs):
        if self._tooltip:
            self._tooltip = None
        cls = self.tooltip_cls
        if isinstance(cls, string_types):
            cls = Factory.get(cls)
        self._tooltip = cls()
        self._update_tooltip()
    
    def _update_tooltip(self, *largs):
        txt = self.tooltip_txt
        if txt:
            self._tooltip.text = txt
        else:
            self._tooltip.text = ''
    
    def on_spinner_select(self, text):
        print(text)
    
    def on_mouse_pos(self, *args):
        if not self.get_root_window():
            return
        pos = args[1]
        self._tooltip.pos = pos
        Clock.unschedule(self.display_tooltip) # cancel scheduled event since I moved the cursor
        self.close_tooltip() # close if it's opened
        if self.collide_point(*self.to_widget(*pos)):
            Clock.schedule_once(self.display_tooltip, 1)

    def close_tooltip(self, *args):
        Window.remove_widget(self._tooltip)

    def display_tooltip(self, *args):
        Window.add_widget(self._tooltip)
        
        
class MainApp(App):
    def build(self):
        return MyBar()

if __name__ == '__main__':
    MainApp().run()