[python趣味实战]----python动态爱心代码

正文

01-效果演示

        下图是代码运行之后的爱心显示结果:

        下面的视频该爱心是动态效果,较为简洁,如果需要使用,可以进行完善,这里只是一个趣味实战,下面将对代码实现进行非常详细地描述:

动态ai'xin

02-代码实现

        下图是动态爱心代码实现,对所有代码进行详细解释如下:

         import random   // 导入Python中的random模块,用于生成随机数。

        from math import  sin, cos, pi, log   // 从math模块中导入sin、cos、pi和log函数,用于数学计算。

        from tkinter import *    // 从tkinter模块中导入所有内容,用于创建GUI界面。


        CANVAS_WIDTH = 640

        CANVAS_HEIGHT = 480

        CANVAS_CENTER_X = CANVAS_WIDTH / 2

        CANVAS_CENTER_Y = CANVAS_HEIGHT / 2

        // 定义了一些常量:CANVAS_WIDTH, CANVAS_HEIGHT, CANVAS_CENTER_X, CANVAS_CENTER_Y,用于定义画布的大小和中心位置。


        def center_window(root, width, height):  // 定义了一个函数center_window,接受三个参数root(窗口对象),width(窗口宽度),height(窗口高度)

        screenwidth = root.winfo_screenwidth():  // 使用winfo_screenwidth()方法获取屏幕的宽度,并将值赋给screenwidth变量

        screenheight = root.winfo_screenheight():  // 使用winfo_screenheight()方法获取屏幕的高度,并将值赋给screenheight变量

        size = '%dx%d+%d+%d' % (width, height, (screenwidth - width) / 2, (screenheight - height) / 2):  // 构建一个字符串size,其中包括窗口的宽度和高度,以及窗口左上角的位置(居中显示计算)

        root.geometry(size):  // 使用geometry方法将窗口的大小和位置设置为size字符串中描述的值,从而将窗口居中显示在屏幕上


        def heart_function(t, shrink_ratio):  // 定义了一个心形函数heart_function,接受两个参数t(参数值)和shrink_ratio(缩小比率)

        x = 16 * (sin(t) ** 3):   // 计算心形曲线上点的x坐标,根据参数t的正弦值的三次方乘以16来计算

        y = -(13 * cos(t) - 5 * cos(2 * t) - 2 * cos(3 * t) - cos(4 * t)):   // 计算心形曲线上点的y坐标,根据参数t的余弦值的线性组合来计算

        x *= shrink_ratio:   // 将计算出的x坐标乘以缩小比率shrink_ratio,用于对心形曲线进行缩放

        y *= shrink_ratio:   // 将计算出的y坐标乘以缩小比率shrink_ratio,用于对心形曲线进行缩放

        x += CANVAS_CENTER_X:   // 将x坐标移动到画布横向中心位置

        y += CANVAS_CENTER_Y:   // 将y坐标移动到画布纵向中心位置

        return int(x), int(y):   // 返回计算出的x和y坐标,使用int()函数将结果转换为整数类型。这样就得到了在画布上绘制心形图案所需要的坐标


         Heart类用于生成心形图案,初始化时会生成心形曲线上的点,并根据一定的规则进行扩散。生成心形图案后,可以根据指定的帧数进行渲染。下面逐个分析:

        def __init__(self, generate_frame=20):  // 定义了Heart类的初始化方法,接受一个参数generate_frame,默认值为20。在这个方法中进行了一些初始化操作

        self._points = set():   // 创建一个空集合_points,用于存储心形图案的点

        self._edge_diffusion_points = set():   // 创建一个空集合_edge_diffusion_points,用于存储心形图案的边缘扩散点

        self._center_diffusion_points = set():  // 创建一个空集合_center_diffusion_points,用于存储心形图案的中心扩散点

        self.all_points = {}:   // 创建一个空字典all_points,用于存储所有计算出的点

        self.build(2000):   // 调用build方法,传入参数2000,用于构建心形图案

        self.random_halo = 1000:   // 设置random_halo属性为1000,用于表示随机光晕的大小

        self.generate_frame = generate_frame:  // 将传入的generate_frame参数赋值给实例属性generate_frame,表示生成帧数

        for frame in range(generate_frame)::   // 遍历生成帧数的范围

        self.calc(frame):   // 调用calc方法,传入当前帧数frame作为参数,用于计算心形图案的点的位置。通过循环生成帧数次来完整绘制心形图案


        def build(self, number):  // 定义了Heart类中的build方法,接受一个参数number,用于生成指定数量的点

        for _ in range(number)::   // 遍历指定数量的次数

        t = random.uniform(0, 2 * pi):   // 生成一个随机的角度t,范围在0到2π之间

        x, y = heart_function(t, 11):   // 调用之前定义的heart_function函数,传入角度t和缩小比率11,计算得到心形曲线上的点的x和y坐标

        self._points.add((x, y)):   // 将计算出的点的坐标添加到_points集合中,第一个for循环结束后,_points集合中包含了心形曲线上的点

        for _x, _y in list(self._points)::   // 遍历_points集合中的所有点

        for _ in range(3)::   // 嵌套循环,执行3次

        self._edge_diffusion_points.add((x, y)):   // 将当前点加入_edge_diffusion_points集合中,实现边缘扩散

        point_list = list(self._points):   // 将_points集合转换为列表point_list

        for _ in range(4000)::   // 遍历4000次

        x, y = random.choice(point_list):   // 从point_list中随机选择一个点的坐标

        self._center_diffusion_points.add((x, y)):   // 将选定的点作为中心点,加入_center_diffusion_points集合中,实现中心扩散


         @staticmethod:  // 标记下面的calc_position方法为静态方法,即不需要实例化对象也可以直接调用该方法

        def calc_position(x, y, ratio):   // 定义了静态方法calc_position,接受三个参数x(x坐标)、y(y坐标)和ratio(比率)

        force = 1 / (((x - CANVAS_CENTER_X) ** 2 + (y - CANVAS_CENTER_Y) ** 2) ** 0.520):   // 计算力(force),根据点到画布中心的距离的0.520次方来计算

        dx = ratio * force * (x - CANVAS_CENTER_X) + random.randint(-1, 1):   // 计算x方向上的偏移量,根据比率、力和距离画布中心的偏移,再加上一个随机值

        dy = ratio * force * (y - CANVAS_CENTER_Y) + random.randint(-1, 1):   // 计算y方向上的偏移量,同样根据比率、力和距离画布中心的偏移,再加上一个随机值

        return x - dx, y - dy:   // 返回新计算出的x和y坐标,通过减去对应的偏移量,实现点的位置更新

        这个静态方法calc_position用于根据点与画布中心的距离,以及指定的比率,计算点的新位置偏移量,从而实现点的移动。同时添加了一些随机性以增加点的变化


        def calc(self, generate_frame):  // 定义了Heart类中的calc方法,接受一个参数generate_frame,用于计算心形图案的点的位置

        ratio = 10:   // 设置比率为10

        halo_radius = int(4 + 6):   // 计算光晕的半径为10

        halo_number = int(3000 + 4000):   // 设置生成的光晕点的数量在7000左右

        all_points = []:   // 创建一个空列表all_points,用于存储所有计算出的点

        heart_halo_point = set():   // 创建一个空集合heart_halo_point,用于存储心形光晕点

        第一个循环用于生成心形光晕点,并添加到all_points列表中。随机选取角度t,计算心形曲线上的点坐标,加入heart_halo_point集合,加入随机偏移,选择大小,将点信息加入all_points列表

        第二个循环遍历心形曲线上的点,通过调用calc_position方法计算新的位置坐标,加入all_points列表

        第三个循环遍历边缘扩散点,同样通过调用calc_position方法计算新的位置坐标,加入all_points列表

        self.all_points[generate_frame] = all_points:   // 将生成的所有点信息存储在all_points字典中,以当前帧数generate_frame作为键值

        通过这些步骤,calc方法计算了心形图案上的点的新的位置,并将结果存储在all_points字典中


         def render(self, render_canvas, render_frame):  // 定义了Heart类中的render方法,接受两个参数render_canvas(用于绘制点的画布)和render_frame(当前帧数)

        for x, y, size in self.all_points[render_frame % self.generate_frame]::   // 遍历all_points字典中与当前帧数对应的点的信息

        alpha = int(((render_frame % self.generate_frame) / self.generate_frame) * 255):   // 计算当前帧数对总帧数取余再除以总帧数后的比例,并将其转换为alpha值(0-255范围)

        color = f"#{alpha:02X}0000":   // 根据计算得到的alpha值生成颜色字符串,格式为RGBA中的R=alpha,G=0,B=0,表示红色

        render_canvas.create_rectangle(x, y, x + size, y + size, width=0, fill=color):   // 在绘制点的画布上创建一个矩形,以(x, y)为左上角坐标,(x+size, y+size)为右下角坐标,填充颜色为之前计算得到的红色


        root = Tk():  // 创建一个Tkinter应用程序的根窗口对象root

        root.title("浪漫爱心"):   // 设置窗口的标题为"浪漫爱心"

        center_window(root, CANVAS_WIDTH, CANVAS_HEIGHT):   // 调用center_window函数来将窗口居中显示在屏幕上,参数包括窗口对象root以及指定的画布宽度和高度

        canvas = Canvas(root, bg='black', height=CANVAS_HEIGHT, width=CANVAS_WIDTH):   // 在窗口root上创建一个Canvas对象canvas,背景颜色为黑色,指定画布的高度和宽度为预定义的值CANVAS_HEIGHT和CANVAS_WIDTH

        canvas.pack():   // 将Canvas对象canvas放置到窗口中进行显示

        heart = Heart():   // 创建一个Heart类的实例对象heart,用于生成心形图案并进行后续的计算和渲染

        通过以上代码,创建了一个Tkinter窗口并在窗口中显示了一个黑色背景的Canvas画布,同时生成了一个Heart类的实例对象heart,准备后续使用该对象进行心形图案的绘制和动画效果展示。


        def draw(main, render_canvas, render_heart, render_frame=0):  // 定义了一个名为draw的函数,接受四个参数,分别为main(主窗口对象),render_canvas(用于绘制的画布对象),render_heart(Heart类实例对象),render_frame(当前帧数,默认为0)

        render_canvas.delete('all'):   // 在每次绘制之前,清空画布上的所有内容

        render_heart.render(render_canvas, render_frame):   // 调用Heart类中的render方法,将当前帧数和绘制的画布传入,用于绘制心形图案上的点

        main.after(300, draw, main, render_canvas, render_heart, render_frame + 1):   // 使用Tkinter中的after方法,设置300毫秒后执行draw函数,实现动画效果。同时将main窗口对象、render_canvas画布对象、render_heart对象以及更新后的render_frame作为参数传入

        Label(root, text="I Love You", bg="black", fg="#FF0000", font="Helvetic 20 bold").place(relx=.5, rely=.5, anchor=CENTER):   // 创建一个标签对象,显示文本"I Love You",设置背景颜色为黑色,前景色为红色,字体为Helvetica 20粗体,在窗口中居中显示

        root.mainloop():   // 进入Tkinter的主事件循环,等待响应用户事件,保持窗口显示

        通过以上代码,实现了一个动画效果,不断更新心形图案的点的位置并在画布上绘制,同时在窗口中显示 “I Love You” 的标签文本。整个程序会持续运行,直到用户关闭窗口。

import random   
from math import sin, cos, pi, log   
from tkinter import *
CANVAS_WIDTH = 640
CANVAS_HEIGHT = 480
CANVAS_CENTER_X = CANVAS_WIDTH / 2
CANVAS_CENTER_Y = CANVAS_HEIGHT / 2
def center_window(root, width, height):
    screenwidth = root.winfo_screenwidth()  
    screenheight = root.winfo_screenheight()  
    size = '%dx%d+%d+%d' % (width, height, (screenwidth - width) / 2, (screenheight - height) / 2)  
    root.geometry(size)  
def heart_function(t, shrink_ratio):
    x = 16 * (sin(t) ** 3)
    y = -(13 * cos(t) - 5 * cos(2 * t) - 2 * cos(3 * t) - cos(4 * t))
    x *= shrink_ratio
    y *= shrink_ratio
    x += CANVAS_CENTER_X
    y += CANVAS_CENTER_Y
    return int(x), int(y)
class Heart:
    def __init__(self, generate_frame=20):
        self._points = set()  
        self._edge_diffusion_points = set()  
        self._center_diffusion_points = set()  
        self.all_points = {}  
        self.build(2000)
        self.random_halo = 1000
        self.generate_frame = generate_frame
        for frame in range(generate_frame):
            self.calc(frame)
    def build(self, number):
        for _ in range(number):
            t = random.uniform(0, 2 * pi)
            x, y = heart_function(t, 11)
            self._points.add((x, y))
        for _x, _y in list(self._points):
            for _ in range(3):
                x, y = _x, _y
                self._edge_diffusion_points.add((x, y))
        point_list = list(self._points)
        for _ in range(4000):
            x, y = random.choice(point_list)
            self._center_diffusion_points.add((x, y))
    @staticmethod
    def calc_position(x, y, ratio):
        force = 1 / (((x - CANVAS_CENTER_X) ** 2 + (y - CANVAS_CENTER_Y) ** 2) ** 0.520)
        dx = ratio * force * (x - CANVAS_CENTER_X) + random.randint(-1, 1)
        dy = ratio * force * (y - CANVAS_CENTER_Y) + random.randint(-1, 1)
        return x - dx, y - dy
    def calc(self, generate_frame):
        ratio = 10
        halo_radius = int(4 + 6)
        halo_number = int(3000 + 4000)
        all_points = []
        heart_halo_point = set()
        for _ in range(halo_number):
            t = random.uniform(0, 2 * pi)
            x, y = heart_function(t, 11.6)
            x, y = x, y
            if (x, y) not in heart_halo_point:
                heart_halo_point.add((x, y))
                x += random.randint(-14, 14)
                y += random.randint(-14, 14)
                size = random.choice((1, 2, 2))
                all_points.append((x, y, size))
        for x, y in self._points:
            x, y = self.calc_position(x, y, ratio)
            size = random.randint(1, 3)
            all_points.append((x, y, size))
        for x, y in self._edge_diffusion_points:
            x, y = self.calc_position(x, y, ratio)
            size = random.randint(1, 2)
            all_points.append((x, y, size))
        self.all_points[generate_frame] = all_points
    def render(self, render_canvas, render_frame):
        for x, y, size in self.all_points[render_frame % self.generate_frame]:
            alpha = int(((render_frame % self.generate_frame) / self.generate_frame) * 255)
            color = f"#{alpha:02X}0000"
            render_canvas.create_rectangle(x, y, x + size, y + size, width=0, fill=color)
root = Tk()
root.title("浪漫爱心")
center_window(root, CANVAS_WIDTH, CANVAS_HEIGHT)  
canvas = Canvas(root, bg='black', height=CANVAS_HEIGHT, width=CANVAS_WIDTH)
canvas.pack()
heart = Heart()
def draw(main, render_canvas, render_heart, render_frame=0):
    render_canvas.delete('all')
    render_heart.render(render_canvas, render_frame)
    main.after(300, draw, main, render_canvas, render_heart, render_frame + 1)
draw(root, canvas, heart, 0)
Label(root, text="I Love You", bg="black", fg="#FF0000", font="Helvetic 20 bold").place(
    relx=.5, rely=.5, anchor=CENTER)
root.mainloop()