下载

3下载券

加入VIP
  • 专属下载券
  • 上传内容扩展
  • 资料优先审核
  • 免费资料无限下载

上传资料

关闭

关闭

关闭

封号提示

内容

首页 QT参考文档与教程QT教程

QT参考文档与教程QT教程.doc

QT参考文档与教程QT教程

dlm_2011
2018-09-08 0人阅读 举报 0 0 0 暂无简介

简介:本文档为《QT参考文档与教程QT教程doc》,可适用于IT/计算机领域

Qt教程一第一章:Hello,World!第一个程序是一个简单的HelloWorld例子。它只包含你建立和运行Qt应用程序所需要的最少的代码。上面的图片是这个程序的快照。********************************************************************Qt教程一******************************************************************#include<qapplicationh>#include<qpushbuttonh>intmain(intargc,char**argv){QApplicationa(argc,argv)QPushButtonhello("Helloworld!",)helloresize(,)asetMainWidget(hello)helloshow()returnaexec()}一行一行地解说#include<qapplicationh>这一行包含了QApplication类的定义。在每一个使用Qt的应用程序中都必须使用一个QApplication对象。QApplication管理了各种各样的应用程序的广泛资源比如默认的字体和光标。#include<qpushbuttonh>这一行包含了QPushButton类的定义。参考文档的文件的最上部分提到了使用哪个类就必须包含哪个头文件的说明。QPushButton是一个经典的图形用户界面按钮用户可以按下去也可以放开。它管理自己的观感就像其它每一个QWidget。一个窗口部件就是一个可以处理用户输入和绘制图形的用户界面对象。程序员可以改变它的全部观感和它的许多主要的属性(比如颜色)还有这个窗口部件的内容。一个QPushButton可以显示一段文本或者一个QPixmap。intmain(intargc,char**argv){main()函数是程序的入口。几乎在使用Qt的所有情况下main()只需要在把控制转交给Qt库之前执行一些初始化然后Qt库通过事件来向程序告知用户的行为。argc是命令行变量的数量argv是命令行变量的数组。这是一个CC特征。它不是Qt专有的无论如何Qt需要处理这些变量(请看下面)。QApplicationa(argc,argv)a是这个程序的QApplicationewton){if(newton<)newton=if(f==newton)returnf=newtonemitforceChanged(f)}setForce()的实现和setAngle()很相似。唯一的不同是因为我们不显示力量值我们不需要重画窗口部件。voidCannonField::paintEvent(QPaintEvent*e){if(!e>rect()intersects(cannonRect()))return我们现在用只重画需要刷新得部分来优化绘画事件。首先我们检查是否不得不完全重画任何事我们返回是否不需要。QRectcr=cannonRect()QPixmappix(crsize())然后我们创建一个临时的pixmap我们用来不闪烁地画。所有的绘画操作都在这个pixmap中完成并且之后只用一步操作来把这个pixmap画到屏幕上。这是不闪烁绘画的本质:一次准确地在每一个像素上画。更少你会得到绘画错误。更多你会得到闪烁。在这个例子中这个并不重要当代码被写时仍然是很慢的机器导致闪烁但以后不会再闪烁了。我们由于教育目的保留了这些代码。pixfill(this,crtopLeft())我们用这个pixmap来充满这个窗口部件的背景。QPainterp(pix)psetBrush(blue)psetPen(NoPen)ptranslate(,pixheight())pdrawPie(QRect(,,,),,*)protate(ang)pdrawRect(QRect(,,,))pend()我们就像第九章中一样画但是现在我们是在pixmap上画。在这一点上我们有一个绘画工具变量和一个pixmap看起来相当正确但是我们还没有在屏幕上画呢。pbegin(this)pdrawPixmap(crtopLeft(),pix)所以我们在CannonField上面打开绘图工具并在这之后画这个pixmap。这就是全部了。在顶部和底部各有一对线并且这个代码是不闪烁的。QRectCannonField::cannonRect()const{QRectr(,,,)rmoveBottomLeft(rect()bottomLeft())returnr}这个函数返回一个在窗口部件坐标中封装加农炮的矩形。首先我们创建一个*大小的矩形然后移动它使它的左下角和窗口部件自己的左下角相等。QWidget::rect()函数在窗口部件自己的坐标(左上角是,)中返回窗口部件封装的矩形。tmaincppMyWidget::MyWidget(QWidget*parent,constchar*name):QWidget(parent,name){构造函数也是一样但是已经加入了一些东西。LCDRange*force=newLCDRange(this,"force")force>setRange(,)我们加入了第二个LCDRange用来设置力量。connect(force,SIGNAL(valueChanged(int)),cannonField,SLOT(setForce(int)))connect(cannonField,SIGNAL(forceChanged(int)),force,SLOT(setValue(int)))我们把force窗口部件和cannonField窗口部件连接起来就和我们对angle窗口部件做的一样。QVBoxLayout*leftBox=newQVBoxLayoutgrid>addLayout(leftBox,,)leftBox>addWidget(angle)leftBox>addWidget(force)在第九章我们把angle放到了布局的左下单元。现在我们想在这个单元中放入两个窗口部件所一个我们用了一个垂直的盒子把这个垂直的盒子放到这个网格单元中并且把angle和force放到这个垂直的盒子中。force>setValue()我们初始化力量的值为。行为闪烁已经走了并且我们还有一个力量控制。(请看编译来学习如何创建一个makefile和连编应用程序。)练习让加农炮的炮筒的大小依赖于力量。把加农炮放到右下角。试着加入一个更好的键盘接口。例如用和来增加或者减少力量用enter来发射。提示:QAccel和在LCDRange中新建addStep()和subtractStep()就像QSlider::addStep()。如果你被左面和右面键所苦恼(我就是!)试着都改变!现在你可以进行第十一章了。Qt教程一第十一章:给它一个炮弹在这个例子里我们介绍了一个定时器来实现动画的射击。tlcdrangeh包含LCDRange类定义。tlcdrangecpp包含LCDRange类实现。tcannonh包含CannonField类定义。tcannoncpp包含CannonField类实现。tmaincpp包含MyWidget和main。一行一行地解说tcannonhCannonField现在就有了射击能力。voidshoot()当炮弹不在空中中调用这个槽就会使加农炮射击。privateslots:voidmoveShot()当炮弹正在空中时这个私有槽使用一个定时器来移动射击。private:voidpaintShot(QPainter*)这个函数来画射击。QRectshotRect()const当炮弹正在空中的时候这个私有函数返回封装它所占用空间的矩形否则它就返回一个没有定义的矩形。inttimerCountQTimer*autoShootTimerfloatshootangfloatshootf}这些私有变量包含了描述射击的信息。timerCount保留了射击进行后的时间。shootang是加农炮射击时的角度shootf是射击时加农炮的力量。tcannoncpp#include<mathh>我们包含了数学库因为我们需要使用sin()和cos()函数。CannonField::CannonField(QWidget*parent,constchar*name):QWidget(parent,name){ang=f=timerCount=autoShootTimer=newQTimer(this,"movementhandler")connect(autoShootTimer,SIGNAL(timeout()),this,SLOT(moveShot()))shootang=shootf=setPalette(QPalette(QColor(,,)))}我们初始化我们新的私有变量并且把QTimer::timeout()信号和我们的moveShot()槽相连。我们会在定时器超时的时候移动射击。voidCannonField::shoot(){if(autoShootTimer>isActive())returntimerCount=shootang=angshootf=fautoShootTimer>start()}只要炮弹不在空中这个函数就会进行一次射击。timerCount被重新设置为零。shootang和shootf设置为当前加农炮的角度和力量。最后我们开始这个定时器。voidCannonField::moveShot(){QRegionr(shotRect())timerCountQRectshotR=shotRect()if(shotRx()>width()||shotRy()>height())autoShootTimer>stop()elser=runite(QRegion(shotR))repaint(r)}moveShot()是一个移动射击的槽当QTimer开始的时候每毫秒被调用一次。它的任务就是计算新的位置重新画屏幕并把炮弹放到新的位置并且如果需要的话停止定时器。首先我们使用QRegion来保留旧的shotRect()。QRegion可以保留任何种类的区域并且我们可以用它来简化绘画过程。shotRect()返回现在炮弹所在的矩形稍后我们会详细介绍。然后我们增加timerCount用它来实现炮弹在它的轨迹中移动的每一步。下一步我们算出新的炮弹的矩形。如果炮弹已经移动到窗口部件的右面或者下面的边界我们停止定时器或者添加新的shotRect()到QRegion。最后我们重新绘制QRegion。这将会发送一个单一的绘画事件但仅仅有一个到两个举行需要刷新。voidCannonField::paintEvent(QPaintEvent*e){QRectupdateR=e>rect()QPainterp(this)if(updateRintersects(cannonRect()))paintCannon(p)if(autoShootTimer>isActive()updateRintersects(shotRect()))paintShot(p)}绘画事件函数在前一章中已经被分成两部分了。现在我们得到的新的矩形区域需要绘画检查加农炮和或炮弹是否相交并且如果需要的话调用paintCannon()和或paintShot()。voidCannonField::paintShot(QPainter*p){p>setBrush(black)p>setPen(NoPen)p>drawRect(shotRect())}这个私有函数画一个黑色填充的矩形作为炮弹。我们把paintCannon()的实现放到一边它和前一章中的paintEvent()一样。QRectCannonField::shotRect()const{constdoublegravity=doubletime=timerCountdoublevelocity=shootfdoubleradians=shootang*doublevelx=velocity*cos(radians)doublevely=velocity*sin(radians)doublex=(barrelRectright())*cos(radians)doubley=(barrelRectright())*sin(radians)doublex=xvelx*timedoubley=yvely*time*gravity*time*timeQRectr=QRect(,,,)rmoveCenter(QPoint(qRound(x),height()qRound(y)))returnr}这个私有函数计算炮弹的中心点并且返回封装炮弹的矩形。它除了使用自动增加所过去的时间的timerCount之外还使用初始时的加农炮的力量和角度。运算公式使用的是有重力的环境下光滑运动的经典牛顿公式。简单地说我们已经选择忽略爱因斯坦理论的结果。我们在一个y坐标向上增加的坐标系统中计算中心点。在我们计算出中心点之后我们构造一个*大小的QRect并把它的中心移动到我们上面所计算出的中心点。同样的操作我们把这个点移动到窗口部件的坐标系统(请看坐标系统)。qRound()函数是一个在qglobalh中定义的内嵌函数(被其它所有Qt头文件包含)。qRound()把一个双精度实数变为最接近的整数。tmaincppclassMyWidget:publicQWidget{public:MyWidget(QWidget*parent=,constchar*name=)}唯一的增加是Shoot按钮。QPushButton*shoot=newQPushButton("Shoot",this,"shoot")shoot>setFont(QFont("Times",,QFont::Bold))在构造函数中我们创建和设置Shoot按钮就像我们对Quit按钮所做的那样。注意构造函数的第一个参数是按钮的文本并且第三个是窗口部件的名称。connect(shoot,SIGNAL(clicked()),cannonField,SLOT(shoot()))把Shoot按钮的clicked()信号和CannonField的shoot()槽连接起来。行为Thecannoncanshoot,butthere'snothingtoshootat(请看编译来学习如何创建一个makefile和连编应用程序。)练习用一个填充的圆来表示炮弹。提示:QPainter::drawEllipse()会对你有所帮助。当炮弹在空中的时候改变加农炮的颜色。现在你可以进行第十二章了。Qt教程一第十一章:悬在空中的砖在这个例子中我们扩展我们的LCDRange类来包含一个文本标签。我们也会给射击提供一个目标。tlcdrangeh包含LCDRange类定义。tlcdrangecpp包含LCDRange类实现。tcannonh包含CannonField类定义。tcannoncpp包含CannonField类实现。tmaincpp包含MyWidget和main。一行一行地解说tlcdrangehLCDRange现在有了一个文本标签。classQLabel我们名称声明QLabel因为我们将在这个类声明中使用一个QLabel的指针。classLCDRange:publicQVBox{QOBJECTpublic:LCDRange(QWidget*parent=,constchar*name=)LCDRange(constchar*s,QWidget*parent=,constchar*name=)我们添加了一个新的构造函数这个构造函数在父对象和名称之外还设置了标签文本。constchar*text()const这个函数返回标签文本。voidsetText(constchar*)这个槽设置标签文本。private:voidinit()因为我们现在有了两个构造函数我们选择把通常的初始化放在一个私有的init()函数。QLabel*label我们还有一个新的私有变量:一个QLabel。QLabel是一个Qt标准窗口部件并且可以显示一个有或者没有框架的文本或者pixmap。tlcdrangecpp#include<qlabelh>这里我们包含了QLabel类定义。LCDRange::LCDRange(QWidget*parent,constchar*name):QVBox(parent,name){init()}这个构造函数调用了init()函数它包括了通常的初始化代码。LCDRange::LCDRange(constchar*s,QWidget*parent,constchar*name):QVBox(parent,name){init()setText(s)}这个构造函数首先调用了init()然后设置标签文本。voidLCDRange::init(){QLCDNumber*lcd=newQLCDNumber(,this,"lcd")slider=newQSlider(Horizontal,this,"slider")slider>setRange(,)slider>setValue()label=newQLabel("",this,"label")label>setAlignment(AlignCenter)connect(slider,SIGNAL(valueChanged(int)),lcd,SLOT(display(int)))connect(slider,SIGNAL(valueChanged(int)),SIGNAL(valueChanged(int)))setFocusProxy(slider)}lcd和slider的设置和上一章一样。接下来我们创建一个QLabel并且让它的内容中间对齐(垂直方向和水平方向都是)。connect()语句也来自于上一章。constchar*LCDRange::text()const{returnlabel>text()}这个函数返回标签文本。voidLCDRange::setText(constchar*s){label>setText(s)}这个函数设置标签文本。tcannonhCannonField现在有两个新的信号:hit()和missed()。另外它还包含一个目标。voidnewTarget()这个槽在新的位置生成一个新的目标。signals:voidhit()voidmissed()hit()信号是当炮弹击中目标的时候被发射的。missed()信号是当炮弹移动超出了窗口部件的右面或者下面的边界时被发射的(例如当然这种情况下它将不会击中目标)。voidpaintTarget(QPainter*)这个私有函数绘制目标。QRecttargetRect()const这个私有函数返回一个封装了目标的矩形。QPointtarget这个私有变量包含目标的中心点。tcannoncpp#include<qdatetimeh>我们包含了QDate、QTime和QDateTime类定义。#include<stdlibh>我们包含了stdlib库因为我们需要rand()函数。newTarget()这一行已经被添加到了构造函数中。它为目标创建一个“随机的”位置。实际上newTarget()函数还试图绘制目标。因为我们在一个构造函数中CannonField窗口部件还是不可以见的。Qt保证在一个隐藏的窗口部件中调用repaint()是没有害处的。voidCannonField::newTarget(){staticboolfirsttime=TRUEif(firsttime){firsttime=FALSEQTimemidnight(,,)srand(midnightsecsTo(QTime::currentTime()))}QRegionr(targetRect())target=QPoint(rand(),rand())repaint(runite(targetRect()))}这个私有函数创建了一个在新的“随机的”位置的目标中心点。我们使用rand()函数来获得随机整数。rand()函数通常会在你每次运行这个程序的时候返回同样一组值。这就会使每次运行的时候目标都出现在同样的位置。为了避免这些我们必须在这个函数第一次被调用的时候设置一个随机种子。为了避免同样一组数据随机种子也必须是随机的。解决方法就是使用从午夜到现在的秒数作为一个假的随机值。首先我们创建一个静态布尔型局域变量。静态变量就是在调用函数前后都保证它的值不变。if测试会成功因为只有当这个函数第一次被调用的时候我们在if块中把firsttime设置为FALSE。然后我们创建一个QTime对象midnight它将会提供时间::。接下来我们获得从午夜到现在所过的秒数并且使用它作为一个随机种子。请看QDate、QTime和QDateTime文档来获得更多的信息。最后我们计算目标的中心点。我们把它放在一个矩形中(x=y=width=height=)(例如可能的x和y值是x=~和y=~)在一个我们把窗口边界的下边界作为y的零点并且y向上增加X轴向通常一样左边界为零点并且x向右增加的坐标系统中。通过经验我们发现这都在炮弹的射程之内。注意rand()返回一个>=的随机整数。voidCannonField::moveShot(){QRegionr(shotRect())timerCountQRectshotR=shotRect()定时器时间这部分和上一章一样。if(shotRintersects(targetRect())){autoShootTimer>stop()emithit()if语句检查炮弹矩形和目标矩形是否相交。如果是的炮弹击中了目标(哎哟!)。我们停止射击定时器并且发射hit()信号来告诉外界目标已经被破坏并返回。注意我们可以在这个点上创建一个新的目标但是因为CannonField是一个组件所以我们要把这样的决定留给组件的使用者。}elseif(shotRx()>width()||shotRy()>height()){autoShootTimer>stop()emitmissed()这个if语句和上一章一样除了现在它发射missed()信号告诉外界这次失败。}else{函数的其余部分和以前一样。CannonField::paintEvent()isasbefore,exceptthatthishasbeenadded:if(updateRintersects(targetRect()))paintTarget(p)这两行确认在需要的时候目标也被绘制。voidCannonField::paintTarget(QPainter*p){p>setBrush(red)p>setPen(black)p>drawRect(targetRect())}这个私有函数绘制目标一个由红色填充有黑色边框的矩形。QRectCannonField::targetRect()const{QRectr(,,,)rmoveCenter(QPoint(targetx(),height()targety()))returnr}这个私有函数返回封装目标的矩形。从newTarget()中所得的target点使用点在窗口部件的下边界的y。我们在调用QRect::moveCenter()之前在窗口坐标中计算这个点。我们选择这个坐标映射的原因是在目标和窗口部件的下边界之间垂直距离。记住这些可以让用户或者程序在任何时候都可以重新定义窗口部件的大小。tmaincpp在MyWidget类中没有新的成员了但是我们稍微改变了一下构造函数来设置新的LCDRange的文本标签。LCDRange*angle=newLCDRange("ANGLE",this,"angle")我们设置角度的文本标签为“ANGLE”。LCDRange*force=newLCDRange("FORCE",this,"force")我们设置力量的文本标签为“FORCE”。行为加农炮会向目标射击当它射中目标的时候一个新的目标会自动被创建。LCDRange窗口部件看起来有一点奇怪QVBox中内置的管理给了标签太多的空间而其它的却不够。我们将会在下一章修正这一点。(请看编译来学习如何创建一个makefile和连编应用程序。)练习创建一个作弊的按钮当按下它的时候让CannonField画出炮弹在五秒中的轨迹。如果你在上一章做了“圆形炮弹”的练习试着改变shotRect()为可以返回一个QRegion的shotRegion()这样你就可以真正的做到准确碰撞检测。做一个移动目标。确认目标被完全创建在屏幕上。确认加农炮窗口部件不能被重新定义大小这样目标不是可见的。提示:QWidget::setMinimumSize()是你的朋友。不容易的是在同一时刻让几个炮弹在空中成为可能。提示:建立一个炮弹对象。现在你可以进行第十三章了。Qt教程一第十三章:游戏结束在这个例子中我们开始研究一个带有记分的真正可玩的游戏。我们给MyWidget一个新的名字(GameBoard)并添加一些槽。我们把定义放在gamebrdh并把实现放在gamebrdcpp。CannonField现在有了一个游戏结束状态。在LCDRange中的布局问题已经修好了。tlcdrangeh包含LCDRange类定义。tlcdrangecpp包含LCDRange类实现。tcannonh包含CannonField类定义。tcannoncpp包含CannonField类实现。tgamebrdh包含GameBoard类定义。tgamebrdcpp包含GameBoard类实现。tmaincpp包含MyWidget和main。一行一行地解说tlcdrangeh#include<qwidgeth>classQSliderclassQLabelclassLCDRange:publicQWidget我们继承了QWidget而不是QVBox。QVBox是非常容易使用的但是它也显示了它的局域性所以我们选择使用更加强大和稍微有一些难的QVBoxLayout。(和你记忆中的一样QVBoxLayout不是一个窗口部件它管理窗口部件。)tlcdrangecpp#include<qlayouth>我们现在需要包含qlayouth来获得其它布局管理API。LCDRange::LCDRange(QWidget*parent,constchar*name):QWidget(parent,name)我们使用一种平常的方式继承QWidget。另外一个构造函数作了同样的改动。init()没有变化除了我们在最后加了几行:QVBoxLayout*l=newQVBoxLayout(this)我们使用所有默认值创建一个QVBoxLayout管理这个窗口部件的子窗口部件。l>addWidget(lcd,)AtthetopweaddtheQLCDNumberwithanonzerostretchl>addWidget(slider)l>addWidget(label)然后我们添加另外两个它们都使用默认的零伸展因数。这个伸展控制是QVBoxLayout(和QHBoxLayout和QGridLayout)所提供的而像QVBox这样的类却不提供。在这种情况下我们让QLCDNumber可以伸展而其它的不可以。tcannonhCannonField现在有一个游戏结束状态和一些新的函数。boolgameOver()const{returngameEnded}如果游戏结束了这个函数返回TRUE或者如果游戏还在继续返回FALSE。voidsetGameOver()voidrestartGame()这里是两个新槽:setGameOver()和restartGame()。voidcanShoot(bool)这个新的信号表明CannonField使shoot()槽生效的状态。我们将在下面使用它用来使Shoot按钮生效或失效。boolgameEnded这个私有变量包含游戏的状态。TRUE说明游戏结束FALSE说明游戏还将继续。tcannoncppgameEnded=FALSE这一行已经被加入到构造函数中。最开始的时候游戏没有结束(对于玩家是很幸运的:)。voidCannonField::shoot(){if(isShooting())returntimerCount=shootang=angshootf=fautoShootTimer>start()emitcanShoot(FALSE)}我们添加一个新的isShooting()函数所以shoot()使用它替代直接的测试。同样shoot告诉世界CannonField现在不可以射击。voidCannonField::setGameOver(){if(gameEnded)returnif(isShooting())autoShootTimer>stop()gameEnded=TRUErepaint()}这个槽终止游戏。它必须被CannonField外面的调用因为这个窗口部件不知道什么时候终止游戏。这是组件编程中一条重要设计原则。我们选择使组件可以尽可能灵活以适应不同的规则(比如在一个首先射中十次的人胜利的多人游戏版本可能使用不变的CannonField)。如果游戏已经被终止我们立即返回。如果游戏会继续到我们的设计完成设置游戏结束标志并且重新绘制整个窗口部件。voidCannonField::restartGame(){if(isShooting())autoShootTimer>stop()gameEnded=FALSErepaint()emitcanShoot(TRUE)}这个槽开始一个新游戏。如果炮弹还在空中我们停止设计。然后我们重置gameEnded变量并重新绘制窗口部件。就像hit()或miss()一样moveShot()同时也发射新的canShoot(TRUE)信号。CannonField::paintEvent()的修改:voidCannonField::paintEvent(QPaintEvent*e){QRectupdateR=e>rect()QPainterp(this)if(gameEnded){psetPen(black)psetFont(QFont("Courier",,QFont::Bold))pdrawText(rect(),AlignCenter,"GameOver")}绘画事件已经通过如果游戏结束比如gameEnded是TRUE就显示文本“GameOver”而被增强了。我们在这里不怕麻烦来检查更新矩形是因为在游戏结束的时候速度不是关键性的。为了画文本我们先设置了黑色的画笔当画文本的时候画笔颜色会被用到。接下来我们选择Courier字体中的号加粗字体。最后我们在窗口部件的矩形中央绘制文本。不幸的是在一些系统中(特别是使用Unicode的X服务器)它会用一小段时间来载入如此大的字体。因为Qt缓存字体我们只有第一次使用这个字体的时候才会注意到这一点。if(updateRintersects(cannonRect()))paintCannon(p)if(isShooting()updateRintersects(shotRect()))paintShot(p)if(!gameEndedupdateRintersects(targetRect()))paintTarget(p)}我们只有在设计的时候画炮弹在玩游戏的时候画目标(这也就是说当游戏没有结束的时候)。tgamebrdh这个文件是新的。它包含最后被用来作为MyWidget的GameBoard类的定义。classQPushButtonclassLCDRangeclassQLCDNumberclassCannonField#include"lcdrangeh"#include"cannonh"classGameBoard:publicQWidget{QOBJECTpublic:GameBoard(QWidget*parent=,constchar*name=)protectedslots:voidfire()voidhit()voidmissed()voidnewGame()private:QLCDNumber*hitsQLCDNumber*shotsLeftCannonField*cannonField}我们现在已经添加了四个槽。这些槽都是被保护的只在内部使用。我们也已经加入了两个QLCDNumbers(hits和shotsLeft)用来显示游戏的状态。tgamebrdcpp这个文件是新的。它包含最后被用来作为MyWidget的GameBoard类的实现我们已经在GameBoard的构造函数中做了一些修改。cannonField=newCannonField(this,"cannonField")cannonField现在是一个成员变量所以我们在使用它的时候要小心地改变它的构造函数。(Trolltech的好程序员从来不会忘记这点但是我就忘了。告诫程序员-如果“programmor”是拉丁语至少。无论如何返回代码。)connect(cannonField,SIGNAL(hit()),this,SLOT(hit()))connect(cannonField,SIGNAL(missed()),this,SLOT(missed()))这次当炮弹射中或者射失目标的时候我们想做些事情。所以我们把CannonField的hit()和missed()信号连接到这个类的两个被保护的同名槽。connect(shoot,SIGNAL(clicked()),SLOT(fire()))以前我们直接把Shoot按钮的clicked()信号连接到CannonField的shoot()槽。这次我们想跟踪射击的次数所以我们把它改为连接到这个类里面一个被保护的槽。注意当你用独立的组件工作的时候改变程序的行为是多么的容易。connect(cannonField,SIGNAL(canShoot(bool)),shoot,SLOT(setEnabled(bool)))我们也使用cannonField的canShoot()信号来适当地使Shoot按钮生效和失效。QPushButton*restart=newQPushButton("NewGame",this,"newgame")restart>setFont(QFont("Times",,QFont::Bold))connect(restart,SIGNAL(clicked()),this,SLOT(newGame()))我们创建、设置并且连接这个NewGame按钮就像我们对其它按钮所做的一样。点击这个按钮就会激活这个窗口部件的newGame()槽。hits=newQLCDNumber(,this,"hits")shotsLeft=newQLCDNumber(,this,"shotsleft")QLabel*hitsL=newQLabel("HITS",this,"hitsLabel")QLabel*shotsLeftL=newQLabel("SHOTSLEFT",this,"shotsleftLabel")我们创建了四个新的窗口部件。注意我们不怕麻烦的把QLabel窗口部件的指针保留到GameBoard类中是因为我们不想再对它们做什么了。当GameBoard窗口部件被销毁的时候Qt将会删除它们并且布局类会适当地重新定义它们的大小。QHBoxLayout*topBox=newQHBoxLayoutgrid>addLayout(topBox,,)topBox>addWidget(shoot)topBox>addWidget(hits)topBox>addWidget(hitsL)topBox>addWidget(shotsLeft)topBox>addWidget(shotsLeftL)topBox>addStretch()topBox>addWidget(restart)右上单元格的窗口部件的数量正在变大。从前它是空的现在它是完全充足的我们把它们放到布局中来更好的看到它们。注意我们让所有的窗口部件获得它们更喜欢的大小改为在NewGame按钮的左边加入了一个可以自由伸展的东西。newGame()}我们已经做完了所有关于GameBoard的构造所以我们使用newGame()来开始。(newGame()是一个槽但是就像我们所说的槽也可以像普通的函数一样使用。)voidGameBoard::fire(){if(cannonField>gameOver()||cannonField>isShooting())returnshotsLeft>display(shotsLeft>intValue())cannonField>shoot()}这个函数进行射击。如果游戏结束了或者还有一个炮弹在空中我们立即返回。我们减少炮弹的数量并告诉加农炮进行射击。voidGameBoard::hit(){hits>display(hits>intValue())if(shotsLeft>intValue()==)cannonField>setGameOver()elsecannonField>newTarget()}当炮弹击中目标的时候这个槽被激活。我们增加射中的数量。如果没有炮弹了游戏就结束了。否则我们会让CannonField生成新的目标。voidGameBoard::missed(){if(shotsLeft>intValue()==)cannonField>setGameOver()}当炮弹射失目标的时候这个槽被激活如果没有炮弹了游戏就结束了。voidGameBoard::newGame(){shotsLeft>display()hits>display()cannonField>restartGame()cannonField>newTarget()}当用户点击Restart按钮的时候这个槽被激活。它也会被构造函数调用。首先它把炮弹的数量设置为。注意这里是我们在程序中唯一设置炮弹数量的地方。把它改变为你所想要的游戏规则。接下来我们重置射中的数量重新开始游戏并且生成一个新的目标。tmaincpp这个文件仅仅被删掉了一部分。MyWidget没了并且唯一剩下的是main()函数除了名称的改变其它都没有改变。行为射中的和剩余炮弹的数量被显示并且程序继续跟踪它们。游戏可以结束了并且还有一个按钮可以开始一个新游戏。(请看编译来学习如何创建一个makefile和连编应用程序。)练习添加一个随机的风的因素并把它显示给用户看。当炮弹击中目标的时候做一些飞溅的效果。实现多个目标。现在你可以进行第十四章了。上一章下一章教程一主页Qt教程一第十四章:面对墙壁这是最后的例子:一个完整的游戏。我们添加键盘快捷键并引入鼠标事件到CannonField。我们在CannonField周围放一个框架并添加一个障碍物(墙)使这个游戏更富有挑战性。tlcdrangeh包含LCDRange类定义。tlcdrangecpp包含LCDRange类实现。tcannonh包含CannonField类定义。tcannoncpp包含CannonField类实现。tgamebrdh包含GameBoard类定义。tgamebrdcpp包含GameBoard类实现。tmaincpp包含MyWidget和main。一行一行地解说tcannonhCannonField现在可以接收鼠标事件使得用户可以通过点击和拖拽炮筒来瞄准。CannonField也有一个障碍物的墙。protected:voidpaintEvent(QPaintEvent*)voidmousePressEvent(QMouseEvent*)voidmouseMoveEvent(QMouseEvent*)voidmouseReleaseEvent(QMouseEvent*)除了常见的事件处理器CannonField实现了三个鼠标事件处理器。名称说明了一切。voidpaintBarrier(QPainter*)这个私有函数绘制了障碍物墙。QRectbarrierRect()const这个私有寒暑返回封装障碍物的矩形。boolbarrelHit(constQPoint)const这个私有函数检查是否一个点在加农炮炮筒的内部。boolbarrelPressed当用户在炮筒上点击鼠标并且没有放开的话这个私有变量为TRUE。tcannoncppbarrelPressed=FALSE这一行被添加到构造函数中。最开始的时候鼠标没有在炮筒上点击。}elseif(shotRx()>width()||shotRy()>height()||shotRintersects(barrierRect())){现在我们有了一个障碍物这样就有了三种射失的方法。我们来测试一下第三种。voidCannonField::mousePressEvent(QMouseEvent*e){if(e>button()!=LeftButton)returnif(barrelHit(e>pos()))barrelPressed=TRUE}这是一个Qt事件处理器。当鼠标指针在窗口部件上用户按下鼠标的按键时它被调用。如果事件不是由鼠标左键产生的我们立即返回。否则我们检查鼠标指针是否在加农炮的炮筒内。如果是的我们设置barrelPressed为TRUE。注意pos()函数返回的是窗口部件坐标系统中的点。voidCannonField::mouseMoveEvent(QMouseEvent*e){if(!barrelPressed)returnQPointpnt=e>pos()if(pntx()<=)pntsetX()if(pnty()>=height())pntsetY(height())doublerad=atan(((double)rect()bottom()pnty())pntx())setAngle(qRound(rad*))}这是另外一个Qt事件处理器。当用户已经在窗口部件中按下了鼠标按键并且移动拖拽鼠标时它被调用。(你可以让Qt在没有鼠标按键被按下的时候发送鼠标移动事件。请看QWidget::setMouseTracking()。)这个处理器根据鼠标指针的位置重新配置加农炮的炮筒。首先如果炮筒没有被按下我们返回。接下来我们获得鼠标指针的位置。如果鼠标指针到了窗口部件的左面或者下面我们调整鼠标指针使它返回到窗口部件中。然后我们计算在鼠标指针和窗口部件的左下角所构成的虚构的线和窗口部件下边界的角度。最后我们把加农炮的角度设置为我们新算出来的角度。记住要用setAngle()来重新绘制加农炮。voidCannonField::mouseReleaseEvent(QMouseEvent*e){if(e>button()==LeftButton)barrelPressed=FALSE}只要用户释放鼠标按钮并且它是在窗口部件中按下的时候这个Qt事件处理器就会被调用。如果鼠标左键被释放我们就会确认炮筒不再被按下了。绘画事件包含了下述额外的两行:if(updateRintersects(barrierRect()))paintBarrier(p)paintBarrier()做的和paintShot()、paintTarget()和paintCannon()是同样的事情。voidCannonField::paintBarrier(QPainter*p){p>setBrush(yellow)p>setPen(black)p>drawRect(barrierRect())}这个私有函数用一个黑色边界黄色填充的矩形作为障碍物。QRectCannonField::barrierRect()const{returnQRect(,height(),,)}这个私有函数返回障碍物的矩形。我们把障碍物的下边界和窗口部件的下边界放在了一起。boolCannonField::barrelHit(constQPointp)const{QWMatrixmtxmtxtranslate(,height())mtxrotate(ang)mtx=mtxinvert()returnbarrelRectcontains(mtxmap(p))}如果点在炮筒内这个函数返回TRUE否则它就返回FALSE。这里我们使用QWMatrix类。它是在头文件qwmatrixh中定义的这个头文件被qpainterh包含。QWMatrix定义了一个坐标系统映射。它可以执行和QPainter中一样的转换。这里我们实现同样的转换的步骤就和我们在paintCannon()函数中绘制炮筒的时候所作的一样。首先我们转换坐标系统然后我们旋转它。现在我们需要检查点p(在窗口部件坐标系统中)是否在炮筒内。为了做到这一点我们倒置这个转换矩阵。倒置的矩阵就执行了我们在绘制炮筒时使用的倒置的转换。我们通过使用倒置矩阵来映射点p并且如果它在初始的炮筒矩形内就返回TRUE。tgamebrdcpp#include<qaccelh>我们包含QAccel的类定义。QVBox*box=newQVBox(this,"cannonFrame")box>setFrameStyle(QFrame::WinPanel|QFrame::Sunken)cannonField=newCannonField(box,"cannonField")我们创建并设置一个QVBox设置它的框架风格并在之后创建CannonField作为这个盒子的子对象。因为没有其它的东西在这个盒子里了效果就是QVBox会在CannonField周围生成了一个框架。QAccel*accel=newQAccel(this)accel>connectItem(accel>insertItem(KeyEnter),this,SLOT(fire()))accel>connectItem(accel>insertItem(KeyReturn),this,SLOT(fire()))现在我们创建并设置一个加速键。加速键就是在应用程序中截取键盘事件并且如果特定的键被按下的时候调用相应的槽。这种机制也被称为快捷键。注意快捷键是窗口部件的子对象并且当窗口部件被销毁的时候销毁。QAccel不是窗口部件并且在它的父对象中没有任何可见的效果。我们定义两个快捷键。我们希望在Enter键被按下的时候调用fire()槽在CtrlQ键被按下的时候应用程序退出。因为Enter有时又被称为Return并且有时键盘中两个键都有所以我们让这两个键都调用fire()。accel>connectItem(accel>insertItem(CTRLKeyQ),qApp,SLOT(quit()))并且之后我们设置CtrlQ和AltQ做同样的事情。一些人通常使用CtrlQ更多一些(并且无论如何它显示了如果做到它)。CTRL、KeyEnter、KeyReturn和KeyQ都是Qt提供的常量。它们实际上就是Qt::KeyEnter等等但是实际上所有的类都继承了Qt这个命名空间类。QGridLayout*grid=newQGridLayout(this,,,)grid>addWidget(quit,,)grid>addWidget(box,,)grid>setColStretch(,)我们放置box(QVBox)不是CannonField在右下的单元格中。行为现在当你按下Enter的时候加农炮就会发射。你也可以用鼠标来确定加农炮的角度。障碍物会使你在玩游戏的时候获得更多一点的挑战。我们还会在CannnonField周围看到一个好看的框架。(请看编译来学习如何创建一个makefile和连编应用程序。)练习写一个空间入侵者的游戏。(这个练习首先被IgorRafienko作出来了。你可以下载他的游戏。)新的练习是:写一个突围游戏。最后的劝告:现在向前进创造编程艺术的杰作!上一章第一章教程一主

用户评价(0)

关闭

新课改视野下建构高中语文教学实验成果报告(32KB)

抱歉,积分不足下载失败,请稍后再试!

提示

试读已结束,如需要继续阅读或者下载,敬请购买!

评分:

/61

VIP

意见
反馈

免费
邮箱