《哪咤学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这个列表。这个列表的每一项是一个二元组。它如下图右边文字所示:
右边的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_lrcif __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._canvasdef 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 messageboxdef 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 : # 到屏幕最低就重新initself.init()def close_window():"""单击屏幕把running设为False,这样while循环就结束了"""global running if messagebox.askokcancel("白桦林", "你真的要离开吗?"):running = Falseif __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。你可以收藏一下,敬请期待。
《哪咤学python进阶篇》之选学案例三:白桦林的故事_(Python多媒体MV)相关推荐
- python进阶篇:如何进行存档!粉丝特辑!
python进阶篇:如何进行存档!粉丝特辑! 大家好!制作不易,请求赞赏! 大家一般在制作游戏的时候,都很想制作存档这个功能,但到底怎么做呢? 下面就告诉大家! 1.1基本文件操作 为什么要学这个呢? ...
- 三步解决C语言中struct字节对齐问题,Python进阶篇-struct字节对齐问题
Python进阶篇-struct字节对齐问题 Python进阶篇-struct字节对齐问题 Python调用C的时候,会传递一些复杂的数据结构,例如结构体,这时候就会遇到各种各样字节对齐的问题.下边所 ...
- Python进阶篇:百度指数解密【抓包JS逆向数据区分】
前言 大家好,我是辣条哥~ 过往给大家更新了不少基础相关的,今天给大家上点硬货,基础不好的慎入,免得打击你们的积极性~ 其次对数据分析|数据可视化|pandas感兴趣的可以来这里刷刷题: →→→< ...
- 伍六七带你学算法 进阶篇-生命游戏
有趣的算法题–生命游戏 难度-中等 根据 百度百科 ,生命游戏,简称为生命,是英国数学家约翰·何顿·康威在 1970 年发明的细胞自动机. 想要体验生命游戏的小伙伴可以到这里-->生命游戏 进入 ...
- python(进阶篇)——自动化操作Excel(xlrd和xlwt)
活动地址:CSDN21天学习挑战赛 学习的最大理由是想摆脱平庸,早一天就多一份人生的精彩: 虽然永远无法预料明天是晴还是雨, 也无法预知你在乎的人是否还在身旁, 以及你一直以来的坚持究竟能否换来什么. ...
- Python进阶篇:MySQL隔离级别详解
前言 数据库并发的对同一批数据进行增删改,就可能会出现我们所说的脏写.脏读.不可重复读.幻读等一系列问题.MySQL提供了一系列机制来解决事务并发问题,比如事务隔离.锁机制.MVCC多版本并发控制机制 ...
- Python进阶篇:百度指数解密【抓包|JS逆向|数据区分】
前言 大家好,我是辣条哥~ 过往给大家更新了不少基础相关的,今天给大家上点硬货,基础不好的慎入,免得打击你们的积极性~ 其次对数据分析|数据可视化|pandas感兴趣的可以来这里刷刷题: →→→< ...
- 伍六七带你学算法 进阶篇-排序算法
给定一个整数数组 nums,将该数组升序排列. 示例 1: 输入:[5,2,3,1] 输出:[1,2,3,5] 示例 2: 输入:[5,1,1,2,0,0] 输出:[0,0,1,1,2,5] 各排序算 ...
- 伍六七带你学算法 进阶篇-三数之和
三数之和 难度-中等 题目:给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组. 注意 ...
- [Python公开课]零基础玩转Python进阶篇----第九节:Python的异常分析及解决
最新文章
- datagrid底部显示水平滚动_easyUI datagrid 横向滚动条显示问题
- Mac再建管理员帐户
- 【转】VTK修炼之道2_VTK体系结构1
- Vue或React多页应用脚手架
- 分享一些自己的学习过程和学习方法
- vs 2017 无法安装任何 nuget package,提示“库没有注册。。。”
- 链接生成动态二维码图片显示在页面上
- JQuery官方学习资料(译):类型
- Leetcode 513 javascript
- matlab中boxplot函数的参数设置_Matlab箱形图boxplot函数用法
- 有一群志同道合的程序员朋友是怎样的体验?
- 897-了解微服务网关
- 人民的名义泄漏版百度云46-56集百度网盘下载
- 如何将数据从一台主机发送到另一台主机上(详解)
- Excel同一单元格多数据如何求平均数
- 西哈努克之子柬埔寨国王西哈莫尼抵京-西哈努克-国王-抵京
- esp32cam.cpp:30:3: error: ‘camera_sensor_info_t’ was not declared in this scope camera_sensor_info_t
- 语音合成(speech synthesis)方向十一:聊一聊增量式语音合成(iTTS)进化史
- SCOI 2012 喵星球上的点名 题解
- Vscode 设置clang-format