版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
类与对象第十章课程目标课程目标1领会如何通过定义新类为复杂程序提供结构3524能够阅读并编写Python类定义理解封装的概念并知道它如何帮助构建模块化的、可维护的程序能够编写包含简单类定义的程序能够编写包含创新(程序员设计的)控件的交互式图形程序PART1对象的快速复习1对象的快速复习程序“计算”结构化程序“数据”结构化1对象的快速复习对象是管理复杂数据的重要工具对象一组相关信息一组信息的操作存储信息的变量
称为属性“存在”于对象内的函数
称为方法这些操作又称为方法实例变量中一组相关信息被称为对象的“属性”1对象的快速复习例子一个Circle对象
属性:center:记住圆的中心点radius:保存圆的半径
方法:draw方法:检查center和radius,以确定窗口中的哪些像素应该着色着Move方法:改变中心的值,以反映圆的新位置1对象的快速复习记住每个对象都是一个类的一个实例,对象的类决定对象将具有什么属性。一个类描述它的实例知道什么和做什么创建新对象调用构造方法可视为创建新实例的工厂交通工具
是一个类某一辆汽车、某一架飞机、某一辆自行车等
是这个交通工具类的实例对象1对象的快速复习创建一个新的Circle对象:myCircle=Circle(Point(0,0),20)Circle是类的名称,用于调用构造方法实例被创建后,可通过调用它的方法来操作它:myCircle.draw(win)myCircle.move(dx,dy)
创建了一个新的Circle实例,并将引用保存到变量myCircle构造方法的参数用于初始化myCircle内部的一些实例变量(即center和radius)PART2示例程序:炮弹2示例程序:炮弹程序规格说明解决的问题:输入各种发射角度和初始速度,计算炮弹飞多远模拟炮弹程序输入参数输出结果炮弹的发射角(以度为单位)初始速度(以米每秒为单位)初始高度(以米为单位)抛体在撞击地面前飞行的距离(以米为单位)2示例程序:炮弹程序规格说明重力加速度:g≈9.8m/s2用模拟来跟踪炮弹每个时刻的位置计算原理:物体在给定时间内飞行的距离等于其速率乘以时间(d=rt)2示例程序:炮弹程序设计炮弹的两个维度:高度:知道炮弹什么时候碰到地面一距离:记录炮弹飞多远二2示例程序:炮弹程序设计炮弹的位置可视为二维图中的点(x,y)xyx值:炮弹与起点的水平距离y值:炮弹与地面的高度,即垂直距离2示例程序:炮弹程序设计xy(0,0)炮弹飞行过程中,它在二维图中点的坐标是时刻更新的
每隔十分之一秒定时检查炮弹的位置每个维度的精确距离由炮弹在该方向上的速度决定2示例程序:炮弹程序设计分离速度的x和y分量可以让问题变得更容易x速度y速度由于忽略风的阻力,x速度在整个飞行中保持不变由于重力的影响,y速度随时间而变化速度开始为正,后随着炮弹开始下降变为负值2示例程序:炮弹程序设计根据分析,模拟程序要做的事情如下:输入模拟参数:角度、速度、高度、间隔计算炮弹的初始位置:xpos、ypos计算炮弹的初始速度:xvel、yvelwhile炮弹仍在飞行时:将xpos,ypos和yvel的值更新为飞行输出中距离作为xpos的距离2示例程序:炮弹算法的第一行很简单,只需要合适的输入语句序列:程序设计defmain():
angle=float(input("Enterthelaunchangle(indegrees):"))
vel=float(input("Entertheinitialvelocity(inmeters/sec):"))
h0=float(input("Entertheinitialheight(inmeters):"))
time=float(input("Enterthetimeintervalbetweenpositioncalculations:"))给炮弹的初始位置赋值,它将从距离0和高度h0开始:xpos=0.0ypos=h02示例程序:炮弹程序设计水平速度分量公式:垂直速度分量公式:要将角度转换为弧度2示例程序:炮弹程序设计Python的math库提供了一个方便的函数,称为radians,用于将角度数值转换为弧度数值。theta=math.radians(angle)
xvel=vel*cos(theta)
yvel=vel*sin(theta)
计算初始速度的代码:2示例程序:炮弹程序设计程序的主循环代码:whileypos>=0.0:
通过检查ypos的值不断更新炮弹的位置和速度,直到它到达地面使用>=作为关系,可使炮弹在地面上开始(=0)飞行时,让循环不断执行ypos的值下降到0以下时,循环退出,表明炮弹已经到达地面2示例程序:炮弹程序设计模拟的关键:
每次进入循环时,更新炮弹的状态,让它在飞行中移动time秒
风阻忽略,炮弹的水平速度保持不变,由xvel的值给出水平方向xpos=xpos+time*xvel更新水平位置的代码:2示例程序:炮弹程序设计
由于重力加速度的影响,速度每秒减少9.8m/s垂直方向速度随时间而降低yvel1=yvel–time*9.8间隔结束时的新速度计算:2示例程序:炮弹程序设计平均垂直速度重力加速度是恒定的平均速度等于开始和结束速度的平均值(yvel+yvel1)/2.02示例程序:炮弹程序设计whileypos>=0.0: xpos=xpos+time*xvel yvel1=yvel-time*9.8 ypos=ypos+time*(yvel+yvel1)/2.0 yvel=yvel1下面是完成的循环:平均速度乘以间隔时间,给出了高度的变化保持初始值,计算平均速度表示在间隔结束时炮弹的正确垂直速度2示例程序:炮弹程序设计完整的程序:#cball1.py#Simulationoftheflightofacannonball(orotherprojectile)#Thisversionisnotmodularized.frommathimportpi,sin,cosdefmain():angle=float(input("Enterthelaunchangle(indegrees):"))vel=float(input("Entertheinitialvelocity(inmeters/sec):"))
2示例程序:炮弹程序设计完整的程序(续上):h0=float(input("Entertheinitialheight(inmeters):"))time=float(input("Enterthetimeintervalbetweenpositioncalculations:"))radians=(angle*pi)/180.0xpos=0ypos=h0xvel=vel*cos(radians)yvel=vel*sin(radians)2示例程序:炮弹程序设计完整的程序(续上):whileypos>=0:xpos=xpos+time*xvelyvel1=yvel-9.8*timeypos=ypos+time*(yvel+yvel1)/2.0yvel=yvel1print("\nDistancetraveled:{0:0.1f}meters.".format(xpos)最后一步,输出飞行距离2示例程序:炮弹程序模块化defmain():angle,vel,h0,time=getInputs()xpos,ypos=0,h0xvel,yvel=getXYComponents(vel,angle)whileypos>=0:xpos,ypos,yvel=updateCannonBall(time,xpos,ypos,xvel,yvel)print("\nDistancetraveled:{0:0.1f}meters.".format(xpos)使用辅助函数的主算法版本:theta在getXYComponents内部使用yvel1是updateCannonBall的局部变量2示例程序:炮弹程序模块化Projectile类能“理解”炮弹这类物体的物理特性用单个变量来创建和更新合适的对象,表示主算法2示例程序:炮弹程序模块化通过“基于对象”的方法编写主程序:defmain():
angle,vel,h0,time=getInputs()
cball=Projectile(angle,vel,h0)
whilecball.getY()>=0:
cball.update(time)
print("\nDistancetraveled:{0:0.1f}meters.".format(cball.getX()))创建了一个Projectile,名为cball更新状态以记录时间PART3定义新类3定义新类示例:多面骰子MSDie类有多少面当前的值类该如何设计3定义新类示例:多面骰子创建一个新的MSDie指定它将拥有多少面提供三种方法1roll方法:将骰子设置为1~n之间的随机值,包括1和n2setValue方法:将骰子设置为特定值(即作弊)3getValue方法:查看当前的值3定义新类示例:多面骰子调用MSDie的构造方法创建一个骰子实例变量1:内部记录面数实例变量2:保存骰子的当前值面数作为参数骰子初始值为1该该值可以通过roll和setValue方法更改,并通过getValue方法返回。3定义新类示例:多面骰子类其实就是方法的集合,方法就是函数。MSDie的定义:fromrandomimportrandrangeclassMSDie:def__init__(self,sides):#用__init__定义的方法为构造方法
self.sides=sides#类属性=参数
即为属性赋值self.value=1defroll(self):#自定义方法
self.value=randrange(1,self.sides+1)defgetValue(self):#自定义方法
returnself.value#返回属性值defsetValue(self,value):#自定义方法
self.value=value#设置属性值class<class-name>: <method-definitions>类定义的形式包含该方法所在的对象的引用3定义新类示例:多面骰子假设:一个main函数执行die1.setValue(8)Step1调用程序(main)暂停在方法调用处。Step2该方法的形参被赋予调用的实参提供的值。self=die1value=8在执行方法体之前,示例中执行的语句完成了以下赋值:方法调用时,第一个形参对应于该对象defsetValue(self,value):#自定义方法
self.value=value#设置属性值3定义新类示例:多面骰子假设:一个main函数执行die1.setValue(8)Step3执行方法体。Step4控制返回到调用方法之后的位置。在这个例子中,是紧随die1.setValue(8)之后的语句。3定义新类示例:多面骰子假设:一个main函数执行die1.setValue(8)图调用中的控制流:die1.setValue(8)3定义新类示例:多面骰子self即代表对象的参数。方法(参数1,参数2,...)self参数普通参数点表示法:<object>.<instance-var>Self参数可通过名称访问self.value是指与对象关联的实例变量值每个实例都有自己的实例变量3定义新类示例:多面骰子例子:特殊方法__init__类某些方法对Python具有特殊的意义特征:方法的名称以两条下划线开始和结尾调用该方法能够为对象的实例变量提供初始值。构造方法3定义新类示例:多面骰子初始化新的MSDie的语句:die1=MSDie(6)该语句创建了一个新的MSDie,并在该对象上执行inittdie1.sides的初始值:6die1.value的初始值:13定义新类示例:多面骰子记住特定对象的状态并作为对象的一部分传递给程序实例变量值可在其他方法中引用亦可在连续调用相同方法时再次引用函数终止则值消失3定义新类示例:多面骰子一个简单的例子:>>>die1=Die(13)>>>print(die1.getValue())1>>>die1.setValue(8)>>>print(die1.getValue())8在下一行打印出值3定义新类示例:Projectile类抛物体类
初始化实例变量构造方法
改变抛体的状态update方法
得知当前的位置getX方法
得知当前的位置getY方法
3定义新类示例:Projectile类构造方法角度速度高度创建一个炮弹:cball=Projectile(angle,vel,h0)3定义新类示例:Projectile类__init__方法用这些值来初始化cball的实例变量实例变量xposyposxvelyvel3定义新类示例:Projectile类下面是带有构造方法的类:classProjectile:def__init__(self,angle,velocity,height):self.xpos=0.0self.ypos=heighttheta=math.radians(angle)self.xvel=velocity*math.cos(theta)self.yvel=velocity*math.sin(theta)注意如何使用self点表示法,在对象内创建四个实例变量。3定义新类示例:Projectile类defgetX(self):returnself.xposdefgetY(self):returnself.ypos设置获取抛体位置的方法:只需设置一些返回位置值的方法3定义新类示例:Projectile类update方法代码如下所示:defupdate(self,time):self.xpos=self.xpos+time*self.xvelyvel1=self.yvel-time*9.8self.ypos=self.ypos+time*(self.yvel+yvel1)/2.0self.yvel=yvel1接受一个普通参数,表示时间间隔使用yvel1作为临时变量将该值存储到对象中,从而保存该新值3定义新类示例:Projectile类完整的炮弹问题解决方案:frommathimportsin,cos,radiansclassProjectile:def__init__(self,angle,velocity,height):self.xpos=0.0self.ypos=heighttheta=radians(angle)self.xvel=velocity*cos(theta)self.yvel=velocity*sin(theta)3定义新类示例:Projectile类完整的炮弹问题解决方案(续上):defupdate(self,time):self.xpos=self.xpos+time*self.xvelyvel1=self.yvel-9.8*timeself.ypos=self.ypos+time*(self.yvel+yvel1)/2.0self.yvel=yvel1defgetY(self):returnself.yposdefgetX(self):returnself.xpos3定义新类示例:Projectile类完整的炮弹问题解决方案(续上):defgetInputs():a=float(input("输入发射角度(度):"))v=float(input("输入初速度(米/秒):"))h=float(input("输入初始高度(米):"))t=float(input("输入时间间隔:"))returna,v,h,t3定义新类示例:Projectile类完整的炮弹问题解决方案(续上):defmain():angle,vel,h0,time=getInputs()cball=Projectile(angle,vel,h0)whilecball.getY()>=0:cball.update(time)print("\n飞行距离:{0:0.1f}meters.".format(cball.getX()))PART4用类处理数据4用类处理数据
在典型的大学里,课程是按学分来衡量的,而平均分是以4分为基准计算。其中“A”是4分,“B”是3分...如此类推。平均积分点(GPA)计算采用积分点计算平均积分点例如:课程价值3个学分,学生获得“A”,那将获得3(4)=12个积分平均积分点=总积分点除以完成的学分数4用类处理数据姓名学分积分Adams,Henry127228Comptewell,Susan100400DibbleBit,Denny1841.5Jones,Jim48.5155Smith,Frank37125.33学生成绩信息4用类处理数据任务描述:读取文件,找到GPA最好的学生并打印他的名字、学分和GPA创建一个Student类并在构造方法中初始化:classStudent:def__init__(self,name,hours,qpoints):=nameself.hours=float(hours)self.qpoints=float(qpoints)对象是单个学生的信息记录与实例变量名匹配的参数名称让构造方法变得更通用4用类处理数据为HenryAdams创造一个记录:aStudent=Student("Adams,Henry",127,228)定义一些取值的方法:defgetName(self):returndefgetHours(self):returnself.hoursdefgetQPoints(self):returnself.qpoints我们能够使用这些方法从学生记录中获取信息!4用类处理数据要打印学生姓名:print(aStudent.getName())计算GPA的方法:defgpa(self):returnself.qpoints/self.hoursGPA=总积分点/完成的学分数4用类处理数据
逐一查看文件中的学生并记录到目前为止看到的最好的学生。算法原理类似于确定n个数字的最大值的算法
从用户处获取文件名打开文件并读取信息先指定第一个学生为最好的学对于文件中的每个学生ifs.gpa()>best.gpa()指定s为最好的学生打印出最好学生的信息程序算法描述4用类处理数据完整代码:classStudent:def__init__(self,name,hours,qpoints):=nameself.hours=float(hours)self.qpoints=float(qpoints)defgetName(self):returndefgetHours(self):returnself.hoursdefgetQPoints(self):returnself.qpointsdefgpa(self):returnself.qpoints/self.hours4用类处理数据完整代码(续上):defmakeStudent(infoStr):#infoStr是一个用制表符分隔的行:namehoursqpoints#返回一个对应的Student对象name,hours,qpoints=infoStr.split("\t")returnStudent(name,hours,qpoints)defmain():#打开文件并读取信息filename=input("输入文件名称:")infile=open(filename,’r’)
#将文件中的第一个学生设定为最好的学生best=makeStudent(infile.readline())添加辅助函数makeStudent按制表符拆分字段为文件中的第一个学生创建一个记录4用类处理数据完整代码(续上):#处理文件的后续行forlineininfile:s=makeStudent(line)#如果s的最好学生,则用s替换ifs.gpa()>best.gpa():best=sinfile.close()
#打印最好学生的信息print("最好的学生是:",best.getName())print("hours:",best.getHours())print("GPA:",best.gpa())
if__name__==’__main__’:main()再次被调用来处理文件的后续每行4用类处理数据对样本数据的运行结果:输入文件名称:students.dat最好的学生是:Computewell,Susanhours:100.0GPA:4.0这问题如何解决呢?
问题:
若有多名学生都有最佳的GPA,那么只报告第一名学生。PART5对象和封装第三课时5对象和封装封装有用的抽象Projectile类Student类类是让程序变得模块化的一种方法。
主程序只需要关心对象可以执行的操作。这种分离的模式称为“封装”5对象和封装封装有用的抽象你可以使用常规点符号访问任何对象的实例变量。测试Projectile类的构造方法:>>>c=Projectile(60,50,20)>>>c.xpos0.0>>>c.ypos20>>>c.xvel25.0>>>c.yvel43.301270通过创建对象直接检查实例变量的值。5对象和封装封装有用的抽象允许独立地修改和改进类不用担心“破坏”程序的其他部分封装的优点5对象和封装将类放在模块中程序A程序BProjectile类这个类的方法为什么要这么写?添加文档描述5对象和封装模块文档Python包含一种特殊的注释约定,称为“文档字符串”(docstring)。模块类函数在模块、类或函数的第一行插入一个简单的字符串字面量,为该组件提供文档。5对象和封装模块文档简单地忽略了常规注释,但文档字符串实际在执行时被放在一个名为_
_doc_
_的特殊属性中,并且这些字符串可以动态地检查。文档字符串的优点5对象和封装模块文档不记得如何使用随机函数时,可以直接打印其文档字符串:>>>importrandom
>>>printrandom.random.__doc__
random()->xintheinterval[0,1).大多数Python库模块有大量的文档字符串,可用于获取有关使用模块或其内容的帮助。5对象和封装模块文档你可以用交互式帮助获得同样的信息:>>>importrandom>>>help(random.randrange)Helponmethodrandrangeinmodulerandom:randrange(start,stop=None,step=1,_int=<class'int'>)methodofrandom.RandominstanceChoosearandomitemfromrange(start,stop[,step]).Thisfixestheproblemwithrandint()whichincludestheendpoint;inPythonthisisusuallynotwhatyouwant.我有个叫pydoc的程序可以自动构建模块文档。5对象和封装模块文档下面是Projectile类的一个版本:"""projectile.pyProvidesasimpleclassformodelingtheflightofprojectiles."""frommathimportsin,cos,radiansclassProjectile:"""Simulatestheflightofsimpleprojectilesneartheearth’ssurface,ignoringwindresistance.Trackingisdoneintwodimensions,height(y)anddistance(x)."""这是是一个包含文档字符串的模块文件5对象和封装模块文档下面是Projectile类的一个版本(续上):def__init__(self,angle,velocity,height):"""Createaprojectilewithgivenlaunchangle,initialvelocityandheight."""self.xpos=0.0self.ypos=heighttheta=radians(angle)self.xvel=velocity*cos(theta)self.yvel=velocity*sin(theta)5对象和封装模块文档下面是Projectile类的一个版本(续上):defupdate(self,time):"""Updatethestateofthisprojectiletomoveittimesecondsfartherintoitsflight"""self.xpos=self.xpos+time*self.xvelyvel1=self.yvel-9.8*timeself.ypos=self.ypos+time*(self.yvel+yvel1)/2.0self.yvel=yvel15对象和封装模块文档下面是Projectile类的一个版本(续上):defgetY(self):"Returnstheyposition(height)ofthisprojectile."returnself.yposdefgetX(self):"Returnsthexposition(distance)ofthisprojectile."returnself.xpos5对象和封装模块文档下面是在打印时显示文档字符串的示例:>>>print(projectile.Projectile.__doc__)Simulatestheflightofsimpleprojectilesneartheearth’ssurface,ignoringwindresistance.Trackingisdoneintwodimensions,height(y)anddistance(x).可以使用help查看模块的完整文档哦!5对象和封装使用多个模块我们的主程序现在可以简单地从projectile模块导入:fromprojectileimportProjectiledefgetInputs():a=float(input("输入发射角度(度):"))v=float(input("输入初速度(米/秒):"))h=float(input("输入初始高度(米):"))t=float(input("输入时间间隔:"))returna,v,h,tdefmain():angle,vel,h0,time=getInputs()cball=Projectile(angle,vel,h0)whilecball.getY()>=0:cball.update(time)print("\n飞行距离:{0:0.1f}米.".format(cball.getX()))原来的问题可以解决了!5对象和封装使用多个模块首次导入模板创建命名空间创建对已有模块对象的更多引用,续导入不会重新加载该模块。导入成功模块被更改后,即使重新导入也不会得到更新的版本。测试中涉及的任何模块被修改时,应重新开始交互式会话,以保证对模块进行全新(更新)导入。PART6控件6控件示例程序:掷骰子程序
我们将通过Button类实现两个按钮,用DieView类提供骰子数字的图形视图。我们一起来构建一些有用的控件吧!图掷骰子程序的用户界面6控件创建按钮掷骰子程序的按钮应具备以下的方法:方法名描述构造方法创建一个按钮,并能够指定按钮显示的窗口、按钮的位置或大小,以及按钮上的标签。Activate将按钮的状态设置为启用。Deactivate将按钮的状态设置为禁用。Clicked表明按钮是否被点击。若按钮处于启用状态,此方法将确定点击的点是否在按钮区域内。该点必须作为参数发送给该方法。getLabel用于返回按钮的标签字符串并识别特定的按钮。6控件创建按钮Activate方法通过让轮廓变得更粗或让标签文本使用黑体来表示按钮是启用:defactivate(self):"将按钮设置为激活."self.label.setFill('black')self.rect.setWidth(2)self.active=True注意,self参数指向按钮对象。6控件创建按钮Clicked方法按钮处理的代码如下所示:pt=win.getMouse()ifbutton1.clicked(pt):#处理button1的事件elifbutton2.clicked(pt):#处理button2的事件elifbutton3.clicked(pt)#处理button3的事件...graphics包提供了一个getMouse方法,可返回鼠标点击的点。调用getMouse可检查点在哪个启用的按钮中6控件创建按钮需要的变量应包括按钮对象的纵横坐标值的最大值和最小值。Clicked方法主要工作:确定给定点是否在矩形按钮内。判断条件:若点的x和y坐标位于矩形的极值x和y值之间,则该点在矩形内。6控件创建按钮用单个布尔表达式来实现clicked方法:假设:存在实例变量xmin、xmax、ymin和ymaxdefclicked(self,p):"若按钮处于激活,且p在按钮矩形内,则返回true"return(self.activeandself.xmin<=p.getX()<=self.xmaxandself.ymin<=p.getY()<=self.ymax)当三个表达式都为真时,clicked才会返回真。6控件创建按钮完整的类,并带有合适的构造方法:fromgraphicsimport*classButton:"""按钮是窗口中标记的矩形。使用activate()和deactivate()方法激活或禁用它。如果按钮是激活的并且p在按钮内,那么clicked(p)方法将返回true."""def__init__(self,win,center,width,height,label):"""创建一个矩形框按钮qb=Button(myWin,centerPoint,width,height,’Quit’)"""w,h=width/2.0,height/2.0x,y=center.getX(),center.getY()
6控件创建按钮完整的类,并带有合适的构造方法(续上):self.xmax,self.xmin=x+w,x-wself.ymax,self.ymin=y+h,y-hp1=Point(self.xmin,self.ymin)p2=Point(self.xmax,self.ymax)self.rect=Rectangle(p1,p2)self.rect.setFill(’lightgray’)self.rect.draw(win)self.label=Text(center,label)self.label.draw(win)self.deactivate()6控件创建按钮完整的类,并带有合适的构造方法(续上):defclicked(self,p):"按钮处于激活且p在矩形按钮范围时返回true"return(self.activeandself.xmin<=p.getX()<=self.xmaxandself.ymin<=p.getY()<=self.ymax)defgetLabel(self):"返回此按钮的标签字符串."returnself.label.getText()6控件创建按钮完整的类,并带有合适的构造方法(续上):defactivate(self):"设置按钮为激活状态."self.label.setFill(’black’)self.rect.setWidth(2)self.active=Truedefdeactivate(self):"设置按钮为禁用状态."self.label.setFill(’darkgrey’)self.rect.setWidth(1)self.active=False6控件构建骰子类骰子类目的:以图形的方式显示骰子的值。点数是圆形骰子的一面是正方形(通过Rectangle)6控件构建骰子类DieView将具有以下接口:一构造方法用于在窗口中创建一个骰子并以窗口、骰子的中心点和骰子的尺寸作为参数。二setValue方法用于更改视图以显示给定的值,并且要显示的值将作为参数传递。6控件构建骰子类DieView的核心是调整不同点的“开”和“关”,以表示骰子的当前值。一个简单的方法在所有可能的位置预先放置圆圈通过改变颜色来点亮或关闭点6控件构建骰子类构造方法:创建背景正方形和七个圆。setValue方法:根据骰子的值设置圆的颜色。6控件构建骰子类下面是我们的DieView类的代码:fromgraphicsimport*classDieView:"""DieView是一个显示标准六面骰子的的图像表示的部件."""def__init__(self,win,center,size):"""创建一个die视图:d1=DieView(myWin,Point(40,50),20)创建一个以(40,50)为中心,边长为20的die."""#首先定义一些标准值self.win=win#将此保存为稍后绘制点self.background="white"#die背景颜色self.foreground="black"#圆的颜色定义一组值确定骰子的各种属性。6控件构建骰子类下面是我们的DieView类的代码(续上):self.psize=0.1*size#各圆的半径hsize=size/2.0#骰子的一半offset=0.6*hsize#中心点到外部点的距离#创建一个正方形轮廓cx,cy=center.getX(),center.getY()p1=Point(cx-hsize,cy-hsize)p2=Point(cx+hsize,cy+hsize)rect=Rectangle(p1,p2)rect.draw(win)rect.setFill(self.background)6控件构建骰子类下面是我们的DieView类的代码(续上):#在标准位置创建7个圆self.pip1=self.__makePip(cx-offset,cy-offset)self.pip2=self.__makePip(cx-offset,cy)self.pip3=self.__makePip(cx-offset,cy+offset)self.pip4=self.__makePip(cx,cy)self.pip5=self.__makePip(cx+offset,cy-offset)self.pip6=self.__makePip(cx+offset,cy)self.pip7=self.__makePip(cx+offset,cy+offset)#画一个初值self.setValue(1)这个方法只是一个辅助函数,执行绘制每个点时所需的四行代码。以下划线或双下划线开头的方法名称,表示该方法对类是“私有的”6控件构建骰子类下面是我们的DieView类的代码(续上):def__makePip(self,x,y):"以(x,y)为圆心画圆的辅助函数"pip=Circle(Point(x,y),self.psize)pip.setFill(self.background)pip.setOutline(self.background)pip.draw(self.win)returnpip
defsetValue(self,value):"将此die设置为显示值."#turnallpipsoff6控件构建骰子类下面是我们的DieView类的代码(续上):self.pip1.setFill(self.background)self.pip2.setFill(self.background)self.pip3.setFill(self.background)self.pip4.setFill(self.background)self.pip5.setFill(self.background)self.pip6.setFill(self.background)self.pip7.setFill(self.background)#正确打开各圆的“开关”ifvalue==1:self.pip4.setFill(self.foreground)6控件构建骰子类下面是我们的DieView类的代码(续上):elifvalue==2:self.pip1.setFill(self.foreground)self.pip7.setFill(self.foreground)elifvalue==3:self.pip1.setFill(self.foreground)self.pip7.setFill(self.foreground)self.pip4.setFill(self.foreground)elifvalue==4:self.pip1.setFill(self.foreground)self.pip3.setFill(self.foreground)self.pip5.setFill(self.foreground)self.pip7.setFill(self.foreground)6控件构建骰子类下面是我们的DieView类的代码(续上):elifvalue==5:self.pip1.setFill(self.foreground)self.pip3.setFill(self.foreground)self.pip4.setFill(self.foreground)self.pip5.setFill(self.foreground)self.pip7.setFill(self.foreground)else:self.pip1.setFill(self.foreground)self.pip2.setFill(self.foreground)self.pip3.setFill(self.foreground)self.pip5.setFill(self.foreground)self.pip6.setFill(self.foreground)self.pip7.setFill(self.foreground)6控件主程序使用新控件的程序代码:fromrandomimportrandrangefromgraphicsimportGraphWin,PointfrombuttonimportButtonfromdieviewimportDieViewdefmain():#创建应用窗口win=GraphWin("摇骰子")win.setCoords(0,0,10,10)win.setBackground("green2")Button和Dieview类是从各自的模块导入的。6控件主程序使用新控件的程序代码(续上):#绘制界面部件die1=DieView(win,Point(3,7),2)die2=DieView(win,Point(7,7),2)rollButton=Button(win,Point(5,4.5),6,1,"投掷骰子")rollButton.activate()quitButton=Button(win,Point(5,1),2,1,"退出")#事件循环pt=win.getMouse()“投掷骰子”按钮最初处于激活状态。6控件主程序使用新控件的程序代码(续上):whilenotquitButton.clicked(pt):ifrollButton.clicked(pt):value1=randrange(1,7)die1.setValue(value1)value2=randrange(1,7)die2.setValue(value2)quitButton.activate()pt=win.getMouse()#停止win.close()main()程序的核心是事件循环。PART7动画炮弹第四课时7动画炮弹图炮弹飞行的界面让我们一起用新的对象思想为炮弹示例添加一个更好的界面吧!7动画炮弹绘制动画窗口第一步:创建一个图形窗口,并在底部画出合适的坐标线。下面是程序开始部分的代码:defmain():#创建动画窗口win=GraphWin("发射动画",640,480,autoflush=False)win.setCoords(-10,-10,210,155)#绘制坐标线Line(Point(-10,0),Point(210,0)).draw(win)#每个50米绘制刻度forxinrange(0,210,50): Text(Point(x,-5),str(x)).draw(win) Line(Point(x,0),Point(x,2)).draw(win)7动画炮弹绘制动画窗口默认情况:每当对象被要求更改时,都会立即更新图形对象的外观。
mycircle.setFill("green")更改圆的颜色图形命令A图形命令B图形命令C......通过将autoflush设置为false,可以让图形库在实际执行命令之前,允许一些命令在管道中准备好。7动画炮弹绘制动画窗口为什么不让图形命令立即生效呢?关闭autoflush通常能让图形程序更有效率。
原因:
■图形命令需要与底层的操作系统进行通信,与显示器硬件交换信息,比较耗时。■不能多次停止程序来执行一系列小图形命令。7动画炮弹绘制动画窗口关闭autoflush能在更新发生时精确地控制程序。
原因:
■动画期间,屏幕上可能会出现许多需要同步的更新。■关闭autoflush能进行许多更改,然后通过调用update函数时同时显示。这是做动画的常见方式!7动画炮弹绘制动画窗口炮弹飞行的界面程序设置下一帧的更改调用update()显示该帧一次只有一个对象移动动画时几乎总是要关闭autoflush7动画炮弹创建ShotTrackerProjectile类Circle类具备炮弹行为不是图形对象,无法绘制不具备炮弹行为是图形对象,可以绘制
Circle-Projectile混合体
ShotTracker7动画炮弹创建ShotTracker
ShotTrackerProjectile类Circle类ShotTracker的工作:
确保这些实例变量保持彼此同步。7动画炮弹创建ShotTrackerdef__init__(self,win,angle,velocity,height):"""win是反应炮弹发射角度、初速度和初始高度等信息的GrapWin窗口."""j=Projectile(angle,velocity,height)self.marker=Circle(Point(0,height),3)self.marker.setFill("red")self.marker.setOutline("red")self.marker.draw(win)
ShotTracker类的构造方法:7动画炮弹创建ShotTracker
ShotTrackerProjectile类Circle类目标:确保每次更新发生时,Projectile和Circle的位置都会适当地修改。update方法用适当的时间间隔调用它的update方法。通过计算它在x和y方向上移动的距离,以确定更新的抛体所在圆的中心。7动画炮弹创建ShotTrackerdefupdate(self,dt):"""让炮弹在飞行过程中在飞行dt秒"""#更新炮弹j.update(dt)
#将圆移动到新的炮弹位置center=self.marker.getCenter()dx=j.getX()-center.getX()dy=j.getY()-center.getY()self.marker.move(dx,dy)调用update方法处理Projectile和Circle:7动画炮弹创建ShotTrackerdefgetX(self):"""返回当前炮弹的圆心坐标x""" returnj.getX()defgetY(self):"""返回当前炮弹的圆心坐标y""" returnj.getY()defundraw(self):"""擦除炮弹""" self.marker.undraw()添加取值方法和擦除炮弹的方法:7动画炮弹创建输入对话框用户角度速度初始高度获取方法:■采用一些input语句■在GUI中通过使用对话框获取用户的输入7动画炮弹创建输入对话框图炮弹动画的自定义输入对话框一个对话框就是一种miniGUI。“Fire!”“Quit”启动炮弹退出程序7动画炮弹创建输入对话框classInputDialog:"""用于从用户获取模拟值(角度、速度和高度)的自定义窗口."""def__init__(self,angle,vel,height):"""创建并显示输入窗口"""self.win=win=GraphWin("初始值",200,300)win.setCoords(0,4.5,4,.5)Text(Point(1,1),"角度").draw(win)self.angle=Entry(Point(3,1),5).draw(win)self.angle.setText(str(angle))Text(Point(1,2),"速度").draw(win)创建窗口并在构造方法中绘制其内容:7动画炮弹创建输入对话框self.vel=Entry(Point(3,2),5).draw(win)
self.vel.setText(str(vel))Text(Point(1,3),"高度").draw(win)self.height=Entry(Point(3,3),5).draw(win)self.height.setText(str(height))self.fire=Button(win,Point(1,4),1.25,.5,"发射!")self.fire.activate()self.quit=Button(win,Point(3,4),1.25,.5,"停止")self.quit.activate()创建窗口并在构造方法中绘制其内容(续上):7动画炮弹创建输入对话框definteract(self):"""等待用户点击“停止”和“射击”按钮返回一个显示按钮的字符串"""whileTrue:pt=self.win.getMouse()ifself.quit.clicked(pt):return"Quit"ifself.fire.clicked(pt):return"Fire"使用自己的事件循环实现模态:等待鼠标点击,直到其中一个按钮被按下为止。7动画炮弹创建输入对话框defgetValues(self):"""返回输入值"""a=float(self.angle.getText())v=float(self.vel.getText())h=float(self.height.getText())returna,v,h
defclose(self):"""关闭输入窗口""" self.win.close()该方法的返回值用于指示哪个按钮被点击:添加一个操作来获取数据注意,输入的字符串将转换为浮点值,主程序只是获取数字注意,输入的字符串将转换为浮点值,主程序只是获取数字。7动画炮弹创建输入对话框dialog=InputDialog(45,40,2)choice=eract()ifchoice=="Fire!": angle,vel,height=dialog.getValues()从用户获取值:关闭对话框在每次输入需要时弹出一个新的对话框保持单个对话框打开,并与它进行多次交互7动画炮弹主事件循环defmain():#创建动画窗口win=GraphWin("炮弹动画",640,480,autoflush=False)win.setCoords(-10,-10,210,155)Line(Point(-10,0),Point(210,0)).draw(win)forxinrange(0,210,50):Text(Point(x,-5),str(x)).draw(win)Line(Point(x,0),Point(x,2)).draw(win)#事件循环,每次发射一枚炮弹angle,vel,height=45.0,40.0,2.0whileTrue:#与用户交互
主函数代码:7动画炮弹主事件循环inputwin=InputDialog(angle,vel,height)choice=eract()inputwin.close()ifchoice=="Quit":break#创建追踪,直到炮弹到达地方或离开窗口angle,vel,height=inputwin.getValues()shot=ShotTracker(win,angle,vel,height)while0<=shot.getY()and-10<shot.getX()<=210:
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 中学教师职称晋升制度
- 养老院入住老人心理健康监测制度
- 企业内部绩效考核制度
- 2026浙江台州市温岭市保安服务有限公司招聘保安员10人备考题库附答案
- 2026湖北恩施州宣恩茗智未来农业科技有限责任公司招聘1人备考题库附答案
- 2026湖南长沙市南雅星沙实验中学秋季学期教师招聘参考题库附答案
- 2026福建浦丰乡村发展集团有限公司及其下属企业招聘4人参考题库附答案
- 2026福建省面向江南大学选调生选拔工作参考题库附答案
- 2026辽宁科技学院面向部分高校招聘5人备考题库附答案
- 2026重庆飞驶特人力资源管理有限公司外派至华商国际会议中心(华商酒店)招聘1人备考题库附答案
- GB/T 43824-2024村镇供水工程技术规范
- 心力衰竭药物治疗的经济评估与成本效益分析
- 道路绿化养护投标方案(技术方案)
- QA出货检验日报表
- 校服采购投标方案
- 中外建筑史课件
- 母婴保健-助产技术理论考核试题题库及答案
- dd5e人物卡可填充格式角色卡夜版
- 海克斯康机器操作说明书
- GB/T 6003.1-1997金属丝编织网试验筛
- GB/T 24207-2009洗油酚含量的测定方法
评论
0/150
提交评论