25-Tkinter 组件-balloon气泡提示框

balloon气泡提示框

原生气泡介绍

tix.Balloon气球窗口小部件

balloon这个小型控件存在于 tkinter.tix 中,必须在tix创建的Tk窗口中使用

Tix.Balloon气球窗口小部件一个部件弹出提供帮助。当用户将光标移动到已绑定了气球窗口小部件的窗口小部件中时,屏幕上将显示一个带有描述性消息的小型弹出式窗口。
使用tix.Balloon气球窗口小部件的构造语法如下:

balloon = tix.Balloon (master, statusbar=None)

  • master 这代表了父部件。
  • statusbar是这个部件的绑定的状态条部件。

绑定小部件。

balloon .bind_widget(widget, balloonmsg=’’, statusmsg=None)
  • widget是要绑定的小部件,也就是说鼠标移动到小部件上方会出现需要提示信息。
  • balloonmsg是需要提示的信息。
  • statusmsg是需要在状态栏显示的信息。

解绑小部件。

balloon.unbind_widget(widget)

widget是要解绑的小部件。

演示

# -*- coding: utf-8 -*-
# https://blog.csdn.net/hepu8
# 独狼荷蒲 QQ:2775205

import tkinter.tix as Tix   #导入Tkinter.tix
from tkinter.constants import *

TCL_ALL_EVENTS		= 0

def RunSample (root):
    balloon = DemoBalloon(root)
    balloon.mainloop()
    balloon.destroy()

class DemoBalloon:
    def __init__(self, w):
        self.root = w
        self.exit = -1

        z = w.winfo_toplevel()
        z.wm_protocol("WM_DELETE_WINDOW", lambda self=self: self.quitcmd())
        z.title('Tix.Balloon演示')  #Tkinter中设置窗口标题方法

        status = Tix.Label(w, width=40, relief=Tix.SUNKEN, bd=1)
        status.pack(side=Tix.BOTTOM, fill=Tix.Y, padx=2, pady=1)

        # 建立2个 tix按钮
        button1 = Tix.Button(w, text='关闭窗口',
                             command=self.quitcmd)
        button2 = Tix.Button(w, text='按钮自毁')
        button2['command'] = lambda w=button2: w.destroy()
        button1.pack(side=Tix.TOP, expand=1)
        button2.pack(side=Tix.TOP, expand=1)

        # 建立 tixballoon
        b = Tix.Balloon(w, statusbar=status)

        b.bind_widget(button1, balloonmsg='关闭这个窗口',
                      statusmsg='按下这个按钮,关闭窗口。')
    
        b.bind_widget(button2, balloonmsg='删除这个按钮',
                      statusmsg='按下这个按钮,删除这个按钮。')

    def quitcmd (self):
        self.exit = 0

    def mainloop(self):
        foundEvent = 1
        while self.exit < 0 and foundEvent > 0:
            foundEvent = self.root.tk.dooneevent(TCL_ALL_EVENTS)

    def destroy (self):
        self.root.destroy()

if __name__ == '__main__':
    root = Tix.Tk()
    RunSample(root)

自写气泡

原本的气泡限制太多,且还显示使用tix的组件使用。自己改进的气泡提示框tk组件通用

# coding = utf-8
# File date: Hi_2022/1/3 11:29
# File_name: 22_Balloon气球窗口小部件.py
from tkinter import Label, Toplevel
from typing import Literal


class Balloon:
    """
    自定义气泡类
    """

    def __init__(self,
                 widget,
                 text: str = "",
                 fg="white",
                 bg="skyblue",
                 justify: Literal["left", "center", "right"] = "left",
                 anchor: Literal["nw", "n", "ne", "w", "center", "e", "sw", "s", "se"] = "center",
                 x_offset: int = 0,
                 y_offset: int = 0,
                 show_width: int = 0,
                 follow: bool = True,
                 alpha: float = 0.95,
                 delayed=250,
                 topmost=True,
                 *args,
                 **kwargs):
        """
        导入Balloon类即可使用,传入widget参数即需要显示提示的组件。
            :param widget: 要提示的组件
            :param text: 提示的文字信息
            :param fg: 前景色默认"black",由于使用了ttk皮肤实际颜色可能会不对应
            :param bg:背景色,默认"SkyBlue",由于使用了ttk皮肤实际颜色可能会不对应
            :param justify:定义对齐方式,可选Literal["left", "center", "right"],默认为 "left"。
            :param anchor:文本或图像在背景内容区的位置,默认"w",可选Literal["nw", "n", "ne", "w", "center", "e", "sw", "s", "se"]
            :param x_offset: 显示提示左上角x坐标偏移量,>0向右,<0向左,默认0
            :param y_offset: 显示提示左上角y坐标偏移量,>0向下,默认有15的偏移量必须有偏移量否则当与鼠标处于同一位置会造成无法显示的问题
            :param show_width: 提示框宽度,以像素为单位,不更改字体缩放比例100%大概12个像素一个汉字
            :param alpha: 透明度,1.0表示不透明,0.0表示完全透明
            :param delayed: 移动时更新延时以毫秒为单位
            :param follow: 提示跟随移动,设置Ture时鼠标在指定组件内移动时会一直跟随,False则只会在进入时显示就不再跟随显示
            :param topmost: 窗口置顶,在主窗口已经置顶时需要添加此参数
            :param args: 其他更多参数参考tkinter.Label可传入参数
            :param kwargs: 其他更多参数参考Label可传入参数
        """
        self.text = text
        self.fg = fg
        self.bg = bg
        self.width = show_width
        self.justify = justify
        self.anchor = anchor
        self.args = args
        self.kwargs = kwargs

        self.top = None
        self.id = None
        self.follow = follow
        self.widget = widget
        self.alpha = alpha
        self.delayed = delayed  # 延时,毫秒
        self.topmost = topmost

        # xy坐标偏移量
        self.x_offset = x_offset
        self.y_offset = y_offset

        self.widget.bind('<Enter>', lambda x: self.enter())  # 进入控件区域
        self.widget.bind("<ButtonPress>", lambda x: self.leave())  # 按下任意鼠标按下消失
        self.widget.bind('<Leave>', lambda x: self.leave())  # 离开控件区域
        if self.follow:  # 跟随显示
            self.widget.bind("<Motion>", lambda x: self.move())  # 鼠标移动提示跟随

    def balloon_show(self):
        """显示toplevel"""
        if self.top:
            return

        self.updata_width()  # 显示前先更新宽度

        self.top = Toplevel(autostyle=False)  # 如果在ttkbootstrap需要自定义配色,不受全局主题影响则设置autostyle=False参数,纯tk请删除此参数
        self.top.attributes("-alpha", self.alpha)  # 透明度
        self.top.overrideredirect(True)  # 只保留窗口不要最小化关闭等
        self.top.attributes("-topmost", self.topmost)  # 置顶
        self.top.minsize(self.width, 30)  # 宽度最少需要240小于240不能居中

        # 偏移量默认居中
        x = self.widget.winfo_pointerx() + self.x_offset - self.width // 2
        if self.y_offset + 15 < 15:
            self.y_offset = 0
        y = self.widget.winfo_pointery() + self.y_offset + 15
        self.top.geometry(f"+{x}+{y}")

        balloon = Label(self.top,
                        text=self.text,
                        fg=self.fg,
                        bg=self.bg,
                        justify=self.justify,
                        anchor=self.anchor,
                        wraplength=self.width - 20,
                        autostyle=False, # 如果在ttkbootstrap需要自定义配色,不受全局主题影响则设置autostyle=False参数,纯tk请删除此参数
                        *self.args, **self.kwargs)
        balloon.pack(fill="both", expand=1, ipadx=10, ipady=5)  # ipadx要等于wraplength的一半

    def updata_width(self):
        """根据文本大小自适应宽度"""
        if not self.width:
            test_long = len(self.text)
            if test_long <= 30:
                self.width = test_long * 12 + 20  # 每个字大概12,20是给宽度补充的
            elif test_long <= 90:
                self.width = 300
            elif 90 < test_long <= 250:
                self.width = test_long + 200
            else:
                self.width = 450

    def enter(self):
        self.schedule()

    def leave(self):
        self.unschedule()
        self.destroy_balloon()

    def schedule(self):
        self.unschedule()
        self.id = self.widget.after(self.delayed, self.balloon_show)

    def unschedule(self):
        show_id = self.id
        self.id = None
        if show_id:
            self.widget.after_cancel(show_id)

    def move(self):
        """移动提示框"""
        if self.top:
            x = self.widget.winfo_pointerx() + self.x_offset - self.width // 2
            if self.y_offset + 15 < 15:
                self.y_offset = 0
            y = self.widget.winfo_pointery() + self.y_offset + 15
            self.top.geometry(f"+{x}+{y}")

    def destroy_balloon(self):
        """取消气泡提示"""
        if self.top:
            self.top.destroy()
            self.top = None

    def updata_text(self, new_text, new_width=0):
        """
        更新文本并重新计算宽
        :param new_text: 要更新的文本
        :param new_width: 重置的宽度不设置默认0,则自动计算新宽度
        """
        self.text = new_text
        self.width = new_width  # 设置False才能重新计算高度

    def unballoon(self):
        """
        彻底取消气泡,再次使用需要重新调用Balloon
        重复调用会报AttributeError: 'Balloon' object has no attribute 'widget'
        """
        self.widget.unbind('<Enter>')  # 进入控件区域
        self.widget.unbind("<ButtonPress>")  # 按下任意鼠标按下消失
        self.widget.unbind('<Leave>')  # 离开控件区域
        if self.follow:  # 跟随显示
            self.widget.unbind("<Motion>")  # 鼠标移动提示跟随

            del self.text
            del self.fg
            del self.bg
            del self.justify
            del self.anchor
            del self.args
            del self.kwargs

            del self.top
            del self.id
            del self.follow
            del self.widget
            del self.width
            del self.alpha
            del self.delayed
            del self.topmost

            # xy坐标偏移量
            del self.x_offset
            del self.y_offset
            return True
        except AttributeError:
            return 1


if __name__ == '__main__':
    from tkinter import Tk
    from tkinter.ttk import Button

    root = Tk()
    width = 230
    height = 600
    win_w = root.winfo_screenwidth()  # 桌面长宽
    win_h = root.winfo_screenheight()
    root.geometry(f"{width}x{height}+{(win_w - width) // 2}+{(win_h - height) // 2}")  # 设置窗口位置和宽高

    not_fllow = Button(root, text="不跟随")
    fllow_but = Button(root, text="跟随")
    text_but = Button(root, text="稍长文本")
    long_but = Button(root, text="超长文本自适应宽度")
    long_width_but = Button(root, text="超长文本宽度调节")
    test_width_but = Button(root, text="文本宽度测试")

    not_fllow.pack(ipady=30, ipadx=20, pady=5)
    fllow_but.pack(ipady=30, ipadx=20, pady=5)
    text_but.pack(ipady=30, ipadx=20, pady=5)
    long_but.pack(ipady=30, ipadx=20, pady=5)
    long_width_but.pack(ipady=30, ipadx=20, pady=5)
    test_width_but.pack(ipady=30, ipadx=20, pady=5)

    long_str = """
    1、已经渐渐养成不去解释的习惯,很多情绪也无从分享,干脆自我消化。懂的人自然懂,不懂的人再多解释也有时差,有些故事只能说给懂的人听。
    2、到了一定的时候,我身边的人纷纷离去。当一个个熟悉和离去的越来越快的时候,我发现已经很久没有遇见以前朝夕相伴的人了。
    3、坐在自己那张宽大的双人床上,房间忽然变的很大,空空荡荡的,天花版上写满了寂寞的句子,心里装满了读不懂的失落。
    4、很多人都说,真想知道,如果有一天我失去了一切,变得一无所有,谁还会站在我身
    """

    # 9、牵过你的手,吻过你的唇,感受过你的怀抱,拥有过你的温柔,所以以后不许再有他人。
    # 10、每一个人心中都深藏着一个人,你不知道对方是否生活的好与不好,但有时候,你怀念的却只是一个简单的名字,一段简单的相遇。
    width_str = "一二三四五六七八九零一二三四五六七八九零一二三四五六七八九零"
    Balloon(not_fllow, text="这是不跟随效果", follow=False)
    Balloon(fllow_but, text="这是跟随效果")
    Balloon(text_but, text="这是稍长文本展示\n每一个人心中都深藏着一个人,你不知道对方是否生活的好与不好,但有时候,你怀念的却只是一个简单的名字,一段简单的相遇")
    Balloon(long_but, text=long_str)
    Balloon(long_width_but, text=long_str, show_width=600)
    test_bool = Balloon(test_width_but, width_str)

    root.mainloop()

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

士别三日,当挖目相待

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值