微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Python ttk 组合框中每个项目的悬停提示/工具提示

如何解决Python ttk 组合框中每个项目的悬停提示/工具提示

这是关于 ttk 组合框。 我需要通过工具提示为 ttk.comboBox 中的每个项目(文本)显示帮助信息。

最小示例如下:

import tkinter as tk
from tkinter import ttk
from idlelib.tooltip import Hovertip
names =["One","Two","Three"]
root = tk.Tk()
root.title("Combo Test GUI")
Frame1 = ttk.Frame(root,padding="3 3 12 12")
Frame1.grid(column= 0,row = 0)
label_1 = ttk.Label(Frame1,text="Select Item:",anchor=tk.W)
label_1.grid(column= 0,row = 1)
item1 = ttk.ComboBox(Frame1,state="readonly",values=names,width=35)
item1.grid(column= 0,row = 2)
m1 = Hovertip(item1,"One test")
m2 = Hovertip(label_1,"Another test")
root.mainloop()

问题:我想将 Hovertip 绑定到组合列表中的文本,即“一”、“二”、“三”。

用户将鼠标悬停在“一”上时,说明“这是选项一”,应显示在悬停提示/工具提示中;其他项目“二”和“三”也一样。

我到处搜索,在 Stackoverflow 上,在参考文档中,在其他网站上,但似乎 Hovertip/Tooltip 只能与小部件一起使用,而不能与组合框中的简单文本一起使用。

有没有办法让它成为可能?

解决方法

这个想法是创建一个类似于 Hovertip 的类,但它适用于列表框项目而不是小部件。当前项目存储在 self._current_item 中,当鼠标在列表框中移动时,如果项目发生变化,将重新安排工具提示的显示。每个项目的文本都存储在字典 self.tips = {item index: text,...} 中。

主要问题是显示 Listbox 选项的 Combobox 不能通过 python 直接访问。所以我不得不使用一些 Tcl 命令来做到这一点:

proc callback {y} {
     event generate .!combobox <<OnMotion>> -y $y
}

set popdown [ttk::combobox::PopdownWindow .!combobox]
bind $popdown.f.l <Motion> {callback %y}

当鼠标移动时,上面的代码在组合框中生成一个虚拟事件<<OnMotion>>(其中.!comboboxCombobox小部件的名称,y是鼠标相对小部件中的 y 坐标)。

然后我将组合框的 _on_motion() 方法绑定到此 <<OnMotion>> 事件以检查当前项目是否已更改。要获取当前项目,我使用 Listbox 方法 nearest(y) 但来自 Tcl。

我还修改了 get_position() 方法以在当前项目正下方显示工具提示,并修改 showcontents() 以显示与项目对应的文本。

完整代码如下:

import tkinter as tk
from tkinter import ttk
from idlelib.tooltip import OnHoverTooltipBase

class ComboboxTip(OnHoverTooltipBase):
    def __init__(self,combobox_widget,hover_delay=1000):
        super(ComboboxTip,self).__init__(combobox_widget,hover_delay=hover_delay)
        self.tips = {}
        self._current_item = 0

        combobox_widget.tk.eval("""
proc callback {y} {
     event generate %(cb)s <<OnMotion>> -y $y
}

set popdown [ttk::combobox::PopdownWindow %(cb)s]
bind $popdown.f.l <Motion> {callback %%y}
""" % ({"cb": combobox_widget}))

        self._id4 = combobox_widget.bind("<<OnMotion>>",self._on_motion)

    def _on_motion(self,event):
        current_item = int(self.anchor_widget.tk.eval("$popdown.f.l nearest %i" % event.y))
        if current_item != self._current_item:
            self._current_item = current_item
            self.hidetip()
            if current_item in self.tips:
                self.schedule()
            else:
                self.unschedule()

    def __del__(self):
        try:
            self.anchor_widget.unbind("<<OnMotion>>",self._id4)
        except tk.TclError:
            pass
        super(ComboboxTip,self).__del__()

    def add_tooltip(self,index,text):
        self.tips[index] = text

    def get_position(self):
        """choose a screen position for the tooltip"""
        try:
            h = self.anchor_widget.winfo_height()
            bbox = self.anchor_widget._getints(self.anchor_widget.tk.eval("$popdown.f.l bbox %i" % self._current_item))
            return bbox[0] + bbox[2],bbox[1] + bbox[-1] + h
        except Exception:
            return 20,self.anchor_widget.winfo_height() + 1

    def showcontents(self):
        label = tk.Label(self.tipwindow,text=self.tips[self._current_item],justify=tk.LEFT,background="#ffffe0",relief=tk.SOLID,borderwidth=1)
        label.pack()


names = ["One","Two","Three"]
root = tk.Tk()
cb = ttk.Combobox(root,values=names)
cb.pack()

t = ComboboxTip(cb)
t.add_tooltip(0,"This is One")
t.add_tooltip(1,"This is Two")
t.add_tooltip(2,"This is Three")

root.mainloop()    
,

"""NotToolTip.py

此脚本基于这样的想法:在任何时候都只能看到一个工具提示。

工作原理:

我使用 Toplevel 对象创建了一个使用 overrideredirect 的虚拟工具提示。

ttk.Combobox 的值数据存储在字典中并传递给 NotToolTip class。 字典键用于 combobox values,字典值提供工具提示消息。

padxpady 启用“工具提示”的微定位

工具提示出现在 Entry 对象中的选定值上。

这会调用 on_enter 方法并使用选择作为访问相关字典值的键。 该值被插入到 Toplevel's 'Labelbefore being displayed bydeiconify` 中。

on_leave 方法 withdraws Toplevel

目前还没有时间延迟,我不想使用 time.sleepafter - 所以我仍在努力。

“工具提示”是一个 Toplevel 窗口!

您可以显示带有格式化文本的图像或 Text 对象,甚至可以实现 TTS 以便工具提示会说话!

想象一下你可以用它做什么。

我一直在试验它,所以代码包含了颜色、字体、图像边框宽度等方便的对象。

这里是... NotToolTip

"""

import tkinter as tk
from tkinter import ttk
from tkinter import filedialog as fido

imager = False

class NotToolTip:

    def flexx( self,o,r = 0,c = 0,rw = 1,cw = 1 ) ->'Set grid manager to grow':
        '''tool.flexx(o(TkContainer),r=0,c=0,rw=1,cw=1)'''
        if r != None:
            o.rowconfigure( r,weight = rw )
        if c != None:
            o.columnconfigure( c,weight = cw )


    def __init__( self,master,names,padx = 0,pady = 0 ):
        '''NotToolTip( master,names(dict),padx(int),pady(int) )'''

        self.master = master
        self.master.title( "Not Tool Tip" )
        self.flexx( master )

        # Define names,padx,pady and keylist
        self.names = names
        self.padx,self.pady = padx,pady        
        self.keylist = list( self.names.keys() )
        
        # Define frame
        self.frame = ttk.Frame( self.master )
        self.frame.grid( row = 0,column = 0,sticky = tk.NSEW )
        self.flexx( self.frame )

        # Begin defining `Combobox`
        self.var = tk.StringVar()
        self.combo = ttk.Combobox(
            self.frame,state="readonly",textvariable = self.var,values = self.keylist )
        self.combo.grid( row = 0,sticky = tk.NSEW )
        self.master.geometry( '244x33' )
        self.master.update()
        self.master.deiconify()

        # Only one single 'tooltip' is required per `Tk`
        self.tooltip = tk.Toplevel(
            self.master,highlightbackground = [ 'red','black','light green' ][ 0 ],highlightthickness = [ 0,1,2,3 ][ 2 ] )
        self.tooltip.withdraw()

        # Define label
        self.label = tk.Label(
            self.tooltip,justify = [ 'left','right','center' ][ 2 ],text = "I am not a tooltip\nI am a Toplevel window",font = 'Helvetica 12 {italic bold}',background = [ 'white','yellow','cyan' ][ 2 ] )

        # Complete label initialization
        if imager and my_image:
            self.label.config(
                image = my_image,compound = [
                    'bottom','center','left','none','top' ][ ~0 ] )
        self.label.grid( row = 0,sticky = tk.NSEW )
        self.tooltip.update()
        self.tooltip.overrideredirect( 1 )

        # NotToolTip control
        self.combo.bind( "<Enter>",self.on_enter )
        self.combo.bind( "<Leave>",self.on_leave )

    def getpos( self ):
        return f'+{self.combo.winfo_rootx()+self.padx}+{self.combo.winfo_rooty()+self.pady}'

    def on_enter( self,e ):
        try:
            self.tooltip.geometry( self.getpos() )
            name = self.names[ self.var.get() ]
            self.label[ 'text' ] = name
        except:
            pass
        finally:
            self.tooltip.deiconify()

    def on_leave( self,e ):
        self.tooltip.geometry( self.getpos() )
        self.tooltip.withdraw()


if __name__ == "__main__":

    root = tk.Tk()
    root.withdraw()

    if imager:
        imageName = fido.askopenfilename( title = 'Pick Image' )
        my_image = tk.PhotoImage( file = imageName ).subsample( 4,4 )
    else:
         my_image = False

    names = {
        "One": "Something about one","Two": "Something about two","Three": "Something about three" }

    tooltip = NotToolTip( root,pady = 30 )

    root.mainloop()

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。