Linux 版 (精华区)
发信人: pilot (〓〓★〓〓), 信区: Linux
标 题: Qt Tutorial 8
发信站: 紫 丁 香 (Sat May 13 20:35:19 2000), 转信
Subject Qt tutorial -------"Preparing for Battle"
Posted by kensou
Translated by Pilot-=?=-
第八章: Preparing for Battle
这个例程演示了如何创建我们的第一个具有自画能力(paint itself)的组件。
lcdrange.h LCDRange类定义
/****************************************************************
**
** Definition of LCDRange class, Qt tutorial 8
**
****************************************************************/
#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 8
**
****************************************************************/
#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, // direction
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 8
**
****************************************************************/
#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; }
public slots:
void setAngle( int degrees );
signals:
void angleChanged( int );
protected:
void paintEvent( QPaintEvent * );
private:
int ang;
};
#endif // CANNON_H
cannon.cpp CannonField类实现文件
/****************************************************************
**
** Implementation CannonField class, Qt tutorial 8
**
****************************************************************/
#include "cannon.h"
CannonField::CannonField( QWidget *parent, const char *name )
: QWidget( parent, name )
{
ang = 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 );
}
void CannonField::paintEvent( QPaintEvent * )
{
QString s;
s.sprintf( "Angle = %i", ang );
drawText( 200, 100, s );
}
main.cpp 包含MyWidget类及main函数.
/****************************************************************
**
** Qt tutorial 8
**
****************************************************************/
#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;
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 );
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)));
angle->setValue( 60 );
}
void MyWidget::resizeEvent( QResizeEvent * )
{
cannonField->resize( width() - cannonField->x() - 10,
height() - cannonField->y() - 10 );
}
int main( int argc, char **argv )
{
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:20 hanord Exp $
#
# Win32 Makefile, requires Microsoft nmake.
#
# Copyright (C) 1998 by Troll Tech AS. All rights reserved.
#
############################################################################
#
PROJECT = t8
!INCLUDE $(QTDIR)\Makefile.inc
逐行解释
lcdrange.h
基本与上一章的lcdrange.h一样,在这里加了一个slot, setRange().
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的滚动条的数值范围,因为我们设置QLCDNumber只能显示两位
数字,所以希望能够把其范围限制在0..99以避免显示溢出.我们可以试图使用setRange()
把范围设置成-9..99,这时warning()就会显示错误信息并立即返回。warning()是一个
类似于printf()的函数,可以把信息输出到标准错误输出(stderr)。你也可以使用自己
函数来输出错误信息。
cannon.h
CannonField是一个知道如何显示自身的自定义组件(widget)。
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非常类似,只有一个角度值作为接口。
protected:
void paintEvent( QPaintEvent * );
现在我们接触到了众多的QWidget事件句柄中的第二个(第一个是QResizeEvent),当组
件需要刷新自身时调用这个虚拟函数。
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 );
}
设置角度值,因为我们选择角度有效范围为5..70度,所以如果指定的角度值越界的话
就把它调整到有效范围内,但不显示错误信息.
如果指定的角度值与原先的相同就立即返回,这样只有当前角度值被改变的情况下才发
出angleChanged()信号。
然后我们设置角度值并重画组件。repaint()函数先擦去组件(用背景色填充组件区域),
然后发出重画事件(paint event),立即,paint函数被调用。如果你想要Qt稍后再发出
重画事件(当组件获得当前输入焦点时),可以调用update()函数。
最后,我们发出angleChanged()消息(signal)告诉大家角度值已经被改变。emit是Qt
自己而非C++的关键字。实际上emit是一个宏。
void CannonField::paintEvent( QPaintEvent * )
{
QString s;
s.sprintf( "Angle = %i", ang );
drawText( 200, 100, s );
}
这是我们自己写的第一个重画句柄函数(paint event handler)。QPaintEvent包含了
需要重画的组件的矩形区域。现在我们只是偷懒的直接重画整个组件。
我们想把角度值显示在组件上的一个固定位置。首先创建一个QString对象,QString是
Qt所封装的字符串类(参见Qt文档)。接着使用QString::sprintf()(与sprintf()函
数很相似)给字符串赋值。最后使用QWidget::drawText()函数把字符串显示在(200,100)
坐标处(相对于字符串的基线(baseline))。一般我们使用QPaint来画组件,不过用
drawText()输出字符串更方便,在下一章我们将会使用QPaint.
main.cpp
#include "cannon.h"
We include our new class.
class MyWidget : public QWidget
{
public:
MyWidget( QWidget *parent=0, const char *name=0 );
protected:
void resizeEvent( QResizeEvent * );
private:
QPushButton *quit;
LCDRange *angle;
CannonField *cannonField;
};
现在最基本的组件(MyWidget)中包括了一个CannonField,LCDRange也变成了一个。
angle = new LCDRange( this, "angle" );
angle->setRange( 5, 70 );
angle->setGeometry( 10, quit->y() + quit->height() + 10, 75, 130 );
创建一个LCDRange,范围5..70,在quit按钮以下10个像素处,尺寸75x130。
cannonField = new CannonField( this, "cannonField" );
cannonField->move( angle->x() + angle->width() + 10, angle->y() );
cannonField->setBackgroundColor( QColor( 250, 250, 200) );
创建一个CannonField,在LCDRange右边10个像素处,y坐标值与LCDRange相同,尺寸在
resize事件中设置。
接下来设置CannonField背景色。QColor是Qt所封装的色彩类之一。在这里我们指定背景
色RGB值为红250,绿250,蓝200(近似于蜡黄色)。 RGB的取值范围是0..255。也可以使
用HSV色彩模式赋值。
另一个色彩类是QPalette,提供了全部的色彩队列(array),这样你就可以在制作3D效
果时使用平滑的过渡色。
connect( angle,SIGNAL(valueChanged(int)), cannonField,SLOT(setAngle(int)));
connect( cannonField,SIGNAL(angleChanged(int)), angle,SLOT(setValue(int)));
我们把LCDRange的valueChanged()信号与CannonField的setAngle()事件链接起来,这
样当滚动条数值改变时CannonField的角度值也会改变。同时反向链接,于是当CannonF
ield
的角度值改变时LCDRange的数值也会随之改变。虽然在例程中我们并没有试图直接改变
CannonField的角度值,但这样可以保证LCDRange与CannonField的同步。
这里体现了组件编程(component programming)与严格封装的优点。
注意确保只有当前角度值被改变时才发出angleChanged()信号,否则将进入死循环。
angle->setValue( 60 );
最后我们设置了一个初始角度值,这时angleChanged()与LCDRange,CannonField的链接
被第一次触发。
void MyWidget::resizeEvent( QResizeEvent * )
{
cannonField->resize( width() - cannonField->x() - 10,
height() - cannonField->y() - 10 );
}
除了右边以及下边10个像素的边框以外我们把所有的区域都给了CannonField。
程序行为
操作滚动条时CannonField会显示当前角度值。resize时CannonField得到了尽可能大的
区域。
在256色的窗口系统中,新的背景颜色会令人无法接受,下一章解决这个问题。
练习
试一下让角度值的显示位置随角度值而变化。
改动resize事件让LCDRange代替CannonField得到尽可能大的区域。
你现在可以进入第九章了.
--
〓〓★〓〓
比别人飞的更高,更快,更强!
※ 修改:.pilot 于 May 13 20:47:00 修改本文.[FROM: dns.mtlab.hit.ed]
※ 来源:.紫 丁 香 bbs.hit.edu.cn.[FROM: dns.mtlab.hit.ed]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:206.935毫秒