tkinter文本框小功能(2):提示框

  • 引言
  • 思路
  • 实现
  • 窗口外的ToolTip
  • 注意区别


引言

在Python自带,用tkinter编写的IDLE中,会出现一些函数使用气泡提示框在文本框中显示注释文本,虽然效率有点慢,但是能够让读者直观地显示函数的意义和作用。
从idlelib的源码中,可以看出这个是用tix中的气泡提示框实现的,但是只能够显示文本。在我的这篇使用Label实现Balloon的文章中,Label组件内可以继续放置tkinter组件,从而提升气泡提示框的丰富程度。

现在,我们可以用那一篇文章的思路,更简洁地来完成这个功能。

思路

之前的文章是在窗口下实现对组件绑定气泡提示框,而在文本框中,绑定tag就可以实现相同的功能。
为了在所有文本框中均可以使用,先定义一个文本框类:

from tkinter import Text
class MyText(Text):
    
    def __init__(self,master,**kw):
        Text.__init__(self,master,**kw)
    
    def text_balloon_show(self,event,text,fg='black',bg='#f0f0f0'):#显示提示框
        pass

    def text_balloon_end(self,event):#提示框关闭
        pass

根据前面的文章,思路是在文本框中,通过对文本添加tag,当鼠标进入时显示气泡提示框,当鼠标离开时关闭气泡提示框。

实现

以下是测试代码:

t=MyText(fg='black',bg='#00011')#颜色是乱写的

t.tag_configure('test',bg='blue')
t.insert('end','长征火箭','test')
t.tag_bind('test','<Enter>',lambda event:t.text_balloon_show(event,'中国航天局的运载火箭系列名称,英文:CZ'))
t.tag_bind('test','<Leave>',t.text_balloon_end)

t.mainloop()#组件也有mainloop

由此可以继续编写text_balloon_show和text_balloon_end两个函数

from tkinter import Text
class MyText(Text):
    
    def __init__(self,master,**kw):
        Text.__init__(self,master,**kw)

    def text_balloon_show(self,event,text,fg='black',bg='#f0f0f0'):#受控的气泡提示框
        ball=Label(self,text=text,fg=fg,bg=bg)#创建一个Label
        ball.place(x=event.x,y=event.y)#定位鼠标在文本框中的位置
        self.ball_text=ball

    def text_balloon_end(self,event):
        self.ball_text.place_forget()

效果见tkinter文本框小功能(1)。

在TinReader中的超链接,使用 <word>描述;前景色;链接

窗口外的ToolTip

之前的代码中,提示框都是显示在组件内部,但是相比于整个窗口而言,提示框的功能就小了很多。而在常见的tkinter拓展中,都会出现一个叫 ToolTip 的拓展组件,这个组件可以设置透明度、显示延迟、鼠标跟随,同时还能够插入其它组件。下面的ToolTip代码取自PAGE-tkinter编辑器。

class ToolTip(Toplevel):
    """
    Provides a ToolTip widget for Tkinter.
    To apply a ToolTip to any Tkinter widget, simply pass the widget to the
    ToolTip constructor
    """
    def __init__(self, wdgt, tooltip_font, msg=None, msgFunc=None, fg='black', bg='#ffff00',
                 delay=0.5, follow=True):
        """
        Initialize the ToolTip

        Arguments:
          wdgt: The widget this ToolTip is assigned to
          tooltip_font: Font to be used
          msg:  A static string message assigned to the ToolTip
          msgFunc: A function that retrieves a string to use as the ToolTip text
          delay:   The delay in seconds before the ToolTip appears(may be float)
          follow:  If True, the ToolTip follows motion, otherwise hides
        """
        self.wdgt = wdgt
        # The parent of the ToolTip is the parent of the ToolTips widget
        self.parent = self.wdgt.master
        self.fg,self.bg=fg,bg
        # Initalise the Toplevel
        Toplevel.__init__(self, self.parent, bg=self.bg, padx=1, pady=1)
        # Hide initially
        self.withdraw()
        # The ToolTip Toplevel should have no frame or title bar
        self.overrideredirect(True)

        # The msgVar will contain the text displayed by the ToolTip
        self.msgVar = tk.StringVar()
        if msg is None:
            self.msgVar.set('No message provided')
        else:
            self.msgVar.set(msg)
        self.msgFunc = msgFunc
        self.delay = delay
        self.follow = follow
        self.visible = 0
        self.lastMotion = 0
        # The text of the ToolTip is displayed in a Message widget
        tk.Message(self, textvariable=self.msgVar, bg=self.bg, fg=self.fg,
                font=tooltip_font,
                aspect=1000).grid()

        # Add bindings to the widget.  This will NOT override
        # bindings that the widget already has
        self.wdgt.bind('<Enter>', self.spawn, '+')
        self.wdgt.bind('<Leave>', self.hide, '+')
        self.wdgt.bind('<Motion>', self.move, '+')

    def spawn(self, event=None):
        """
        Spawn the ToolTip.  This simply makes the ToolTip eligible for display.
        Usually this is caused by entering the widget

        Arguments:
          event: The event that called this funciton
        """
        self.visible = 1
        # The after function takes a time argument in milliseconds
        self.after(int(self.delay * 1000), self.show)

    def show(self):
        """
        Displays the ToolTip if the time delay has been long enough
        """
        if self.visible == 1 and time() - self.lastMotion > self.delay:
            self.visible = 2
        if self.visible == 2:
            self.deiconify()

    def move(self, event):
        """
        Processes motion within the widget.
        Arguments:
          event: The event that called this function
        """
        self.lastMotion = time()
        # If the follow flag is not set, motion within the
        # widget will make the ToolTip disappear
        #
        if self.follow is False:
            self.withdraw()
            self.visible = 1

        # Offset the ToolTip 10x10 pixes southwest of the pointer
        self.geometry('+%i+%i' % (event.x_root+20, event.y_root-10))
        try:
            # Try to call the message function.  Will not change
            # the message if the message function is None or
            # the message function fails
            self.msgVar.set(self.msgFunc())
        except:
            pass
        self.after(int(self.delay * 1000), self.show)

    def hide(self, event=None):
        """
        Hides the ToolTip.  Usually this is caused by leaving the widget
        Arguments:
          event: The event that called this function
        """
        self.visible = 0
        self.withdraw()

    def update(self, msg):
        """
        Updates the Tooltip with a new message. Added by Rozen
        """
        self.msgVar.set(msg)

这里里面的注释写得很详细。如果想用在文本框内,可进行如下更改:

def __init__(self, text, tagname,tooltip_font, msg=None, msgFunc=None, fg='black', bg='#ffff00',
                 delay=0.5, follow=True):#wdgt=>tagname, +text
        self.tagname=tagname###
        self.parent = text.master###
        #...
        text.tag_bind(self.tagname,'<Enter>', self.spawn, '+')
        text.tag_bind(self.tagname,'<Leave>', self.hide, '+')
        text.tag_bind(self.tagname,'<Motion>', self.move, '+')

注意区别

与前一篇文章相比,该文章是有一定的区别的,使用场合也是不一样的。
如果没有看过之前的文章,现在建议查看一下使用Label实现Balloon

在之前的文章中,是组建需要绑定提示框,那么提示框必须在组件之外。<Button-1>事件中,event会返回两个坐标数组,其中rootx, rooty是相对于窗口左上角顶点的坐标,因此Label只能创建在主窗口中。
在文本框里,文字始终在文本框内部,而且可以滚动,因此使用event.x, event.y更加方便。

需要注意的是,这篇文章只是在文本框实现的小功能,并不是之前那篇文章的升级,因此需要注意使用场合和方式。

☀tkinter创新☀