《哪咤学python进阶篇》之选学案例三:白桦林的故事_(Python多媒体MV)

看完这篇博文你一定会大有收获。在这个细分的Python领域,本人已经走在了全国最前沿。这是用turtle模块制作的多媒体案例。本人制作的东西太多,今天发一个大招。本篇摘自本人撰写的《哪咤学python进阶篇》之选学案例三:白桦林的故事.py。

有一首著名的歌叫《白桦林》,讲的是一个凄美的爱情故事。以下我们先来欣赏一下歌词,如下所示:

静静的村庄飘着白的雪

 阴霾的天空下鸽子飞翔

 白桦树刻着那两个名字

 他们发誓相爱用尽这一生

 有一天战火烧到了家乡

 小伙子拿起枪奔赴边疆

 心上人你不要为我担心

 等着我回来在那片白桦林

 天空依然阴霾依然有鸽子在飞翔

 谁来证明那些没有墓碑的爱情和生命

 雪依然在下那村庄依然安详

 年轻的人们消失在白桦林

 噩耗声传来在那个午后

 心上人战死在远方沙场

 她默默来到那片白桦林

 望眼欲穿地每天守在那里

 长长的路呀就要到尽头

 那姑娘已经是白发苍苍

 她时常听他在枕边呼唤

 来吧亲爱的,来这片白桦林

 天空依然阴霾依然有鸽子在飞翔

 谁来证明那些没有墓碑的爱情和生命

 雪依然在下那村庄依然安详

 年轻的人们消失在白桦林

 在死的时候她喃喃地说

 我来了,等着我在那片白桦林

本案例是把这首歌播放出来,并且同时显示歌词。为了渲染气氛,加了一个阴天般的图片和粒子效果。粒子效果用的是很多小白点从屏幕随机往下移动。每个粒子都继承自海龟类。它们的形状都是dot,不过dot并不是海龟画图自带的形状。由于用circle形状也无法缩到最小的形状,所以作者自定义了一个最小的形状。它的顶点坐标为((0,0),(0,0))。 这个作品最关键的是在唱歌的时候歌词要同步显示。 所以就有了歌词文件的概念。它记录了每句歌词应该在哪个时间显示。以下是歌词文件里的两行。

[00:26.92]静静的村庄飘着白的雪

[00:33.27]阴霾的天空下鸽子飞翔

在上面的中括号里冒号前面的是分钟,后面是秒。在点号后面是毫秒的10倍值。我们可以利用time模块获取时间,当到达了某个时间点就显示那句歌词。这是完全可以的,显示歌词用一只海龟对象的write方法即可。不过在本作品中并没有采用上述所说的方法。为了配合ontimer命令的用法。我们把歌词文件的内容进行转换,主要是把中括号中的时间转换成了歌词应该显示多长时间,当然,并没有保存到磁盘,它只是在内存里。这是由musicwithlrc模块里make_septime_lrc函数完成的。它返回septime_lrc这个列表。这个列表的每一项是一个二元组。它如下图右边文字所示:

python创意多媒体MV白桦林的故事作者李兴球

右边的10000, '白桦林'这句歌词就表示'白桦林'这三个字要显示10秒。算法是很简单的,只是把左图中第二行的时间减去上一行的时间。具体表现在make_septime_lrc函数中的这一句:sep = next_time - current_time。

以下是musicwithlrc模块的内容:

"""musicwithlrc.py。本程序定义两个函数,返回唱词的毫秒间隔时间和歌词。"""

__author__ = "李兴球"
__date__ = "2019/2/20"

def convert_to_msecond(line):
    """把lrc歌词文件中里中括号里的唱点时间转换成毫秒值,
    即把'[00:05.25]歌词'这样的转换成(650,歌词),以元组形式返回。"""
     
    sentence = line.split("]")[-1]            # 歌词    
    time = line.split("]")[0]                 # 中括号里的时间     
    time = time[1:]                           # 下面把00:10.00这样的转换成毫秒
    items = time.split(".")
    items = [item.strip() for item in items]  # 剥皮处理      
    ps = int(items[-1]) * 10                  # 毫秒 
    time = items[0]                           # 01:33形式的时间
    se = int(time.split(":")[-1]) * 1000      # 秒转换成毫秒
    mi = int(time.split(":")[0]) * 60 * 1000  # 分转换成毫秒
    return (mi + se + ps,sentence)

def make_septime_lrc(lrcfile):
    """生成歌词的间隔时间表,给海龟画图的ontimer用,返回列表,列表中的项目是二元组。
    二元组的内容是歌词显示的时间和歌词。    """
    
    septime_lrc = []                        # 存放每行应该显示的时间和歌词
    fc = []                                 # 存放每一行
    f = open(lrcfile,encoding='utf-8')
    for line in f:
        line = line.strip()
        if len(line)>10:fc.append(line)
    f.close()    
    amounts = len(fc)
    # 第一行的内容,它的歌词显示时间为第二行的时间减去它的时间
    current_items = convert_to_msecond(fc[0])# 唱到当前行的时间和歌词
    for index in range(amounts-1):           # 最后一行不需要显示时间   
        next_line = fc[index+1]              # 下一行文件内容            
        next_items = convert_to_msecond(next_line) # 唱到下一行的时间和歌词
        current_time = current_items[0]      # 唱到此行的毫秒数
        next_time = next_items[0]            # 唱到下一行的毫秒数
        sep = next_time - current_time       # 当前行应该显示的毫秒数
        septime_lrc.append((sep,current_items[1].strip()))
        current_items = next_items 
    return septime_lrc

if __name__ == "__main__":

    lrc_for_ontimer = make_septime_lrc("白桦林.lrc")
    for lrc in lrc_for_ontimer:
        print(lrc)
                            

上面的函数通过from musicwithlrc import * 被导入到白桦林的故事.py程序文件中。 在主程序中定义了名为sing的函数。它的参数为wav音乐文件名和歌词文件名。由它来播放音乐和同步显示歌词。同步显示歌词是在sing函数内再定义一个名为display_lrc的无参函数,由它调用屏幕的ontimer功能循环显示歌词。

在播放音乐的时候还要同步模拟下雪效果。这是通过首先定义一个叫Snow的类,然后由它实例化一些对象,具体由这些白色的小小粒子不断地从上到下移动而实现的。经过前面的学习,相信读者能读懂Snow类。

本案例新的知识是关于tkinter的。turtle模块是用tkinter开发的。在turtle.py里定义了_Root类。它继承自Tk类。当海龟画图启动的时候就是由它来实例化一个窗口的。以下是turtle.py中_Root类的源代码(中文注释为本书作者所加):

class _Root(TK.Tk):
    """Root class for Screen based on Tkinter."""
    def __init__(self):
        TK.Tk.__init__(self)             # 生成窗口

    def setupcanvas(self, width, height, cwidth, cheight):
        self._canvas = ScrolledCanvas(self, width, height, cwidth, cheight)                                          # 新建滚动画布对象
        self._canvas.pack(expand=1, fill="both")  # 放置画布

    def _getcanvas(self):
        return self._canvas

    def set_geometry(self, width, height, startx, starty):
        self.geometry("%dx%d%+d%+d"%(width, height, startx, starty))

def ondestroy(self, destroy):
    """使用协议机制来定义关闭窗口时触发的事件"""
        self.wm_protocol("WM_DELETE_WINDOW", destroy) # 关闭窗口事件

    def win_width(self):
        return self.winfo_screenwidth()

    def win_height(self):
        return self.winfo_screenheight()                                      

在turtle.py中有_Screen类,它有一个属性叫_root。而这个_root就是根窗口。所以在海龟画图中用screen._root就能直接访问窗口对象。下面是白桦林的故事.py的源代码,相信读者经过上面的阅读配合注释就能看懂代码。

"""白桦林的故事.py,本程序新建Snow类生成粒子效果做为雪花飘落。
名为sing的函数生成一只海龟,用来在屏幕上显示歌词并播放音乐。"""

__author__ = "李兴球"
__date__   = "2019/2/20"

from turtle import Turtle,Screen
from random import randint,choice
from musicwithlrc import *
from winsound import PlaySound,SND_ASYNC
from tkinter import messagebox

def sing(filename,srcfilename):
    """播放音乐并显示歌词"""
    ziti = ("楷体",22,"normal")                 # 字体样式 
    time_and_lrc = make_septime_lrc(srcfilename)# 时间和歌词
    lrc_amounts = len(time_and_lrc)             # 歌词数量 
    writer = Turtle(visible=False)              # 此作者是海龟
    writer.penup()                              # 抬笔
    writer.sety(30)                             # 设定y坐标
    writer.color("blue")                        # 字的颜色
    PlaySound(filename,SND_ASYNC)               # 播放音乐 
    lrc_index = 0                               # 歌词索引从0开始
     
    def display_lrc():                          # 定义显示歌词函数
        """从列表获取歌词显示出来"""
        nonlocal lrc_index                      # 非本地变量
        if lrc_index < lrc_amounts:             # 索引小于数量则显示歌词
            septime,lrc = time_and_lrc[lrc_index]
            writer.clear()                      # 写新歌词前先擦掉
            writer.write(lrc,align='center',font=ziti) # 显示歌词
            writer.screen.ontimer(display_lrc,septime) # 时间到显示下一句
            lrc_index = lrc_index + 1            # 索引加1 
    display_lrc()        
        
class Snow(Turtle): 
    def __init__(self):        
        Turtle.__init__(self,shape='dot',visible=False)
        self.penup()
        self.color("white")                       # 雪花颜色
        self.sh = self.screen.window_height()     # 屏幕高度
        self.sw = self.screen.window_width()      # 屏幕宽度
        self.init()
        
    def init(self):
        self.ht()                                 # 隐藏自己
        x = randint(-self.sw//2,self.sw//2)       # 设置x坐标
        y = randint(self.sh,self.sh*2)            # 设置y坐标
        self.goto(x,y)                            # 坐标定位
        self.xspeed = randint(-1,1)               # 横向速度
        self.yspeed = randint(-2,-1)              # 垂直速度
        self.st()                                 # 显示自己
        
    def move(self):
        """根据x和y速度移动"""
        x = self.xcor() + self.xspeed   # 水平坐标增加横向速度
        y = self.ycor() + self.yspeed   # 垂直坐标增加垂直速度   
        self.goto(x,y)         
        if y < -self.sh//2 :            # 到屏幕最低就重新init
            self.init()
            
def close_window():
    """单击屏幕把running设为False,这样while循环就结束了"""
    global running    
    if messagebox.askokcancel("白桦林", "你真的要离开吗?"):
        running = False
    
if __name__ == "__main__":
    
    counter = 0                         # 计数器变量
    amounts = 150                       # 设定粒子总数
    width,height = 480,360              # 定义屏幕宽高
    running = True                      # 运行标志
    
    screen = Screen()                   #  新建屏幕
    screen.addshape("dot",((0,0),(0,0)))# 设定dot形状
    screen.setup(width,height)          # 设定屏幕宽高
    screen.bgcolor("black")             # 设定屏幕背景
    screen.bgpic("bg.png")              # 设定背景图片
    screen.title("白桦林的故事_程序制作:李兴球")# 设定标题   
    screen.delay(0)                     # 设定屏幕延时
    
    screen.onclick(lambda x,y:close_window()) # 单击屏幕关窗
    root = screen._root                 # _root继承自Tk类
    # 下一句表示当按窗口关闭按钮时调用close_window函数
    root.ondestroy(close_window)     
    
    sing("白桦林.wav","白桦林.lrc")     # 显示歌词放音乐    
    ps = []                             # 定义粒子列表
    while running:                      # 进入循环
        if counter < amounts:           # 未达指定数量
           p = Snow()                   # 生成雪花粒子
           ps.append(p)                 # 添加到列表
           counter += 1                 # 计数器加一                             
        [p.move() for p in ps]          # 移动每个粒子
        screen.update()                 # 屏幕刷新重画
    root.destroy()                      # 销毁窗口
    

    

无图无真相,要看作品的显示内容,请加本人抖音号即可!13507998321. ,在手机号后面有一个点!本人欲打造原创博客,域名就是我的名字lixingqiu.com。你可以收藏一下,敬请期待。

 

 

 

 

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
├ │ ├ │ │ ├01 python s14 day1 开课介绍.avi │ │ ├02 python s14 day1 开课介绍2.avi │ │ ├03 python s14 day1 开课介绍3.avi │ │ ├04 python s14 day1 开课介绍4.avi │ │ ├05 python s14 day1 python发展介绍.avi │ │ ├06 python s14 day1 第一个python程序.avi │ │ ├07 python s14 day1 变量.avi │ │ ├08 python s14 day1 字符编码与二制.avi │ │ ├09 python s14 day1 字符编码的区别与介绍.avi │ │ ├10 python s14 day1 用户交互程序.avi │ │ ├11 python s14 day1 if else流程判断.avi │ │ ├12 python s14 day1 while 循环.avi │ │ ├13 python s14 day1 while 循环优化版本.avi │ │ ├14 python s14 day1 for 循环及作业要求.avi │ │ └day1.zip │ ├ │ │ ├01 python s14 day2 本节鸡汤.avi │ │ ├02 python s14 day2 模块初识.avi │ │ ├03 python s14 day2 模块初识2.avi │ │ ├04 python s14 day2 pyc是什么.avi │ │ ├05 python s14 day2 python数据类型.avi │ │ ├06 python s14 day2 bytes数据类型.avi │ │ ├07 python s14 day2 列表的使用.avi │ │ ├08 python s14 day2 列表的使用2.avi │ │ ├09 python s14 day2 元组与购物车程序练习.avi │ │ ├10 python s14 day2 购物车程序练习实例.avi │ │ ├11 python s14 day2 字符串常用操作.avi │ │ ├12 python s14 day2 字典的使用.avi │ │ ├13 python s14 day2 三级菜单实例.avi │ │ ├14 python s14 day2 本周作业-购物车优化.avi │ │ └s14day2_code.zip │ ├ │ │ ├day3-code.zip │ │ ├oldboy-10.avi │ │ ├oldboy-11.avi │ │ ├oldboy-12.avi │ │ ├oldboy-13.avi │ │ ├oldboy-14.avi │ │ ├oldboy-15.avi │ │ ├oldboy-16.avi │ │ ├oldboy-17.avi │ │ ├oldboy-18.avi │ │ ├oldboy-19.avi │ │ ├oldboy-20.avi │ │ ├oldboy-21.avi │ │ ├oldboy-22.avi │ │ ├oldboy-23.avi │ │ ├oldboy-24.avi │ │ ├oldboy-25.avi │ │ ├oldboy-26.avi │ │ ├oldboy-27.avi │ │ └oldboy-9.avi │ ├ │ │ ├day4.zip │ │ ├oldboy-1.avi │ │ ├oldboy-10.avi │ │ ├oldboy-11.avi │ │ ├oldboy-12.avi │ │ ├oldboy-13.avi │ │ ├oldboy-14.avi │ │ ├oldboy-15.avi │ │ ├oldboy-16.avi │ │ ├oldboy-17.avi │ │ ├oldboy-18.avi │ │ ├oldboy-2.avi │ │ ├oldboy-3.avi │ │ ├oldboy-4.avi │ │ ├oldboy-5.avi │ │ ├oldboy-6.avi │ │ ├oldboy-7.avi │ │ ├oldboy-8.avi │ │ └oldboy-9.avi │ ├ │ │ ├day5.zip │ │ ├oldboy-1.avi │ │ ├oldboy-10.avi │ │ ├oldboy-11.avi │ │ ├oldboy-12.avi │ │ ├oldboy-13.avi │ │ ├oldboy-14.avi │ │ ├oldboy-2.avi │ │ ├oldboy-3.avi │ │ ├oldboy-4.avi │ │ ├oldboy-5.avi │ │ ├oldboy-6.avi │ │ ├oldboy-7.avi │ │ ├oldboy-8.avi │ │ └oldboy-9.avi │ ├ │ │ ├01 本节小鸡汤(电影分享).avi │ │ ├02 面向对象介绍.avi │ │ ├03 面向对象特性介绍.avi │ │ ├04 实例演示opp编程好处.avi │ │ ├05 实例变量与类变量.avi │ │ ├06 类变量的作用及析构函数.avi │ │ ├07 类的继承.avi │ │ ├08 类的继承2.avi │ │ ├09 经典类与新式类的继承顺序.avi │ │ ├10 继承实例讲解.avi │ │ ├11 多态实例讲解.avi │ │ ├12 本节作业之选课系统开发.avi │ │ └day6.zip │ ├ │ │ ├day7.zip │ │ ├oldboy-1.avi │ │ ├oldboy-10.avi │ │ ├oldboy-11.avi │ │ ├oldboy-12.avi │ │ ├oldboy-13.avi │ │ ├oldboy-2.avi │ │ ├oldboy-3.avi │ │ ├oldboy-4.avi │ │ ├oldboy-5.avi │ │ ├oldboy-6.avi │ │ ├oldboy-7.avi │ │ ├oldboy-8.avi │ │ └oldboy-9.avi │ ├ │ │ ├day8.zip │ │ ├oldboy-01.avi │ │ ├oldboy-02.avi │ │ ├oldboy-03.avi │ │ ├oldboy-04.avi │ │ ├oldboy-05.avi │ │ ├oldboy-06.avi │ │ ├oldboy-07.avi │ │ ├oldboy-08.avi │ │ ├oldboy-09.avi │ │ ├oldboy-10.avi │ │ └oldboy-11.avi │ ├ │ │ ├day9.zip │ │ ├oldboy-1.avi │ │ ├oldboy-10.avi │ │ ├oldboy-11.avi │ │ ├oldboy-12.avi │ │ ├oldboy-13.avi │ │ ├oldboy-14.avi │ │ ├oldboy-15.avi │ │ ├oldboy-2.avi │ │ ├oldboy-3.avi │ │ ├oldboy-4.avi │ │ ├oldboy-5.avi │ │ ├oldboy-6.avi │ │ ├oldboy-7.avi │ │ ├oldboy-8.avi │ │ └oldboy-9.avi │ ├ │ │ ├day10.zip │ │ ├oldboy-1.avi │ │ ├oldboy-10.avi │ │ ├oldboy-11.avi │ │ ├oldboy-12.avi │ │ ├oldboy-13.avi │ │ ├oldboy-14.avi │ │ ├oldboy-15.avi │ │ ├oldboy-16.avi │ │ ├oldboy-17.avi │ │ ├oldboy-18.avi │ │ ├oldboy-2.avi │ │ ├oldboy-3.avi │ │ ├oldboy-4.avi │ │ ├oldboy-5.avi │ │ ├oldboy-6.avi │ │ ├oldboy-7.avi │ │ ├oldboy-8.avi │ │ └oldboy-9.avi │ ├ │ │ ├01 python s14 day 11 上节作业和读书分享_rec.mp4 │ │ ├02 python s14 day 11 jQuery选择器_rec.mp4 │ │ ├03 python s14 day 11 jQuery选择器eq和nth-child的区别_rec.mp4 │ │ ├04 python s14 day 11 jQuery表单选择器_rec.mp4 │ │ ├05 python s14 day 11 jQuery操作属性、css和返回顶部实例_rec.mp4 │ │ ├06 python s14 day 11 jQuery过滤器、事件和扩展方法_rec.mp4 │ │ ├07 python s14 day 11 jQuery模态对话框和ajax_rec.mp4 │ │ ├08 python s14 day 11 eclipse环境的配置_rec.mp4 │ │ ├09 python s14 day 11 MVC框架和python的WEB框架简介_rec.mp4 │ │ ├10 python s14 day 11 Eclipse django 使用_rec.mp4 │ │ ├11 python s14 day 11 创建Django project and app_rec.mp4 │ │ └12 python s14 day 11 Django用户注册和ajax请求_rec.mp4 │ ├ │ │ ├01 python s14 day 12上节作业_rec.mp4 │ │ ├02 python s14 day 12 Django路由系统_rec.mp4 │ │ ├03 python s14 day 12 Windows下Python-MySQL模块的安装_rec.mp4 │ │ ├04 python s14 day 12 模型字段类型_rec.mp4 │ │ ├05 python s14 day 12 增删改查过滤排序_rec.mp4 │ │ ├06 python s14 day12 模版语言和跨站请求伪造1_rec.mp4 │ │ ├07 python s14 day 12 模版语言和跨站请求伪造2_rec.mp4 │ │ └08 python s14 day 12 模板extend和include_rec.mp4 │ ├ │ │ ├day13.zip │ │ ├oldboy-1.avi │ │ ├oldboy-10.avi │ │ ├oldboy-2.avi │ │ ├oldboy-3.avi │ │ ├oldboy-4.avi │ │ ├oldboy-5.avi │ │ ├oldboy-6.avi │ │ ├oldboy-7.avi │ │ ├oldboy-8.avi │ │ └oldboy-9.avi │ ├ │ │ ├01开课介绍.avi │ │ ├02前端技术概要.avi │ │ ├03今日内容概要.avi │ │ ├04Html本质以及在web程序中的作用(一).avi │ │ ├05Html本质以及在web程序中的作用(二).avi │ │ ├06Html本质以及在web程序中的作用(三).avi │ │ ├07html的head内标签.avi │ │ ├08html的body内标签之图标、换行及其他.avi │ │ ├09chrome查看html样式基本操作.avi │ │ ├10html的body内标签之input系列(一).avi │ │ ├11html的body内标签之input系列(二).avi │ │ ├12html的body内标签之多行文本及下来框.avi │ │ ├13html的body内标签之超链接.avi │ │ ├14html的body内标签之图片及表格.avi │ │ ├15html的body内标签之表格.avi │ │ ├16html的body内标签之fieldset标签和label标签.avi │ │ ├17上述内容总结.avi │ │ ├18css选择器.avi │ │ ├19css的存在形式及优先级.avi │ │ ├20css边框以及其他常用样式.avi │ │ ├21css之float样式.avi │ │ ├22css之float实现作业实例.avi │ │ ├23css之display样式.avi │ │ ├24css之内外边距.avi │ │ ├25本周作业以及思路.avi │ │ └day14课上所有.zip

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李兴球

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值