如何解决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>>
(其中.!combobox
是Combobox
小部件的名称,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
,字典值提供工具提示消息。
padx
和 pady
启用“工具提示”的微定位
工具提示出现在 Entry
对象中的选定值上。
这会调用 on_enter
方法并使用选择作为访问相关字典值的键。
该值被插入到 Toplevel's
'Labelbefore being displayed by
deiconify` 中。
on_leave
方法 withdraws
Toplevel
目前还没有时间延迟,我不想使用 time.sleep
或 after
- 所以我仍在努力。
“工具提示”是一个 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 举报,一经查实,本站将立刻删除。