Python游戏引擎开发(二):创建窗口以及重绘界面
来源:互联网 发布:红旗linux系统 编辑:程序博客网 时间:2024/05/16 01:38
开发本地应用之前,我们得先有个窗口,用于显示界面。其次我们还得实现重绘机制,使游戏不停地刷新,达到动态化的目的。所以我们的引擎开发的第一个阶段就是创建窗口和重绘界面。
以下是之前的文章:
Python游戏引擎开发(一):序
Qt的渲染机制
在上一章《序》中我们讲到本次开发用到了PyQt,也就是Qt的Python版。在开始实现引擎功能之前我们要先了解一下Qt,这里先了解渲染机制。
在Qt中,绘画用到的类叫做QPainter
,顾名思义,就是个画家类吧。在这个类中,提供了非常多的方法用于操控这个“画家”,比如说叫他去画个圆,叫他去测量文字的尺寸,画家先生当然是不会推辞的。
在使用QPainter
时,需要给其一个绘画设备,也就类似于画家的画板。这个绘画设备就是一个QWidget
,部件类。这个类又提供了很多方法,比如说设置部件尺寸等。在Qt中,如果只有一个QWidget
,那么这个QWidget
就会成为窗口对象。
Qt中的事件以及信号和槽
Qt中的事件和JavaScript dom事件有点不同,不是通过注册一个事件类型和所对应的监听器来进行事件回调的,而是Qt直接在类中写好了监听器,通过重写这个监听器方法来执行自己的代码,也就是是说Js的事件是加了才会调用的,而Qt是自动加上了事件,通过修改监听器来执行自定义的代码。不过Qt对Js添加事件这一功能换了种说法——信号和槽。这个就好比《基督山伯爵》中菲尔兰出卖阿里国王那段一样——送来的是匕首则引爆炸弹炸掉所有的财产和家人,送来的是戒指则平安无事。这里的“送来匕首”就是信号,“引爆炸弹”就是槽;“送来戒指”是信号,“平安无事”为槽。你还可以把信号理解为事件名,槽理解为回调函数。
上文所提到的绘画需要在QWidget
的paintEvent
中进行。
QApplication
QApplication是整个应用程序的入口。这个类有点特别,具体特别在哪里呢?请看如下的代码:
app = QtGui.QApplication(sys.argv)# ...app.exec_()
也就是说,如果你要使用Qt里一系列的功能,请在实例化QApplication
后,调用exec_
方法前使用这些功能,也就是程序的入口是在代码中# ...
的位置。另外,QApplication
要接受一个参数,也就是代码中的sys.argv
,因此你需要在使用前引入sys模组。
这里就顺便提一下Qt的一些模组。Qt作为一款强大的引擎,难免有很多模组,不过我们大致要用的就只有QtGui
,QtCore
,QtWebKit
这些模组。开发游戏的话,QtGui
,QtCore
这两个模组就已经够用了。
库件基础类
基础部分大致讲完了,开始封装吧,首先封装的就是Stage
类。这个类大致就是管理一些全局的事物吧,比如说获取窗口的大小,设置窗口刷新频率,向最低层加入显示对象等等。不过在实现Stage前,我们得先创建一个库件中最底层的Object
类:
class Object(object): latestObjectIndex = 0 def __init__(self): Object.latestObjectIndex += 1 self.objectIndex = Object.latestObjectIndex
这个类作为库件中所有类的基类,方便以后给所有类扩展功能。然后是Stage类的构造器部分:
class Stage(Object): def __init__(self): super(Stage, self).__init__() self.parent = "root" self.width = 0 self.height = 0 self.speed = 0 self.app = None self.canvasWidget = None self.canvas = None self.timer = None self.childList = [] self.backgroundColor = None
可以看到,这个类中包含了许多全局属性。比如说width
和height
就是用来获取游戏界面宽高的,backgroundColor
用来设置底色。
接下来是_setCanvas
方法,用来完成创建绘画设备和QPainter
以及重绘计时器:
def _setCanvas(self, speed, title, width, height): self.speed = speed self.width = width self.height = height self.canvas = QtGui.QPainter() self.canvasWidget = CanvasWidget() self.canvasWidget.setWindowTitle(title) self.canvasWidget.setFixedSize(width, height) self.canvasWidget.show() self.timer = QtCore.QTimer() self.timer.setInterval(speed) self.timer.start(); QtCore.QObject.connect(self.timer, QtCore.SIGNAL("timeout()"), self.canvasWidget, QtCore.SLOT("update()"))
Qt的QTimer
是一个计时器类,通过调用setInterval
方法来设置计时时间,并循环计时。start
方法开启计时器。然后还用到了connect
方法来连接信号和槽,信号是“计时完毕”,槽是“刷新窗口”。这样一来,我们就可以通过这个信号槽装置来实现定时刷新界面的功能了。
在上面的代码中,我们还保存了绘画类QPainter
,方面在以后的代码中调用这个画笔。
上面的代码中还出现了CanvasWidget
类,它派生自QWidget
类,在这个类中,我们还会加入其他的功能,例如重写绘画事件监听器,键盘事件监听器等。构造器代码如下:
class CanvasWidget(QtGui.QWidget): def __init__(self): super(CanvasWidget, self).__init__()
当然,Qt为我们提供了不只是QWidget
这一种基础部件,还有输入框,按钮,窗口等高级部件,不过我们暂时用不上这些部件,更何况Qt在没有其他窗口部件的情况下,会默认把第一个QWidget
设置为窗口对象,所以这里的CanvasWidget
继承自QWidget
,实例化后,就是窗口对象。
上面用到的setWindowTitle
和setFixedSize
方法分别是用来设置窗口标题,窗口固定大小的。然后使用show
方法把这个部件显示出来。
因为整个游戏中只能有一个Stage
对象,为了方便全局公开使用,我们直接在库件里实例化这个类,作为唯一的舞台对象:
stage = Stage()
在上面的_setCanvas
方法中,我们用到了信号槽来完成界面的刷新,那么到底是怎么刷新的呢?当我们的计时器计时到指定的时间后会调用部件的update
槽,在这个槽里会调用到paintEvent
中,正如我们在“Qt中的事件以及信号和槽”中提到的,这个事件是在QWidget
中已经写好了的,我们只需要在CanvasWidget
中重写这个函数即可:
def paintEvent(self, event): stage._onShow()
调用到Stage
对象的_onShow
方法中:
def _onShow(self): self.canvas.begin(self.canvasWidget) if self.backgroundColor is not None: self.canvas.fillRect(0, 0, self.width, self.height, getColor(self.backgroundColor)) else: self.canvas.eraseRect(0, 0, self.width, self.height) self._showDisplayList(self.childList) self.canvas.end()
在这个函数中,我们首先用QPainter
的begin
方法绑定绘画设备。然后进行擦除界面,再通过_showDisplayList
方法进行重绘,最后结束绘画。值得注意的是_showDisplayList
方法,它通过遍历显示列表,将遍历得到对象进行重画:
def _showDisplayList(self, childList): for o in childList: if hasattr(o, "_show") and hasattr(o._show, "__call__"): o._show(self.canvas)
我们通过调用显示对象的_show
方法就可以完成绘制对象了。在后续的开发中,我们只需要为某个对象添加_show
方法,然后加入显示列表,就可以重绘该对象了。如果在_show
中加入绘画图片的功能,那么就得到了图片类,添加绘画文字的功能,就成了文本类。
在_onShow
方法中我们还用到了getColor
这个全局函数,用于把颜色名字字符串或者16进制值转化为QColor
,毕竟Qt这个文盲,认颜色就只认QColor
:
def getColor(color): if isinstance(color, QtGui.QColor): return color elif not color: return QtCore.Qt.transparent else: colorObj = QtGui.QColor() colorObj.setNamedColor(color) return colorObj
由于Stage
类是用来作为最底层显示列表的,所以加入addChild
,removeChild
方法用来完成添加和删除显示对象:
def addChild(self, child): if child is not None: child.parent = self self.childList.append(child) else: raise ValueError("parameter 'child' must be a display object.") def removeChild(self, child): if child is not None: self.childList.remove(child) child.parent = None else: raise ValueError("parameter 'child' must be a display object.")
最后添加init
全局函数用于初始化界面:
def init(speed, title, width, height, callback): stage.app = QtGui.QApplication(sys.argv) stage._setCanvas(speed, title, width, height) if not hasattr(callback, "__call__"): raise ValueError("parameter 'callback' must be a function.") callback() stage.app.exec_()
其中的QApplication
在上文已经有了详细的描述。
调用init
函数并传入对应的参数就可以看到空空的窗口了。
本文全部代码:
import sysfrom PyQt4 import QtGui, QtCoreclass Object(object): latestObjectIndex = 0 def __init__(self): Object.latestObjectIndex += 1 self.objectIndex = Object.latestObjectIndexclass CanvasWidget(QtGui.QWidget): def __init__(self): super(CanvasWidget, self).__init__() self.setMouseTracking(True) def paintEvent(self, event): stage._onShow()class Stage(Object): def __init__(self): super(Stage, self).__init__() self.parent = "root" self.width = 0 self.height = 0 self.speed = 0 self.app = None self.canvasWidget = None self.canvas = None self.timer = None self.childList = [] self.backgroundColor = None def _setCanvas(self, speed, title, width, height): self.speed = speed self.width = width self.height = height self.canvas = QtGui.QPainter() self.canvasWidget = CanvasWidget() self.canvasWidget.setWindowTitle(title) self.canvasWidget.setFixedSize(width, height) self.canvasWidget.show() self.timer = QtCore.QTimer() self.timer.setInterval(speed) self.timer.start(); QtCore.QObject.connect(self.timer, QtCore.SIGNAL("timeout()"), self.canvasWidget, QtCore.SLOT("update()")) def _onShow(self): self.canvas.begin(self.canvasWidget) if self.backgroundColor is not None: self.canvas.fillRect(0, 0, self.width, self.height, getColor(self.backgroundColor)) else: self.canvas.eraseRect(0, 0, self.width, self.height) self._showDisplayList(self.childList) self.canvas.end() def _showDisplayList(self, childList): for o in childList: if hasattr(o, "_show") and hasattr(o._show, "__call__"): o._show(self.canvas) def addChild(self, child): if child is not None: child.parent = self self.childList.append(child) else: raise ValueError("parameter 'child' must be a display object.") def removeChild(self, child): if child is not None: self.childList.remove(child) child.parent = None else: raise ValueError("parameter 'child' must be a display object.")stage = Stage()def init(speed, title, width, height, callback): stage.app = QtGui.QApplication(sys.argv) stage._setCanvas(speed, title, width, height) if not hasattr(callback, "__call__"): raise ValueError("parameter 'callback' must be a function.") callback() stage.app.exec_()def getColor(color): if isinstance(color, QtGui.QColor): return color elif not color: return QtCore.Qt.transparent else: colorObj = QtGui.QColor() colorObj.setNamedColor(color) return colorObj
预告:下一篇我们实现显示图片。
欢迎大家继续关注我的博客
转载请注明出处:Yorhom’s Game Box
http://blog.csdn.net/yorhomwang
- Python游戏引擎开发(二):创建窗口以及重绘界面
- 开发界面之窗口边框重绘
- iPhone游戏引擎CWGameEngine之一(创建全屏自定义窗口)
- Python游戏引擎开发(一):序
- 赛高游戏引擎重构(二)Game Config
- Irrlicht 游戏引擎开发 cooliris 界面(三)
- 基于引擎开发HTML5游戏实战(二)---游戏剧本
- 利用Android游戏引擎Angle开发界面
- libgdx游戏引擎教程(六)多游戏界面的实现(二)附源码
- pygame游戏之旅2:创建游戏窗口界面
- Kinect结合Unity3D引擎开发体感游戏(二)
- Python游戏引擎开发(三):显示图片
- Python游戏引擎开发(四):TextField文本类
- Python游戏引擎开发(六):动画的小小研究
- Python游戏引擎开发(七):绘制矢量图
- [python]pygame游戏开发学习(二)
- 游戏引擎剖析(二)
- 游戏引擎剖析(二)
- get、put、post、delete含义与区别
- android__常见布局
- Android接口回调,最简单的理解方式
- LeetCode 9_Palindrome Number
- UIStepper控件详解
- Python游戏引擎开发(二):创建窗口以及重绘界面
- Nginx and PHP-FPM Configuration and Optimizing Tips and Tricks
- Spring DAO vs Spring ORM vs Spring JDBC
- 使用Maven
- 华为机试之含23数字排序输出
- 一个google面试的准备清单
- Android 开发日志——Handler引起内存泄漏
- android___测试Test
- FilesCodingConvert--批量文件编码格式转换工具