【进阶篇】python使用tkinter实现透明窗体上绘制运动小球(第三篇)

我一直在思索实现一个透明的窗体,然后可以基于这个窗体可以开发出各种好玩的应用(如桌面运动的小人、运动的小球、截图、录制GIF等等),今天无意间居然让我把其中一个最核心的技术难关突破了,我非常高兴,也借这个机会跟大家分享一下。

这一期,在前面两期的基础上,进一步介绍python使用tkinter实现透明窗体上绘制运动小球。进阶了!

废话不多说,直接开始。

文章目录

  • 一、先上效果吧
  • 二、需求分析
    • (一)先搞清楚角色
    • (二)再搞清楚技术核心点
    • (三)继续搞清楚需要的类库
  • 三、设计原则
    • (一)类化
    • (二)模块化
    • (三)迭代优化
  • 四、核心功能模块
    • (一)运动小球实现
      • 1、运动小球环境参数初始化
      • 2、运动小球功能参数初始化
      • 3、运动小球运动函数
      • 4、运动小球类完整代码
    • (二)主界面实现
      • 1、实现最简单的窗体
      • 2、把灰色设置成透明色
      • 3、放置一个矩形框在canvas上。
      • 4、让透明窗体不断重画的onsize函数
      • 5、绑定onsize函数
      • 6、最终的效果
    • (三)主界面和运动函数的交互实现
      • 1、创建多个ball,并存储在list列表里
      • 2、实现多个小球的运动函数,并在main函数中调用
      • 3、主函数的整体代码
  • 五、初步的运行效果
  • 六、更多参数的优化
    • (一)去掉标题栏
    • (二)置顶显示
    • (三)把旁边的白边去掉
  • 七、最终运行的效果

一、先上效果吧


完全透明的窗体,在桌面最前端显示一堆运动的小球,这些小球按照有规则的运动模式进行运动。

二、需求分析

(一)先搞清楚角色

1、有运动的小球,而且运动的小球有不同的颜色、大小、运动方向、速度等。
2、有主界面,也就是我们的屏幕,这个屏幕不断更新数据并展现,也就是说屏幕在不停的重画重画。

(二)再搞清楚技术核心点

1、需要绘制透明的窗口,整个窗口是透明的,而运动的小球是有不同的颜色的。
2、每个小球的随机运动函数,也是一个非常难的技术点,譬如发生了边界碰撞后需要改变方向、其特性的随机性等等。

(三)继续搞清楚需要的类库

1、实现窗口,使用python自带的tkinter库;
2、实现随机性,使用ptthon自带的random随机函数库

三、设计原则

(一)类化

为了代码简洁、易管理、易懂

(二)模块化

所有技术细节均封装、无需关注实现细节,只关注如何调用、如何使用,各司其职、各尽其职。

(三)迭代优化

每个产品都是需要迭代的、无论商用与否,看我们如何思考和优化,精益求精。

四、核心功能模块

(一)运动小球实现

1、运动小球环境参数初始化

    def __init__(self, canvas,root):
        self.canvas = canvas
        self.root = root

2、运动小球功能参数初始化

    def create_ball(self):
        # tkinter绘图采用屏幕坐标系,原点在左上角,x从左往右递增,y从上往下递增
        # 随机产生表示当前球的大小,也就是半径长度
        self.radius = randint(2, 10)

        self.scrnwidth = int(self.root.winfo_width())
        self.scrnheight = int(self.root.winfo_height())

        # 在绘图区域内,随机产生当前球的圆心的x坐标和y坐标,用于制定出现的位置
        self.xpos = randint(self.radius, int(self.root.winfo_width())-self.radius)
        self.ypos = randint(self.radius, int(self.root.winfo_height())-self.radius)


        # 在绘图区域内,随机产生当前球的x坐标和y坐标的向量
        # 在数学中,几何向量(也称矢量),指具有大小和方向的量
        # 这里我们可以用来表示球的速度
        self.xvelocity = randint(2, 6)
        self.yvelocity = randint(2, 6)

        # 通过lambda表达式创建函数对象r,每次调用r()都会产生0~255之间的数字
        r = lambda: randint(0, 255)

        # 三次调用的数字取前两位,用十六进制数方式存储到self.color里,作为球的颜色
        # RRGGBB,前2是红色,中2是绿色,后2是蓝色,最小是0,最大是F

        # 如全黑#000000  全白#FFFFFF  全红#FF0000
        self.color = "#%02x%02x%02x" % (r(), r(), r())

        # canvas.create_oval可以绘制一个圆
        # 但是需要传入圆的左、上、右、下四个坐标
        # 所以我们先产生4个坐标,通过这个四个坐标,绘制圆的大小

        # 左坐标=x坐标-半径
        x1 = self.xpos - self.radius
        # 上坐标=y坐标-半径
        y1 = self.ypos - self.radius
        # 右坐标=x坐标+半径
        x2 = self.xpos + self.radius
        # 下坐标=y坐标+半径
        y2 = self.ypos + self.radius

        # 通过canvas.create_oval()方法绘出整个圆,填充色和轮廓色分别是self.color生成的颜色
        # self.canvas.delete('ball')
        self.ball = self.canvas.create_oval(x1, y1, x2, y2, fill=self.color, outline=self.color,tag='ball')
        # canvas.addtag_all('t5')
        print(x1,y1,x2,y2,self.color,self.radius)

3、运动小球运动函数

    def move_ball(self):
        # 进行相应的移动,如果坐标超过屏幕边缘则向相反方向移动
        # 让球的x坐标和y坐标,按照向量的大小进行增加,表示球的运行,向下和向右

        self.scrnwidth = int(self.root.winfo_width())
        self.scrnheight = int(self.root.winfo_height())

        self.xpos += self.xvelocity
        self.ypos += self.yvelocity

        # 如果球的y坐标大于等于屏幕高度和球的半径的差,则调整球的运行y轴方向朝上
        if self.ypos >= self.scrnheight - self.radius:
            self.yvelocity = -self.yvelocity

        # 如果球的y坐标小于等于屏幕高度和球的半径的差,则调整球的y轴运行方向朝下
        if self.ypos <= self.radius:
            self.yvelocity = abs(self.yvelocity)

        # 如果球的x坐标大于等于屏幕宽度和球的半径差,则调整球的运行x轴方向朝左
        if self.xpos >= self.scrnwidth - self.radius:
            self.xvelocity = -self.xvelocity

        # 如果球的x坐标小于等于屏幕宽度和球半径的差,则调整球的运行x轴方向朝右
        if self.xpos <= self.radius:
            self.xvelocity = abs(self.xvelocity)

        # 调整canvas对象的move()方法可以让对象动起来,以及对象x轴和y轴的向量大小
        self.canvas.move(self.ball, self.xvelocity, self.yvelocity)

        # self.canvas.after(10, self.move_ball)

4、运动小球类完整代码


class Ball:

    def __init__(self, canvas,root):
        self.canvas = canvas
        self.root = root

    def create_ball(self):
        # tkinter绘图采用屏幕坐标系,原点在左上角,x从左往右递增,y从上往下递增
        # 随机产生表示当前球的大小,也就是半径长度
        self.radius = randint(2, 10)

        self.scrnwidth = int(self.root.winfo_width())
        self.scrnheight = int(self.root.winfo_height())

        # 在绘图区域内,随机产生当前球的圆心的x坐标和y坐标,用于制定出现的位置
        self.xpos = randint(self.radius, int(self.root.winfo_width())-self.radius)
        self.ypos = randint(self.radius, int(self.root.winfo_height())-self.radius)


        # 在绘图区域内,随机产生当前球的x坐标和y坐标的向量
        # 在数学中,几何向量(也称矢量),指具有大小和方向的量
        # 这里我们可以用来表示球的速度
        self.xvelocity = randint(2, 6)
        self.yvelocity = randint(2, 6)

        # 通过lambda表达式创建函数对象r,每次调用r()都会产生0~255之间的数字
        r = lambda: randint(0, 255)

        # 三次调用的数字取前两位,用十六进制数方式存储到self.color里,作为球的颜色
        # RRGGBB,前2是红色,中2是绿色,后2是蓝色,最小是0,最大是F

        # 如全黑#000000  全白#FFFFFF  全红#FF0000
        self.color = "#%02x%02x%02x" % (r(), r(), r())

        # canvas.create_oval可以绘制一个圆
        # 但是需要传入圆的左、上、右、下四个坐标
        # 所以我们先产生4个坐标,通过这个四个坐标,绘制圆的大小

        # 左坐标=x坐标-半径
        x1 = self.xpos - self.radius
        # 上坐标=y坐标-半径
        y1 = self.ypos - self.radius
        # 右坐标=x坐标+半径
        x2 = self.xpos + self.radius
        # 下坐标=y坐标+半径
        y2 = self.ypos + self.radius

        # 通过canvas.create_oval()方法绘出整个圆,填充色和轮廓色分别是self.color生成的颜色
        # self.canvas.delete('ball')
        self.ball = self.canvas.create_oval(x1, y1, x2, y2, fill=self.color, outline=self.color,tag='ball')
        # canvas.addtag_all('t5')
        print(x1,y1,x2,y2,self.color,self.radius)


    def move_ball(self):
        # 进行相应的移动,如果坐标超过屏幕边缘则向相反方向移动
        # 让球的x坐标和y坐标,按照向量的大小进行增加,表示球的运行,向下和向右

        self.scrnwidth = int(self.root.winfo_width())
        self.scrnheight = int(self.root.winfo_height())

        self.xpos += self.xvelocity
        self.ypos += self.yvelocity

        # 如果球的y坐标大于等于屏幕高度和球的半径的差,则调整球的运行y轴方向朝上
        if self.ypos >= self.scrnheight - self.radius:
            self.yvelocity = -self.yvelocity

        # 如果球的y坐标小于等于屏幕高度和球的半径的差,则调整球的y轴运行方向朝下
        if self.ypos <= self.radius:
            self.yvelocity = abs(self.yvelocity)

        # 如果球的x坐标大于等于屏幕宽度和球的半径差,则调整球的运行x轴方向朝左
        if self.xpos >= self.scrnwidth - self.radius:
            self.xvelocity = -self.xvelocity

        # 如果球的x坐标小于等于屏幕宽度和球半径的差,则调整球的运行x轴方向朝右
        if self.xpos <= self.radius:
            self.xvelocity = abs(self.xvelocity)

        # 调整canvas对象的move()方法可以让对象动起来,以及对象x轴和y轴的向量大小
        self.canvas.move(self.ball, self.xvelocity, self.yvelocity)

        # self.canvas.after(10, self.move_ball)


(二)主界面实现

1、实现最简单的窗体

from tkinter import *

if __name__ == '__main__':

    tk = Tk()
    tk.geometry('500x400+500+150')
    tk.title('有趣的透明窗体-开篇了!!!')
    canvas = Canvas(tk)
    canvas.pack(fill=BOTH, expand=Y)
    tk.mainloop()

太简单了,不详细说了,相信大家都看得懂。

2、把灰色设置成透明色

TRANSCOLOUR = 'gray'
tk.wm_attributes('-transparentcolor', TRANSCOLOUR)

3、放置一个矩形框在canvas上。

canvas.create_rectangle(0, 0, canvas.winfo_width(), canvas.winfo_height(), fill=TRANSCOLOUR, outline=TRANSCOLOUR)

4、让透明窗体不断重画的onsize函数

def on_resize(evt):
    tk.configure(width=evt.width,height=evt.height)
    canvas.create_rectangle(0, 0, canvas.winfo_width(), canvas.winfo_height(), fill=TRANSCOLOUR, outline=TRANSCOLOUR)
    print(canvas.winfo_width())

5、绑定onsize函数

tk.bind('', on_resize)

6、最终的效果

哈哈,透明主界面效果出来了。
在这里插入图片描述

(三)主界面和运动函数的交互实现

1、创建多个ball,并存储在list列表里

   balls = []
    num = 20
    for i in range(num):
        ball = Ball(canvas, tk)
        ball.create_ball()
        balls.append(ball)

这里初始化20个小球,你可以初始化1个,也可以初始化更多个。

2、实现多个小球的运动函数,并在main函数中调用

def move_balls():
    for ball in balls:
        ball.move_ball()
    canvas.after(20, move_balls)

move_balls()

3、主函数的整体代码


if __name__ == '__main__':

    tk = Tk()
    tk.geometry('600x500+300+100')
    tk.title('有趣的透明窗体-开篇了!!!')
    tk.bind('', on_resize)

    canvas = Canvas(tk)
    canvas.pack(fill=BOTH, expand=Y)
    tk.update()

    balls = []
    num = 20
    for i in range(num):
        ball = Ball(canvas, tk)
        ball.create_ball()
        balls.append(ball)

    move_balls()

    tk.mainloop()

五、初步的运行效果

好吧,后续我会分享这部分的完整代码给各位,希望大家不是拿来主义,能点点赞、关注一下我啊。

六、更多参数的优化

(一)去掉标题栏

tk.overrideredirect(1)

(二)置顶显示

tk.wm_attributes("-topmost", 1)

(三)把旁边的白边去掉

canvas.config(highlightthickness=0)

呵呵,窗体最后就变成光秃秃的了。

七、最终运行的效果


代码量不大,效果还行。
再深入研究可以有更多更有趣的应用。

分享是一种快乐,希望我们一起快乐,我走过的坎、尝试过的bug,希望你们可以不用再尝试。
对代码有兴趣的朋友们,欢迎关注和点赞,后续我会分享这部分的完整代码给各位,希望大家不是拿来主义,能点点赞、关注一下我啊。
欢迎持续关注,比心。

你可能感兴趣的:(tkinter桌面专栏,python,tkinter,游戏)