我一直在思索实现一个透明的窗体,然后可以基于这个窗体可以开发出各种好玩的应用(如桌面运动的小人、运动的小球、截图、录制GIF等等),今天无意间居然让我把其中一个最核心的技术难关突破了,我非常高兴,也借这个机会跟大家分享一下。
这一期,在前面两期的基础上,进一步介绍python使用tkinter实现透明窗体上绘制运动小球。进阶了!
废话不多说,直接开始。
完全透明的窗体,在桌面最前端显示一堆运动的小球,这些小球按照有规则的运动模式进行运动。
1、有运动的小球,而且运动的小球有不同的颜色、大小、运动方向、速度等。
2、有主界面,也就是我们的屏幕,这个屏幕不断更新数据并展现,也就是说屏幕在不停的重画重画。
1、需要绘制透明的窗口,整个窗口是透明的,而运动的小球是有不同的颜色的。
2、每个小球的随机运动函数,也是一个非常难的技术点,譬如发生了边界碰撞后需要改变方向、其特性的随机性等等。
1、实现窗口,使用python自带的tkinter库;
2、实现随机性,使用ptthon自带的random随机函数库
为了代码简洁、易管理、易懂
所有技术细节均封装、无需关注实现细节,只关注如何调用、如何使用,各司其职、各尽其职。
每个产品都是需要迭代的、无论商用与否,看我们如何思考和优化,精益求精。
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)
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)
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()
太简单了,不详细说了,相信大家都看得懂。
TRANSCOLOUR = 'gray'
tk.wm_attributes('-transparentcolor', TRANSCOLOUR)
canvas.create_rectangle(0, 0, canvas.winfo_width(), canvas.winfo_height(), fill=TRANSCOLOUR, outline=TRANSCOLOUR)
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())
tk.bind('' , on_resize)
哈哈,透明主界面效果出来了。
balls = []
num = 20
for i in range(num):
ball = Ball(canvas, tk)
ball.create_ball()
balls.append(ball)
这里初始化20个小球,你可以初始化1个,也可以初始化更多个。
def move_balls():
for ball in balls:
ball.move_ball()
canvas.after(20, move_balls)
move_balls()
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,希望你们可以不用再尝试。
对代码有兴趣的朋友们,欢迎关注和点赞,后续我会分享这部分的完整代码给各位,希望大家不是拿来主义,能点点赞、关注一下我啊。
欢迎持续关注,比心。