Linux 版 (精华区)
发信人: pilot (不打Quake专心论文), 信区: Linux
标 题: Qt tutorial 10 (Translated by ePilot-=V=-)
发信站: 哈工大紫丁香 (2000年06月11日14:19:10 星期天), 转信
因为我已经把这个Qt作为我的本科毕业论文的英汉翻译部分,所以肯定会在近期内翻
译完毕.
Subject Qt tutorial -------"Smooth as Silk"
Posted by kensou
Translated by ePilot-=V=-
第十章: Smooth as Silk
在这个例程中我们使用位图(pixmap)来消除闪烁,而且加入了对炮台发射炮弹的力度控
制.
lcdrange.h LCDRange类定义
/****************************************************************
**
** Definition of LCDRange class, Qt tutorial 10
**
****************************************************************/
#ifndef LCDRANGE_H
#define LCDRANGE_H
#include <qwidget.h>
class QScrollBar;
class QLCDNumber;
class LCDRange : public QWidget
{
Q_OBJECT
public:
LCDRange( QWidget *parent=0, const char *name=0 );
int value() const;
public slots:
void setValue( int );
void setRange( int minVal, int maxVal );
signals:
void valueChanged( int );
protected:
void resizeEvent( QResizeEvent * );
private:
QScrollBar *sBar;
QLCDNumber *lcd;
};
#endif // LCDRANGE_H
lcdrange.cpp LCDRange类实现文件
/****************************************************************
**
** Implementation of LCDRange class, Qt tutorial 10
**
****************************************************************/
#include "lcdrange.h"
#include <qscrollbar.h>
#include <qlcdnumber.h>
LCDRange::LCDRange( QWidget *parent, const char *name )
: QWidget( parent, name )
{
lcd = new QLCDNumber( 2, this, "lcd" );
lcd->move( 0, 0 );
sBar = new QScrollBar( 0, 99, // range
1, 10, // line/page steps
0, // inital value
QScrollBar::Horizontal, // orientation
this, "scrollbar" );
connect( sBar, SIGNAL(valueChanged(int)), lcd, SLOT(display(int)) );
connect( sBar, SIGNAL(valueChanged(int)), SIGNAL(valueChanged(int)) );
}
int LCDRange::value() const
{
return sBar->value();
}
void LCDRange::setValue( int value )
{
sBar->setValue( value );
}
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 );
}
void LCDRange::resizeEvent( QResizeEvent * )
{
lcd->resize( width(), height() - 16 - 5 );
sBar->setGeometry( 0, lcd->height() + 5, width(), 16 );
}
cannon.h CannonField类定义
/****************************************************************
**
** Definition of CannonField class, Qt tutorial 10
**
****************************************************************/
#ifndef CANNON_H
#define CANNON_H
#include <qwidget.h>
class CannonField : public QWidget
{
Q_OBJECT
public:
CannonField( QWidget *parent=0, const char *name=0 );
int angle() const { return ang; }
int force() const { return f; }
public slots:
void setAngle( int degrees );
void setForce( int newton );
signals:
void angleChanged( int );
void forceChanged( int );
protected:
void paintEvent( QPaintEvent * );
private:
void paintCannon( QPainter * );
QRect cannonRect() const;
int ang;
int f;
};
#endif // CANNON_H
cannon.cpp CannonField类实现文件
/****************************************************************
**
** Implementation CannonField class, Qt tutorial 10
**
****************************************************************/
#include "cannon.h"
#include <qpainter.h>
#include <qpixmap.h>
CannonField::CannonField( QWidget *parent, const char *name )
: QWidget( parent, name )
{
ang = 45;
f = 0;
}
void CannonField::setAngle( int degrees )
{
if ( degrees < 5 )
degrees = 5;
if ( degrees > 70 )
degrees = 70;
if ( ang == degrees )
return;
ang = degrees;
repaint( cannonRect(), FALSE );
emit angleChanged( ang );
}
void CannonField::setForce( int newton )
{
if ( newton < 0 )
newton = 0;
if ( f == newton )
return;
f = newton;
emit forceChanged( f );
}
void CannonField::paintEvent( QPaintEvent *e )
{
QRect updateR = e->rect();
QPainter p;
p.begin( this );
if ( updateR.intersects( cannonRect() ) )
paintCannon( &p );
p.end();
}
const QRect barrel_rect(33, -4, 15, 8);
void CannonField::paintCannon( QPainter *p )
{
QRect cr = cannonRect();
QPixmap pix( cr.size() );
QPainter tmp;
pix.fill( this, cr.topLeft() );
tmp.begin( &pix );
tmp.setBrush( blue );
tmp.setPen( NoPen );
tmp.translate( 0, pix.height() - 1 );
tmp.drawPie( QRect( -35,-35, 70, 70 ), 0, 90*16 );
tmp.rotate( -ang );
tmp.drawRect( barrel_rect );
tmp.end();
p->drawPixmap( cr.topLeft(), pix );
}
QRect CannonField::cannonRect() const
{
QRect r( 0, 0, 50, 50 );
r.moveBottomLeft( rect().bottomLeft() );
return r;
}
main.cpp 包含MyWidget类及main函数
/****************************************************************
**
** Qt tutorial 10
**
****************************************************************/
#include <qapplication.h>
#include <qpushbutton.h>
#include <qscrollbar.h>
#include <qlcdnumber.h>
#include <qfont.h>
#include "lcdrange.h"
#include "cannon.h"
class MyWidget : public QWidget
{
public:
MyWidget( QWidget *parent=0, const char *name=0 );
protected:
void resizeEvent( QResizeEvent * );
private:
QPushButton *quit;
LCDRange *angle;
LCDRange *force;
CannonField *cannonField;
};
MyWidget::MyWidget( QWidget *parent, const char *name )
: QWidget( parent, name )
{
setMinimumSize( 500, 355 );
quit = new QPushButton( "Quit", this, "quit" );
quit->setGeometry( 10, 10, 75, 30 );
quit->setFont( QFont( "Times", 18, QFont::Bold ) );
connect( quit, SIGNAL(clicked()), qApp, SLOT(quit()) );
angle = new LCDRange( this, "angle" );
angle->setRange( 5, 70 );
angle->setGeometry( 10, quit->y() + quit->height() + 10, 75, 130 );
force = new LCDRange( this, "force" );
force->setRange( 10, 50 );
force->setGeometry( 10, angle->y() + angle->height() + 10, 75, 130 );
cannonField = new CannonField( this, "cannonField" );
cannonField->move( angle->x() + angle->width() + 10, angle->y() );
cannonField->setBackgroundColor( QColor( 250, 250, 200) );
connect( angle,SIGNAL(valueChanged(int)), cannonField,SLOT(setAngle(int)));
connect( cannonField,SIGNAL(angleChanged(int)), angle,SLOT(setValue(int)));
connect( force,SIGNAL(valueChanged(int)), cannonField,SLOT(setForce(int)));
connect( cannonField,SIGNAL(forceChanged(int)), force,SLOT(setValue(int)));
angle->setValue( 60 );
force->setValue( 25 );
}
void MyWidget::resizeEvent( QResizeEvent * )
{
cannonField->resize( width() - cannonField->x() - 10,
height() - cannonField->y() - 10 );
}
int main( int argc, char **argv )
{
QApplication::setColorSpec( QApplication::CustomColor );
QApplication a( argc, argv );
MyWidget w;
w.setGeometry( 100, 100, 500, 355 );
a.setMainWidget( &w );
w.show();
return a.exec();
}
Makefile Make文件,包含了支持signal/slot的内容
#############################################################################
# $Id: Makefile,v 2.6 1998/02/27 14:03:18 hanord Exp $
#
# Win32 Makefile, requires Microsoft nmake.
#
# Copyright (C) 1998 by Troll Tech AS. All rights reserved.
#
#############################################################################
PROJECT = t10
!INCLUDE $(QTDIR)\Makefile.inc
逐行解释
cannon.h
这次加入了力度控制。
int angle() const { return ang; }
int force() const { return f; }
public slots:
void setAngle( int degrees );
void setForce( int newton );
signals:
void angleChanged( int );
void forceChanged( int );
力度控制接口,和角度控制接口很相似。
private:
void paintCannon( QPainter * );
QRect cannonRect() const;
我们把画炮台的代码放到一个单独的函数里,定义炮管矩形区域的代码也一样(放到一个
单独的函数里).
int ang;
int f;
};
f是力度值。
cannon.cpp
#include <qpixmap.h>
包含了QPixmap的类定义.
CannonField::CannonField( QWidget *parent, const char *name )
: QWidget( parent, name )
{
ang = 45;
f = 0;
}
力度值f被初始化为0.
void CannonField::setAngle( int degrees )
{
if ( degrees < 5 )
degrees = 5;
if ( degrees > 70 )
degrees = 70;
if ( ang == degrees )
return;
ang = degrees;
repaint( cannonRect(), FALSE );
emit angleChanged( ang );
}
setAngle()作了一点小改动:只重画组件区域中的炮管部分,也就是真正需要重画的部分,
FALSE参数的意义是指在向组件发送重画消息之前不擦除原来的图像.
void CannonField::setForce( int newton )
{
if ( newton < 0 )
newton = 0;
if ( f == newton )
return;
f = newton;
emit forceChanged( f );
}
setForce()与setAngle()极其相似.唯一的区别是我们并不显示当前的力度值,也就不需
要重画组件.
void CannonField::paintEvent( QPaintEvent *e )
{
QRect updateR = e->rect();
QPainter p;
p.begin( this );
if ( updateR.intersects( cannonRect() ) )
paintCannon( &p );
p.end();
}
通过优化重画事件处理函数我们可以只重画真正需要刷新的部分.首先自QPaintEvent
取得需要重画的矩形区域参数,然后我们判断要求刷新的矩形区域是否与炮管区域相交.
如果相交,就重画炮管.注意我们把QPainter指针作为参数传给了paintCannon()函数.
(相交指至少有一个像素为两个矩形所共有)
const QRect barrel_rect(33, -4, 15, 8);
把炮管区域定义为常量.
void CannonField::paintCannon( QPainter *p )
{
QRect cr = cannonRect();
取得炮管矩形区域并用cr来保存.
QPixmap pix( cr.size() );
这里我们第一次碰到QPixmap:用于进行无闪烁绘图的"幕后"绘图类.我们创建了一个与
炮管尺寸一样的位图(pixmap)实例.通过它来画炮管并显示到屏幕上.这种手段我们一般
称之为“双缓冲”,可以有效的防止画面闪烁.
QPainter tmp;
创建一个临时的QPainter对象tmp,用来在pixmap中画图.
pix.fill( this, cr.topLeft() );
当一个pixmap创建时被随机的色彩所填充,所以我们要做的第一件事就是使用组件背景
色重新填充pixmap.同时必须指定pixmap在组件中的位置(cr.topLeft()).于是就能适于
任何类型(这里指颜色)的背景(参见background pixmaps).
tmp.begin( &pix );
tmp.setBrush( blue );
tmp.setPen( NoPen );
就像前一章一样用蓝色的brush和不可见的pen在pixmap上绘图,这次通过重载QPainter
函数setPen(),setBrush()来更快捷的设置brush,pen的属性.
tmp.translate( 0, pix.height() - 1 );
tmp.drawPie( QRect( -35,-35, 70, 70 ), 0, 90*16 );
tmp.rotate( -ang );
tmp.drawRect( barrel_rect );
tmp.end();
这些画炮管的代码与前一章的例子非常相似.不同之处在于这一次translate()是相对于
pixmap的高度(建议复习一下上一章的translate()是怎么调用的),以及这一次炮管的矩
形区域是一个常量.
p->drawPixmap( cr.topLeft(), pix );
最后,我们调用drawPixmap()把这个pixmap显示到屏幕上(in one lightning fast
operation).
QRect CannonField::cannonRect() const
{
QRect r( 0, 0, 50, 50 );
r.moveBottomLeft( rect().bottomLeft() );
return r;
}
这个函数返回组件坐标中的炮管矩形区域.首先创建一个50x50的矩形,然后设置这个矩
形的左下角与组件的左下角重合.rect()返回相对于本身坐标系统的矩形区域值(左上角
是0,0).
main.cpp
class MyWidget : public QWidget
{
public:
MyWidget( QWidget *parent=0, const char *name=0 );
protected:
void resizeEvent( QResizeEvent * );
private:
QPushButton *quit;
LCDRange *angle;
LCDRange *force;
CannonField *cannonField;
};
增加了一个LCDRange用于设置显示力度值.
force = new LCDRange( this, "force" );
force->setRange( 10, 50 );
force->setGeometry( 10, angle->y() + angle->height() + 10, 75, 130 );
把力度LCDRange组件放到角度LCDRange组件的下面,设置调整范围为10..50.
connect( force,SIGNAL(valueChanged(int)), cannonField,SLOT(setForce(int)));
connect( cannonField,SIGNAL(forceChanged(int)), force,SLOT(setValue(int)));
就像角度组件一样,我们把force和cannonField链接起来.
force->setValue( 25 );
程序启动时力度值初始化为25.
程序行为
闪烁消失了.
加入了力度控制.
练习
使炮管尺寸随发射力度而变化.
把炮台改到窗口右下角.
你现在可以进入第十一章了.
--
〓〓★〓〓
比别人飞的更高,更快,更强!
※ 来源:·哈工大紫丁香 bbs.hit.edu.cn·[FROM: mtnews.hit.edu.cn]
※ 修改:·pilot 於 06月11日14:25:24 修改本文·[FROM: mtnews.hit.edu.cn]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:205.960毫秒