项目三 简易数字时钟 一、学习目标 (1)认识Python 语言中的列表; (2)能够将Image 类实例化,创造一个包含图像的对象; (3)能够调用display 模块中的show() 函数,依次显示列表中存储的多个图像,在 micro:bit 控制板的LED 点阵屏上显示简单的动画; (4)能够利用for...in... 语句和range( ) 函数编写基于次数的循环结构的程序。 二、动手实验 在项目二的结尾部分,我们留了一道思考题:如何能够编写一段模拟钟表运行的动 画呢?相信聪明的你肯定已经编写出这个程序了。 但美中不足的是,仅仅基于项目二学到的内容,这个程序一定会非常冗长复杂。那 么,有没有更简单的办法呢?让我们一起来开始本项目的实验吧。 (一)实验准备 (1)micro:bit 控制板1 个; (2)MicroUSB 线缆1 条; (3)BXY Python Editor 软件。 (二)操作步骤 基于项目二已经学到的内容,如果需要编写一段模拟钟表运行的动画,那么需要输 入如下程序。 from microbit import* display.show(Image.CLOCK1) sleep(500) 项目三 简易数字时钟 15 display.show(Image.CLOCK2) sleep(500) display.show(Image.CLOCK3) sleep(500) display.show(Image.CLOCK4) sleep(500) display.show(Image.CLOCK5) sleep(500) display.show(Image.CLOCK6) sleep(500) display.show(Image.CLOCK7) sleep(500) display.show(Image.CLOCK8) sleep(500) display.show(Image.CLOCK9) sleep(500) display.show(Image.CLOCK10) sleep(500) display.show(Image.CLOCK11) sleep(500) display.show(Image.CLOCK12) sleep(500) 可以看到,程序的确非常繁杂。为了解决这个问题,可以调整一下现有程序的结构。 列表(List)是解决这个问题的好帮手。 Python 语言中的列表 列表是Python 语言中一种非常重要的数据结构,它在其他程序设计语言中通常 被称为数组。每一个列表都是“列表”类的一个实例,它是一系列元素的集合,这些 元素可以是数字、字符串,也可以是对象。列表中的每个元素都会被分配一个数字, 用来标示它在列表中的位置,我们把这个数字叫作索引。第一个元素的索引是0,第 二个元素的索引是1,以此类推。 为了调整程序的结构,我们先建立一个名为clockList 的列表,并将所有需要显示的 图形存放在其中,图形之间用半角的“,”进行分隔。 16 中学生Python与micro:bit机器人程序设计 clockList = [Image.CLOCK1,Image.CLOCK2,Image.CLOCK3,Image.CLOCK4, Image.CLOCK5,Image.CLOCK6,Image.CLOCK7,Image.CLOCK8,Image.CLOCK9, Image.CLOCK10,Image.CLOCK11,Image.CLOCK12] 这样,你就得到了一个存有待显示内容的列表clockList。 当需要调取列表中的某个元素时,可直接在列表后输入中括号“[]”,并在中括号中 输入元素索引即可。例如,当需要调取clockList 中存储的Image.CLOCK9 这个元素时, 直接写成clockList[8] 即可。 Python 语言中和列表相关的主要函数如表3-1 所示。 表3-1 Python 语言中和列表相关的主要函数 函 数 名参  数作  用示  例 len( ) 列表的名称返回列表的长度,也就是列表中包含元素的数量len(clockList) max( ) 列表的名称返回列表中所有元素的最大值max(clockList) min( ) 列表的名称返回列表中所有元素的最小值min(clockList) 还有一些关于列表的函数,由于这些函数与“列表”类绑在一起,所以通常不称这 些函数为“函数”,而称为“方法”。Python 语言中和列表相关的主要方法如表3-2 所示。 表3-2 Python 语言中和列表相关的主要方法 方法名参  数作  用示  例 append( ) 需要插入列表的元素将该元素插入到列表的末尾clockList.append(Image.SMILE) extend( ) 多个需要插入列表的元素将多个元素依次插入到列表的末尾 clockList.extend(Image.SMILE, Image.SAD) insert( ) 列表中的索引编号、需要 插入列表的元素 将该元素插入到索引编号对应的位置, 该位置之后的所有元素依次后移 clockList.insert(9,Image.SMILE) pop( ) 列表中的索引编号将列表中指定索引编号对应的元素删除clockList.pop(9) remove( ) 需要从列表中删除的元素将该元素从列表中删除clockList.remove(Image.CLOCK5) count( ) 列表中的某个元素计算该元素在列表中出现的次数clockList.count(Image.CLOCK5) index( ) 列表中的某个元素找到该元素在列表中对应的索引编号clockList.index(Image.CLOCK5) reverse( ) 无参数将列表中所有的元素逆序排列clockList.reverse() 请对比表3-1 和表3-2 中“示例”列所示书写方法的不同,感受一下“函数”和“方 法”的不同,体会“方法是绑定在类上的函数”的含义。 在掌握了列表的相关知识后,就可以直接调用display 模块中的show() 函数,来显 示列表clockList 中存放的图像。show() 函数中输入的参数如果是一个图像列表,那么函 项目三 简易数字时钟 17 数会自动将列表中的每个图像依次取出并显示。这里只需要在show() 函数中添加delay 参数来确定每张图像显示的时间间隔即可。 display.show(clockList,delay=500) 接下来将上面的程序通过BXY Python Editor 烧录到micro:bit 控制板,然后观察一 下效果吧。 事实上,在上面程序的基础上,还可以让程序更加精简。在Image 对象中,存在两 个特殊的属性,即ALL_CLOCKS 和ALL_ARROWS,它们是一种特殊的只读列表,称 为元组(Tuple)。这两个元组中已经存储了所有与时钟相关的图像和与箭头相关的图像, 可以直接调用。利用这两个属性可以将程序直接写成: display.show(Image.ALL_CLOCKS,delay=500) 三、阅读思考 刚刚已经对程序进行了最大限度的精简,但是这种精简是建立在microbit 库已经提 供了很多便捷功能的基础之上的。试想一下,如果show() 函数不能直接显示列表中的多 个图像,只能显示单独的图像,怎样才能比较简单地完成显示动画的目标呢? 在这种情况下,就需要用到Python 语言中的for...in... 语句来解决这个问题。 Python 语言中的for...in... 循环 for...in... 语句能够构建一个循环结构,并在循环过程中将某个列表中的所有元素 依次取出。 for i in Image.ALL_CLOCKS:    display.show(i)    sleep(500) 从上面的程序中可以看到,通过“for i in Image.ALL_CLOCKS:”这行语句构建 了一个循环,程序会依次从Image.ALL_CLOCKS 列表里取出一个元素临时放到变量 i 中,这样我们就可以直接调用变量i 进行操作了,例如通过show() 函数将i 显示出来。 18 中学生Python与micro:bit机器人程序设计 整个循环的次数就是Image.ALL_CLOCKS 列表中元素的数量。 除了从列表中依次取出元素的用途之外,for...in... 语句也是一种重要的构建循环 结构的方式。项目二中学过的while 语句主要依靠条件来构建一个循环结构,在条件 为真的情况下循环会一直重复进行。而for...in... 语句则可以构建一个次数固定的循环 结构,当达到设定的次数时,循环就会停止。 为了能够灵活构建一个基于for...in... 语句的计次循环,往往需要用到range( ) 函数。 range( ) 函数用于生成一个存储着整数的列表,它的参数表如表3-3 所示。 表3-3 range( ) 函数的参数表 参 数作  用 起始值 列表中第一个整数的值。 起始值在range( ) 函数中可以不填写,如不填写,则默认为0 结束值 结束值是range( ) 函数中必须填写的一个参数,它是列表中最后一个整数生成后结束时的值,但不包括 这个结束值本身。例如,输入range(0,3),列表中包含的整数是0、1、2。 你可以尝试这样理解:在遇到这个结束值之前,函数会在列表中持续生成整数,直到遇到结束值,生成 操作才会立即停止 步长值 从起始值开始,加上步长值,就是列表中下一个整数的数值。例如,输入range(0,20,5),列表中 包含的整数是0、5、10、15。 步长值在range( ) 函数中可以不填写,如不填写,则默认是1 通过表3-3 可以看到,range( ) 函数的参数很多,看起来也很复杂。但事实上在起始值、 结束值、步长值这三个参数中,只有结束值是必须填写的。 当使用range( ) 函数配合for...in... 语句来构建计次循环时,只需要确保列表中元素的 数量符合要求,就可以对循环的次数进行控制了。至于这些元素本身是什么并不重要, 只需要填写结束值一个参数就可以了。例如: for i in range(5): display.scroll("HELLO!") 利用上面的程序可以让“HELLO!”在LED 点阵屏上滚动5 次。 四、实践探究 我们已经掌握了通过Image 类中预定义的属性,以及display 模块中的show() 函数 在LED 点阵屏上显示图像的方法。也可以运用多种技巧,让一张张单独的图像连续播放, 项目三 简易数字时钟 19 组成动画。现在是时候来创建一组属于你自己的个性动画了。 为了绘制出有个性的动画,Image 类的属性中预存储的图像显然是不够的。还记得 在项目二中提到的“类”和“实例”之间的关系吗?下面将学习Image 类实例化的方法, 来创建一张自定义的图像。 在micro:bit 控制板的LED 点阵屏上,每一个LED 的点亮或熄灭状态都可以用数字 来表示。用0 表示LED 熄灭,用数字1~9 分别表示LED 的亮度,1 为最暗,9 为最亮。 为了将Image 类实例化,从第一行开始,按从左到右的顺序来设定每一个LED 的状 态。例如: car = Image("00000:05550:05050:99999:09090") display.show(car) 你能想象出这幅图像是什么样子的吗?如果很难想象,也可以换一种输入方式。 car = Image("00000:" "05550:" "05050:" "99999:" "09090") display.show(car) 不妨把它编写出来,烧录进micro:bit 控制板,看一 看程序的效果。通过操作,你应该能看到如图3-1 所示 的图像car 的显示效果。 下面要开始让这辆小汽车动起来了。小汽车需要从 画面的最左侧出现,然后向右行驶,并在画面的最右侧 消失。想一想,这里需要几个步骤呢? 首先,需要将动画分解成9 幅不同的画面,小汽车 动画的分解图如图3-2 所示。 接下来需要建立一个列表,将9 幅画面全部存储到 列表中。 最后,可以利用for...in... 循环配合show() 函数;或者直接利用show() 函数,将列表 中的画面依次显示出来。 图3-1 图像car 的显示效果 20 中学生Python与micro:bit机器人程序设计 请亲自动手尝试一下,把这个动画制作出来吧。期待你的作品。 五、效果检测 1. Python 语言中有两种循环结构,其中while 语句构建的是基于    的循环结构, for...in... 语句构建的是基于    的循环结构。 2. 如果想构建一个循环5 次的循环结构,需要写for i in range(    )。 3. 现有一段程序: intList = range(4,18,2) 列表intList 中共有    个元素,它们分别是            。 图3-2 小汽车动画的分解图 项目三 简易数字时钟 21 4. 我们在项目二的“效果检测”环节看到了这样的图形,如图3-3 所示。 图3-3 图形展示 请根据图3-3 所示的图形编写一段程序,并基于这个图形用Image 类创建一个名为 Star 的对象。 项目四 情绪显示器 一、学习目标 (1)认识布尔变量; (2)理解输入元件和输出元件的区别; (3)理解由if、elif、else 组成的单分支结构与多分支结构之间的区别; (4)能够利用is_ pressed( ) 方法检测按钮是否按下; (5)能够利用micro:bit 控制板上的按钮编写基于分支结构的程序。 二、动手实验 我们每个人都有过感冒的经历,如果在嗓子疼的时 候有一个情绪显示器能帮助我们不用说话,也能向其他 人传递自己的情绪,那将是一件多么好的事情啊。在本 项目中,我们一起利用micro:bit 控制板的按钮和LED 点阵屏制作一个这样的情绪显示器,更好地向身边的朋 友表达自己的情绪。micro:bit 控制板上的按钮如图4-1 所示。 (一)实验准备 (1)micro:bit 控制板1 个; (2)MicroUSB 线缆1 条; (3)BXY Python Editor 软件。 (二)操作步骤 为了完成这个项目,需要在BXY Python Editor 的编程环境中输入以下代码。 from microbit import* 图4-1 micro:bit 控制板上的按钮 项目四 情绪显示器 23 while True: if button_a.is_pressed(): display.show(Image.HAPPY) if button_b.is_pressed(): display.show(Image.SAD) 将程序烧录进micro:bit 控制板,分别按下micro:bit 控制板上的两个按钮,观察效果。 三、阅读思考 在本项目中,将会接触到micro:bit 控制板上的按钮。按钮和LED 点阵屏是两种截 然不同的元件,这两个元件最大的区别如下。 LED 点阵屏只能通过亮灭状态输出程序的执行结果,我们把这类元件称为输出元件。 按钮可以直接和用户进行交互,用户可以把自 己的操作通过按钮输入给micro:bit 控制板,再由 micro:bit 控制板根据用户输入的操作执行不同的程 序,我们把这类元件称为输入元件。 想一想,micro:bit 控制板上还有加速度计和电 子罗盘,它们属于输入元件还是输出元件? 有了输入元件,用户就可以将不同指令输入给 micro:bit 控制板,那么程序就不能再一股脑地从头 执行到尾,它必须根据输入操作的不同来决定运 行哪些程序。这时,就需要用到分支结构来构建程 序。刚刚烧录进micro:bit 控制板的程序就是一段 典型的包含单分支结构的程序,为了能更好地理解 这段程序的运行过程,先来绘制这段程序的流程图 (见图4-2)。 接下来对照流程图来解读这段程序。 while True: 尽管这是一段基于分支结构的程序,但是在顺序结构中“从上到下依次逐行运行、 所有程序语句都执行完后停止”的特性依然是生效的。还记得项目二中曾做过“跳动的