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