Linux 版 (精华区)

发信人: tcpip (高级草包), 信区: Linux
标  题: Qt tutorial 8-------Preparing for Battle
发信站: 哈工大紫丁香 (2000年06月11日12:49:37 星期天), 转信

:第八章 Preparing for Battle




在本例子中,我们将介绍如何定制可以自身重绘的控件. 

lcdrange.h包含了LCDRange类的定义. 
lcdrange.cpp包含了LCDRange类的实现. 
cannon.h 包含了CannonField类的定义. 
cannon.cpp 包含CannonField类的实现. 
main.cpp包含MyWidget 和 main. 
Makefile contains some rules for generating the meta object information necessary for signal/slot creation. 

代码逐行解释

lcdrange.h

这个文件仍然很简单,我们仅加了一个slot:setRange()l 

void setRange( int minVal, int maxVal );

我们现在给LCDRange加上了设置范围的能力.以前,它被固定在0和99之间. 

lcdrange.cpp

void LCDRange::setRange( int minVal, int maxVal )
{
if ( minVal < 0 || maxVal > 99 || minVal > maxVal ) {
warning( "LCDRange::setRange(%d,%d)\n"
"\tRange must be 0..99\n"
"\tand minVal must not be greater than maxVal",
minVal, maxVal );
return;
}
sBar->setRange( minVal, maxVal );
}

setRange()设置LCDRange控件中的滚动条的范围.因为我们在创建的是只显示
两位数字的液晶显示器,所以,我们希望限制 minVal和 maxVal
在0到99之间,这样会避免液晶显示器的溢出.我们可以允许它们的值在-9..99,但
是我们不会这样做.如果参数是非法的,我们用Qt的warning()函数来发给用户警告并立即返回.
warning()和printf函数类似,缺省情况下,它把输出发送到stderr.如果你需要
使用自己的handlerfunction,可以自己安装. 

cannon.h

CannonField 是我们定制的新控件,并且,它可以自行控制自己的显示. 

class CannonField : public QWidget
{
Q_OBJECT
public:
CannonField( QWidget *parent=0, const char *name=0 );

CannonField 继承于 QWidget,这和我们定义LCDRange时是一样的. 

int angle() const { return ang; }
public slots:
void setAngle( int degrees );
signals:
void angleChanged( int );

在目前情况下,CannonField 仅包含一个角度值作为其预留的接口,这和在LCDRange中 定义value是一样的. 

protected:
void paintEvent( QPaintEvent * );

这是我们遇到的QWidget的第二个事件处理函数.当一个控件需要更新(例如重绘界面)时, 这个虚函数(virtual
function)就会被Qt调用. 

cannon.cpp

CannonField::CannonField( QWidget *parent, const char *name )
: QWidget( parent, name )
{

我们再次使用上一章定义LCDRange时的语法. 

ang = 45;
}

构造函数把角度值预设为45度. 

void CannonField::setAngle( int degrees )
{
if ( degrees < 5 )
degrees = 5;
if ( degrees > 70 )
degrees = 70;
if ( ang == degrees )
return;
ang = degrees;
repaint();
emit angleChanged( ang );
}

We have chosen not to issue a warning if the new angle is out of range.
本函数设置角度值。我们已经选定了一个范围:5....70并且调整角度
值.这次,当新的角度值超出范围时,我们不再发出警告. 

如果新的角度值等于旧的值,我们让函数立即返回.
这一点十分重要,因为当角度值真正地改变时,我们才让angleChanged() signal发射. 

然后,我们设置新的角度值并且重绘控件.repaint() 函数会清除控件(例如使用背景颜色填充)并且向控件发送 paint
事件.这样就会立即调用控件的 paint event函数.
如果你想要Qt稍后来发送这个事件(当它收回控制权时),就使用update() 函数吧. 

最后,我们发射 angleChanged() signal 来告诉外界角度发生了改变.在QT中,emit
关键字是唯一的非C++语法.事实上,它是一个宏指令. 

void CannonField::paintEvent( QPaintEvent * )
{
QString s;
s.sprintf( "Angle = %i", ang );
drawText( 200, 100, s );
}

这是我们尝试编写的第一个paint event 处理程序. 事件参数包含了paint event
的描述(description).QPaintEvent包含了控件需要 更新的矩形区域.暂时,我们懒于使用它,简单的更新整个控件. 

我们的代码把角度值显示在控件里的固定的位置. 首先,我们创建了一个QString 对象.QString是Qt
的字串类(详情见 文献 ).然后,我们使用 QString::sprintf() 函数 (和sprintf()相似)来设置规范化的字串.
最后,我们使用QWidget::drawText() 函数把字串显示在控件的200,100位置(以字串的 基线为准).
通常,我们使用QPainter在控件里绘制对象,但是,drawText() 用来绘制字串十分便利.
在下一章,你将看到QPainter是如何工作的. 

main.cpp

#include "cannon.h"

我们包括了自己的新类: cannon 

class MyWidget : public QWidget
{
public:
MyWidget( QWidget *parent=0, const char *name=0 );
protected:
void resizeEvent( QResizeEvent * );
private:
QPushButton *quit;
LCDRange *angle;
CannonField *cannonField;
};

这一次,我们在自己的顶级控件中包括了一个LCDRange和CannonField. 

angle = new LCDRange( this, "angle" );
angle->setRange( 5, 70 );
angle->setGeometry( 10, quit->y() + quit->height() + 10, 75, 130 );

在构造函数中,我们创建了LCDRange,其范围为5...70,固定尺寸为75X130, 并且把它放在 quit
按钮之下10象素的地方. 

cannonField = new CannonField( this, "cannonField" );
cannonField->move( angle->x() + angle->width() + 10, angle->y() );
cannonField->setBackgroundColor( QColor( 250, 250, 200) );

创建CannoField.横向上距LCDRange10象素,纵向上,和LCDRange相等.其 尺寸我们将在resize 事件中设置. 

然后,我们设置CannonField的背景颜色.QColor是Qt的颜色类.在这里,我们
使用设置RGB值的方式:red=250,green=250,blue=200(近似于黄色).RGB方式
的值应该在0..255之间.你也可以使用HSV模式 来设置. 

Qt的另一个颜色类是QPalette,它已经用数组形式提供所有的颜色,这样,
在你要改变颜色时,就不会搞乱控件的3D效果. 

connect( angle,SIGNAL(valueChanged(int)), cannonField,SLOT(setAngle(int)));
connect( cannonField,SIGNAL(angleChanged(int)), angle,SLOT(setValue(int)));

这里,我们把LCDRange的 valueChanged() signal连接到CannonField的 setAngle()
slot上.这样,当用户操作LCDRange时,CannonField的角度值就会更新. 同样,我们也连接了CannonFiled的
angleChanged() signal 和 LCDRange的 setValue() slot ,因此,在CannonField中改变角度值也会更新
LCDRange的值.在我们的例子中,
我们从来不会在CannonField中直接改变角度值,使用第二connect()的目的是确保
将来的变动不会破坏这两个值之间的一致性. 

这个例子说明了组件编程和严格封装的威力. 

注意当角度真正地改变时才发射 angleChanged() signal是十分重要的. 如果 LCDRange 和 CannonField
都有次种设计缺陷,程序在value第一次 改变时,就会陷入死循环. 

angle->setValue( 60 );

最后,我们设置角度值的初始值.注意,这同样会触发LCDRange 到 CannonField 的连接. 

void MyWidget::resizeEvent( QResizeEvent * )
{
cannonField->resize( width() - cannonField->x() - 10,
height() - cannonField->y() - 10 );
}

除去底部和右边的10象素的边框之外,我们给予CannonField全部的空间. 

程序行为

当操作滚动条时,CannonField将显示新的角度值.最大化后,CannonField 会获得最大的空间. 

On Windows machines with an 8-bit display, the new background color is dithered to death. The next chapter works around
this. 

练习

使本文显示的位置依赖于角度值. 

改写 resize 事件给予 LCDRange最大尺寸而非CannonField. 

你现在可以进入第九章了. 

[上一章] [下一章] [教程目录] 


Copyright ?1999 Troll Tech
Trademarks 
Qt version 1.44




--
"这一千多年没写诗了?"
"写了, 不过只写了两句."
"千年得两句, 一定是万古丽句了. 念来听听."
"好吧, 我现丑了" 太白星清了清嗓子, 浑厚的男中音在天庭响起:
大海啊, 都是水;
骏马啊, 四条腿;

※ 来源:·哈工大紫丁香 bbs.hit.edu.cn·[FROM: tcpip.hit.edu.cn]
[百宝箱] [返回首页] [上级目录] [根目录] [返回顶部] [刷新] [返回]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:2.468毫秒