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毫秒