打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
蛇年当然贪吃蛇 PyQt版贪吃蛇引子

蛇年到,贪吃蛇还是要出马下的,不准备写完整的程序,就让蛇跑起来,尾巴的长起来吧,蛇头有点动画得了。

 

先讲讲一些原理,蛇的脑袋使用键盘控制,因此重写他的keyPressEvent是势在必行的;

蛇身能够增长,运动,原来我计划是蛇身的每一块的坐标都会移动,可看见一个老哥说每次只要把尾巴移动的蛇脑袋那里,蛇脑袋再往前跑跑,蛇就动了,想想也是。蛇身是一块块组成的,很对的块形成一个组,变成蛇的身子,每次蛇要长长,只要在这个组里增加新的块即可。

因为使用了大量的图形,因此使用PyQt里面的带Graphics的那一堆类,重写QGraphicsItem的类或者子类,完成特定的功能,作为基本的元素;使用QGraphicsScene增加元素;QGraphicsView显示。

想使用动画Animation,看了看里面需要QObject来初始化,而QGraphicsItem居然不是继承自QObject的。因此我选择了QGraphicsObject作为基类来完善需求,他继承自QGraphicsItemQObject,可以使用动画的东西。

下面是完整的代码,先贴上:

View Code

 

资源文件:

View Code

 

开始讲解:

蛇的身体:

 1 class Segment(QGraphicsObject): 2     width = 50 3     def __init__(self,pos,parent=None): 4         super(Segment,self).__init__(parent) 5         self.setPixmap(QPixmap(":/img/book.jpg")) 6         self.setFlags(QGraphicsItem.ItemIsSelectable| 7                       QGraphicsItem.ItemIsMovable) 8         self.setPos(pos) 9 #    def setPos(self,pos):10 #        super(Segment,self).setPos(pos)11     def pixmap(self):12         return self.pix13     def setPixmap(self,pix):14         self.pix = pix15     def paint(self,painter,option,widget):16         painter.drawPixmap(QRect(0,0,self.width,self.width),17                            self.pixmap())        18     def boundingRect (self):19         return QRectF(0,0,self.width,self.width)

 

boundingRect是指明这个对象所占的矩形空间大小,因为有的时候你可以用鼠标选择图片,那么到底哪个区域呢,就是这个区域。

为了让蛇身蛇头的大小一致,设置了一个width作为蛇身的静态成员变量,并且在paint方法中重绘了图片,这样,不管图片多么大,画出来都会变成指定的大小,保证了蛇身蛇头相同的大小。

蛇头

 1 class Head(Segment): 2     speed = Segment.width     3     UP = 0 4     DOWN = 1 5     LEFT = 2 6     RIGHT = 3 7     TimeGap = 500 8     CURVE_TYPES = [(n, c) for n, c in QEasingCurve.__dict__.items() 9                 if isinstance(c, QEasingCurve.Type) and c != QEasingCurve.Custom]10     def _set_pos(self,pos):11         self.setPos(pos)12     def _get_pos(self):13         return self.pos()14     15     position = pyqtProperty(QPointF,fset = _set_pos,16                                      fget = _get_pos)17     def __init__(self,pos,parent=None):18         super(Head,self).__init__(pos,parent)19         self.setPixmap(QPixmap(":/img/person.jpg"))20         self.setFlags(QGraphicsItem.ItemIsSelectable|21                       QGraphicsItem.ItemIsMovable|22                       QGraphicsItem.ItemIsFocusable)23         self.curveType = QEasingCurve.OutBounce24         self.setPos(pos)25         self.setFocus(Qt.OtherFocusReason)26         self.direction = self.RIGHT27         self.timer = QTimer()28         self.timer.start(self.TimeGap)29         QObject.connect(self.timer, SIGNAL("timeout()"),30                      self.move)31     def move(self):32         pos = self.pos()33         # add animation,i dont know why must do this34         # else it wont be move35         if self.direction == self.LEFT:36             position = pos + QPointF(-self.speed,0)37         elif self.direction == self.RIGHT:38             position = pos + QPointF(self.speed,0)39         elif self.direction == self.UP:40             position = pos + QPointF(0,-self.speed)41         elif self.direction == self.DOWN:42             position = pos + QPointF(0,self.speed)43         44         pre = pos = position45         46         rect = QRectF(self.boundingRect())47         if self.direction == self.LEFT:48             self.setPos(pos+QPointF(-self.speed,0))49         elif self.direction == self.RIGHT:50             self.setPos(pos+QPointF(self.speed,0))51         elif self.direction == self.UP:52             self.setPos(pos+QPointF(0,-self.speed))53         elif self.direction == self.DOWN:54             self.setPos(pos+QPointF(0,self.speed))55         current = self.pos()56         self.animationChanged(pre, current)57 58     def animationChanged(self,pre,current):    59         self.anim= QPropertyAnimation(self,'position')60         self.anim.setStartValue(pre)61         self.anim.setEndValue(current)62         63         self.anim.setEasingCurve(self.curveType)64         self.anim.setDirection(self.TimeGap-200)65         self.anim.start()66     def keyPressEvent (self,event):67         key = event.key()68         if key == Qt.Key_Left:69             self.direction = self.LEFT70             return71         if key == Qt.Key_Right:72             self.direction = self.RIGHT73             return74         if key == Qt.Key_Up:75             self.direction = self.UP76             return77         if key == Qt.Key_Down:78             self.direction = self.DOWN79             return

 

设置了四个方向,并且重写了keyPressEvent方法,键盘按下的时候,改变self.direction的方向。

CURVE_TYPES是动画的所有可能取值,有40多种,可查阅QEasingCurve的文档。

蛇头的运动,是在move方法中实现的,如果不添加动画的话,第一段if else语句并不需要加,程序运行正常,可添加动画后,不知道为什么,每次运行到这里,pos的值与上一次运行move的值相同,不得已加了一段if else使得程序能正常运行。

animationChanged方法实现了蛇头的动画,self.anim.setEasingCurve(self.curveType)设置动画效果,为了让动画效果可以转变,因此保留了一个self.curveType变量。另外在构造时self.anim= QPropertyAnimation(self,'position'),第一个参数需要是QObject对象,而第二个参数需要时Qt的属性,因此在上面我生命了一个position属性,代码如下:

1     def _set_pos(self,pos):2         self.setPos(pos)3     def _get_pos(self):4         return self.pos()5     6     position = pyqtProperty(QPointF,fset = _set_pos,7                                      fget = _get_pos)

 

构造方法的定时器让蛇身连续移动,这个在我之前的博客中有介绍,不在啰嗦。

蛇身Group

 1 class SegmentGroup(QObject): 2     def __init__(self,scene,parent = None): 3         super(SegmentGroup,self).__init__(parent) 4         self.scene = scene 5         self.items = deque() 6          7     def addSegment(self,segment): 8         self.scene.addItem(segment) 9         self.items.append(segment)10         11     def move(self,headPos):12         if not self.items:return 13         tail = self.items.pop()14         tail.setPos(headPos)15         self.items.appendleft(tail)16     17     def tail(self):18         if not self.items:return19         return self.items[-1]

 

这是为了方便管理,将蛇头和整段身体分离而做的一个容器,因为主要实在第一节蛇身和蛇尾之间进行插入删除操作,所以使用python的deque()来实现这个容器,并且这个容器直接将蛇身添加到scene中去。代码木有神马特别需要说明的。

Scene

 1 class SnackScene(QGraphicsScene): 2     def __init__(self,parent=None): 3         super(SnackScene,self).__init__(parent) 4         self.setSceneRect(0,0,400,300) 5         self.segmentGroup = SegmentGroup(self) 6         self.segmentGroup.addSegment(Segment(QPointF(0,0))) 7          8         self.head = Head(QPointF(Segment.width,0)) 9         self.addItem(self.head)10         11         self.setFocusItem(self.head)12         self.timer = QTimer()13         self.timer.start(Head.TimeGap)14         15         self.connect(self.timer, SIGNAL("timeout()"),16                      self.segmentMove)17     def segmentMove(self):18         self.segmentGroup.move(self.head.pos())19     def mousePressEvent (self, event):20         if event.button() == Qt.LeftButton:21             tail = self.segmentGroup.tail()22             self.segmentGroup.addSegment(23                                          Segment(tail.pos())24                                          )25         elif event.button() == Qt.RightButton:26             curveType = random.choice(Head.CURVE_TYPES)27             self.head.curveType = QEasingCurve(curveType[1])

 

在这个类中添加数据,蛇头,蛇身管理器,设置了简单的增长蛇身的事件——按下鼠标左键;而按下右键则是更改蛇头的动画效果。将蛇身的移动与蛇头的移动对应起来,用一个与蛇头定时器时间相同的定时器来实现。

最后的form则是使用mainwindow在实现的应用程序。

 1 class Form(QMainWindow): 2     def __init__(self,parent=None): 3         super(Form,self).__init__(parent) 4         self.fileMenu = self.menuBar().addMenu("&File") 5         self.statusBar().showMessage("Ready", 5000) 6         self.scene = SnackScene() 7          8         self.view = QGraphicsView(self.scene) 9         self.view.setRenderHint(QPainter.SmoothPixmapTransform)10         self.view.setDragMode(QGraphicsView.RubberBandDrag)11         self.setCentralWidget(self.view)

 

后记:做这个依然是为了熟练qt,本来是想用图形界面做设计模式的,但是有些模式想不好写神马好,就像装饰者模式,用神马例子捏?

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
pyqt5 动画学习(三) 指定控件的移动轨迹
PyQt5之QGraphics 008 QGraphicsItem四连杆机构动画
人工蜂群(ABC)算法的群体智能优化问题方法(Python完整实现)
一步步用python制作游戏外挂
Python实例讲解 wxpython 最小到托盘及欢迎图片
Meta最新模型LLaMA语言模型细节与代码详解
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服