How to make ToolTip using Kivy?

后端 未结 2 1045
情歌与酒
情歌与酒 2021-01-01 06:50

I want to see tooltip as in Qt when the mouse pointer is hovering over icon in ActionBar.
Yes, I can use mode=\'spinner\', but icons are n

相关标签:
2条回答
  • 2021-01-01 07:27

    Extended Spinner with Tooltip

    OK, I have extend it a little bit.
    Now it's possible to set tooltip text in KV language. So every object/instance would have its own tooltip.
    Tooltip and its text tooltip_txt, are properties of the ToolTipSpinner.

    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()
    
    0 讨论(0)
  • 2021-01-01 07:29

    A simple example you can improve and extend:

    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()
    

    First I bind on_mouse_pos method to Window.mouse_pos event so I can detect when the mouse cursor hovers over my subclass of ActionButton. This is based on this snippet. Then I shedule an action with Clock.schedule_once() to make my toolbox visible if I won't move my cursor. To display I'm just adding a subclass of Label to the stack of widgets. You can replace display_tooltip() and close_tooltip() methods with more sophisticated ones.


    EDIT: Updated the code accordingly to this answer

    0 讨论(0)
提交回复
热议问题