四、工具类
PhotoImage
这个就是原 tkinter 模块的 PhotoImage 类的强化版
仍是只支持 png 和 gif 格式的图片,但可以很好的处理 gif 的格式,且提供对 png 图片的缩放操作
参数说明
PhotoImage(file: str | bytes, **kw)
① file: 文件路径,和原 tkinter 模块里的 PhotoImage 一样的参数
② **kw: 其他参数,为了兼容原 tkinter 模块里的 PhotoImage 的其他参数
实例方法
① parse
parse()
用于解析动图,没有参数,返回一个生成器(Generator)
② play
1. play(canvas: Canvas, 2. id, # type: tkinter._CanvasItemId 3. interval: int)
用于播放动图(播放条件:canvas 的 lock 属性的值为 True)
① canvas: 播放动画的画布
② id: 播放动画的 _CanvasItemId(就是 create_text 的返回值)
③ interval: 每帧动画的间隔时间(单位:毫秒)
③ zoom
zoom(rate_x: float, rate_y: float, precision: float = 1)
缩放图片(目前返回值为原 tkinter 的 PhotoImage 类)
暂时只支持对 png 格式图片的缩放,gif 格式的仍在测试中……
① rate_x: 横向缩放倍率
② rate_y: 纵向缩放倍率
③ precision: 精度到小数点后的位数,越大运算就越慢
详细用法
如果是 png 格式的图片,用法和原 tkinter 模块里的 PhotoImage 用法一模一样,没有任何改变,主要是对 gif 格式图片有所改变。
首先是,读取 gif 格式的图片时,不需要再写 format 参数了,自动读取并解析每一帧的动画。先实例化一个 gif 格式的图片的 PhotoImage 类,然后再解析它,可以一次性解析它全部的帧数,但考虑到大的动图解析很慢,所以就将解析方法的返回值改成了生成器,以便在外部用协程的方式去迭代它,以实现进度条的功能。这里说的很抽象,具体看下面的示例。
动图播放
上述效果的源代码:
import tkintertools root = tkintertools.Tk('tkinter辅助模块操作说明', '960x540') canvas = tkintertools.Canvas(root, 960, 540, bg='black') canvas.place(x=0, y=0) # 用来播放动图的 _CanvasItemId background = canvas.create_image(480, 270) # 创建 PhotoImage 类 image = tkintertools.PhotoImage('background.gif') # 解析每一帧的图片 for i in image.parse(): # 每次读取的返回值为当前已解析的帧数 print(i) # 播放动图 image.play(canvas, background, 70) root.mainloop()
上述代码执行后并不是立刻就会弹出窗口的,因为它此时正在解析,在命令行上会看到打印出的当前已解析的帧数。解析完后窗口会立刻弹出并播放动图。
解析的方法设计成这样,一是避免解析大动图(帧数很多)时造成卡顿,二是可以实现进度条的效果,实时查看解析的进度,这样当我们在写大项目的时候,一开始要解析很多动图时,就可以这样把所有的动图整合起来,一次性加载,顺便搞个进度条。而且!最重要的是!这样加载动图并实现进度条的功能,是不需要多线程的参与的!实现这些的功能是最简单的协程 —— yield 关键字!
当然了,也可以用错误捕捉加上 next 函数去迭代生成器以实现进度条的功能:
进度条
上述产生进度条的源代码:
import tkintertools root = tkintertools.Tk('tkinter辅助模块操作说明', '960x540') canvas = tkintertools.Canvas(root, 960, 540, bg='black') canvas.place(x=0, y=0) # 用来播放动图的 _CanvasItemId background = canvas.create_image(480, 270) # 创建 PhotoImage 类 image = tkintertools.PhotoImage('background.gif') # 创建生成器 generator = image.parse() # 创建进度条 canvas.create_rectangle(60, 450, 900, 480, outline='orange', width=2) bar = canvas.create_rectangle(60, 450, 60, 480, width=0, fill='lightgreen') def load(): """ 加载函数 """ try: # 解析下一帧 i = next(generator) # 修改进度条的长度 canvas.coords(bar, 60, 450, 60 + i * 20, 480) # 再次执行该函数,间隔1毫秒 root.after(1, load) except: # 播放动图 image.play(canvas, background, 70) load() root.mainloop()
关于缩放,那就很简单了嘛,但是!你可以去网上找找,看看有没有不用 PIL 库就可以实现缩放图片的,我曾经找过,几乎没有!然鹅,我这里在不引入第三方库的情况下,实现了!虽然在计算速度上可能比 PIL 库稍差一点,但是也足以满足我们的需求!
回到缩放的方法上,precision 参数可以是浮点数,默认为 1,那么缩放的倍率呢就支持到小数点后一位,多了也不精准。顺便一提,将 precision 提升至 2 的话,计算量和计算时间会增长 10 倍!因此,若非对精度有很高的要求,建议把 precision 设在 1.5 以下为好。
这里还要附加地说明一个缺点,这个缺点不是我这个模块造成的,而是原 tkinter 模块就有这个缺陷。就是原 tkinter 的 PhotoImage 的示例对象必须是全局变量的情况下,才能显示其图片,否则只是一片空白,所以用 zoom 进行缩放后得到的图片,记得要“全局化”后再调用!
五、功能函数
1、move_widget
控件移动函数
以特定方式移动由 Place 布局的某个控件或某些控件的集合或图像
参数说明
move_widget(master: Tk | Canvas | tkinter.Misc | tkinter.BaseWidget, widget: Canvas | _BaseWidget | tkinter.BaseWidget, dx: int, dy: int, times: int, mode: # type: Literal['smooth', 'rebound', 'flat'] | tuple[function, float, float] )
master: 控件所在的父控件
widget: 要移动位置的控件
dx: 横向移动的距离(单位:像素)
dy: 纵向移动的距离
times: 移动总时长(单位:秒)
mode: 模式,可选三种
smooth: 速度先慢后快再慢(Sin函数模式,0~π)
rebound: 和 smooth 一样,但是最后会回弹一下(Cos函数模式,0~0.6π)
flat: 匀速平移
或者为一个元组的形式:(移动函数, 起始值,终止值)
详细用法
这个函数不仅仅可以对本模块的控件产生效果,对所有原 tkinter 模块的控件也有同样的效果。
有了它,我们可以很轻松的实现以下效果:(鼠标左键按下弹出提示框)
【默认外观】
默认外观-提示框
源代码:
import tkintertools root = tkintertools.Tk('tkinter辅助模块操作说明', '960x540') canvas = tkintertools.Canvas(root, 960, 540) canvas.place(x=0, y=0) def callback(): """ 键盘关联函数 """ tip = tkintertools.CanvasLabel(canvas, 700, 550, 250, 100, 0, '— 提示 —\n恭喜你!\n账号登录成功!') tkintertools.move_widget(canvas, tip, 0, -120, 0.3, 'rebound') # 按下鼠标左键执行关联函数(当然,其他函数的触发方式也可) root.bind('<Button-1>', lambda _: callback()) root.mainloop()
【换上皮肤】
换上皮肤-提示框
源代码:
import tkintertools root = tkintertools.Tk('tkinter辅助模块操作说明', '960x540') canvas = tkintertools.Canvas(root, 960, 540) canvas.place(x=0, y=0) background = tkintertools.PhotoImage('background.png') canvas.create_image(480, 270, image=background) def callback(): """ 键盘关联函数 """ tip = tkintertools.CanvasLabel(canvas, 700, 550, 250, 100, 0, '— 提示 —\n恭喜你!\n账号登录成功!', color_fill=tkintertools.COLOR_NONE, color_text=('grey', 'white', ''), color_outline=('grey', 'white', '')) tkintertools.move_widget(canvas, tip, 0, -120, 0.3, 'rebound') # 按下鼠标左键执行关联函数(当然,其他函数的触发方式也可) root.bind('<Button-1>', lambda _: callback()) root.mainloop()
同样的道理,我们也可以让它在适当的时候,再收回去,然后删除它以释放内存。这样,一个提示框的功能就轻易实现了!
又或者我们可以实现一个界面切换的操作界面切换
上述例子的源代码:
import tkintertools root = tkintertools.Tk('界面切换', geometry='960x540') canvas_1 = tkintertools.Canvas(root, 960, 540, bg='lightgreen') canvas_2 = tkintertools.Canvas(root, 960, 540, bg='skyblue') canvas_1.place(x=0, y=0) canvas_2.place(x=960, y=0) tkintertools.CanvasButton( canvas_1, 830, 500, 120, 30, 5, '切换界面', command=lambda: (tkintertools.move_widget(root, canvas_1, -960, 0, 0.25, 'smooth'), tkintertools.move_widget(root, canvas_2, -960, 0, 0.25, 'smooth'))) tkintertools.CanvasButton( canvas_2, 10, 500, 120, 30, 5, '切回界面', command=lambda: (tkintertools.move_widget(root, canvas_1, 960, 0, 0.25, 'smooth'), tkintertools.move_widget(root, canvas_2, 960, 0, 0.25, 'smooth'))) root.mainloop()
2、correct_text
修正字符串长度
可将目标字符串改为目标长度并以某种方式对齐
参数说明
1. correct_text(length: int, 2. string: str, 3. position: Literal['left', 'center', 'right'] = 'center')
length: 目标长度
string: 要修改的字符串
position: 文本处于该长度范围的位置,可选靠左、居中、靠右三个值
left: 文本靠左
center: 文本居中
right: 文本靠右
详细用法
这个就很简单了,一般用于多行文本和单行多个文本之间的对齐,直接给出一个示例吧。
1. print(tkintertools.correct_text(20, '这是个文本')) 2. """ 输出 3. tkintertools #到这里截止 4. """
实战中就是像下面这样的:
correct_text 详细用法
这里每一列的文本长度都不是一定,所以就用 correct_text 函数改变它们的长度,并让其居中以达到对齐的效果。
3、change_color
颜色处理函数
按一定比率给出已有 RGB 颜色字符串的渐变 RGB 颜色字符串或其对比色
change_color(color: tuple[str, str] | list[str, str] | str, proportion: float = 1)
color: 颜色元组 (要修改的颜色, 目标颜色),或者一个颜色(返回其对比色)
proportion: 改变比率,默认为 1
详细用法
这个很简单,就是按着参数的来就行,下面给个实例: 渐变色的效果
效果展示的代码:
import tkintertools from math import sin, pi root = tkintertools.Tk('tkinter辅助模块操作说明', '960x540') canvas = tkintertools.Canvas(root, 960, 540) canvas.place(x=0, y=0) for i in range(700): canvas.create_line( (100 + i) * canvas.rate_x, 100 * canvas.rate_x, (100 + i) * canvas.rate_x, 400 * canvas.rate_y, fill=tkintertools.change_color(('#0000FF', '#00FF00'), sin(pi*i/700)), width=2) root.mainloop()
更加逼真的效果:
逼真的渐变色
怎么样?看起来是不是有内味儿了?源代码在下面:
import tkintertools root = tkintertools.Tk('tkinter辅助模块操作说明', '960x540') canvas = tkintertools.Canvas(root, 960, 540, bg='white') canvas.place(x=0, y=0) for i in range(200): color = tkintertools.change_color(('#FFFFFF', '#000000'), i/200) canvas.create_oval(200 - i/2, 200 - i/2, 300 + i, 300 + i, outline=color, width=2) root.mainloop()
又或者说,我们可以用这个渐变色啊,干点好玩的事情……
变幻色按钮
上面骚操作的源代码如下:
import tkintertools from random import randint root = tkintertools.Tk('tkinter辅助模块操作说明', '960x540') canvas = tkintertools.Canvas(root, 960, 540) canvas.place(x=0, y=0) def colorful(ind=0, color=[None, '#F1F1F1']): """ 颜色变幻函数 """ if not ind: color[0], color[1] = color[1], '#%06X' % randint(0, 1 << 24) colorful_button.color_fill[0] = tkintertools.change_color(color, ind) colorful_button.state() # 更新按钮外观 root.after(10, colorful, 0 if ind >= 1 else ind+0.01) colorful_button = tkintertools.CanvasButton( canvas, 100, 100, 120, 30, 0, '颜色变幻', command=colorful) root.mainloop()
后记
看了这么多,你了解到这个辅助模块有多强大了么?它基本上只用原 tkinter 模块(typing 只用来进行类型提示,sys 只用来检测 Python 版本)就做出了这些功能,不知道是否让你满意呢?如果你认为可以,那是否可以为我点一个小小的赞呢?