Linux 版 (精华区)

发信人: pilot (〓〓★〓〓), 信区: Linux
标  题: Qt Tutorial 7
发信站: 紫 丁 香 (Thu May 11 16:01:08 2000), 转信

(Qt Tutorial 6 已经有少许改动,see 6284)

Subject Qt tutorial 7-------"One Thing Leads to Another"
Posted by kensou
Translated by Pilot-=?=-

第七章: One Thing Leads to Another
这个例程演示了如何使用signal/slot机制来创建自定义的组件,以及如何使用更复杂的手
段把它们连接在一起.现在源代码被分成了几个文件.
lcdrange.h  LCDRange类定义
/****************************************************************
**
** Definition of LCDRange class, Qt tutorial 7
**
****************************************************************/
#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 );

    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 7
**
****************************************************************/
#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::resizeEvent( QResizeEvent * )
{
    lcd->resize( width(), height() - 16 - 5 );
    sBar->setGeometry( 0, lcd->height() + 5, width(), 16 );
}
main.cpp    MyWidget类以及main函数.
/****************************************************************
**
** Qt tutorial 7
**
****************************************************************/
#include <qapplication.h>
#include <qpushbutton.h>
#include <qscrollbar.h>
#include <qlcdnumber.h>
#include <qfont.h>
#include "lcdrange.h"
class MyWidget : public QWidget
{
    public:
        MyWidget( QWidget *parent=0, const char *name=0 );

    protected:
        void resizeEvent( QResizeEvent * );

    private:
        QPushButton *quit;
        LCDRange *value[16];
};
MyWidget::MyWidget( QWidget *parent, const char *name )
: QWidget( parent, name )
{
    setMinimumSize( 200, 300 );
    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()) );
    for( int i = 0 ; i < 16 ; i++ ) {
        value[i] = new LCDRange( this );
        if ( i > 0 )
            connect( value[i], SIGNAL(valueChanged(int)),
                     value[i - 1], SLOT(setValue(int)) );
    }
}
void MyWidget::resizeEvent( QResizeEvent * )
{
    int valueWidth = (width() - 20)/4;
    int valueHeight = (height() - 65)/4;
    for( int i = 0 ; i < 16 ; i++ )
        value[i]->setGeometry( 10 + (i%4)*valueWidth,  55 + (i/4)*valueHeight,
                               valueWidth - 5, valueHeight - 5 );
}
int main( int argc, char **argv )
{
    QApplication a( argc, argv );
    MyWidget w;
    w.setGeometry( 100, 100, 400, 400 );
    a.setMainWidget( &w );
    w.show();
    return a.exec();
}
Makefile    Make文件,包含了支持signal/slot的内容
#############################################################################
# $Id: Makefile,v 2.6 1998/02/27 14:03:19 hanord Exp $
#
# Win32 Makefile, requires Microsoft nmake.
#
# Copyright (C) 1998 by Troll Tech AS.  All rights reserved.
#
#############################################################################
PROJECT = t7
!INCLUDE $(QTDIR)\Makefile.inc
逐行解释
lcdrange.h:
这个文件基本上是从第六章的mian.cpp发展来的,改动如下:
#ifndef LCDRANGE_H
#define LCDRANGE_H
这是一个正统的C条件编译语句,其目的是避免同一个头文件被包含多次.把#ifndef写进每
个头文件是个很好的习惯.
#include <qwidget.h>
class QScrollBar;
class QLCDNumber;
包含了qwidget.h,LCDRange继承自QWidget,父类的声明文件是必需的.实际上qwidget.h已
经被qpushbutton.h隐含的包含了.
因为类声明中只有QScrollBar,QLCDNumber的指针而不需要其定义,所以只需简单的声明它
们的类名,这样可以减轻编译器的工作.
class LCDRange : public QWidget
{
    Q_OBJECT

    public:
    LCDRange( QWidget *parent=0, const char *name=0 );
注意Q_OBJECT,使用signal/slot的类必须包含Q_OBJECT宏.Q_OBJECT宏定义了在二进制文
件中实现了的(signal/slot)函数.
int value() const;
public slots:
    void setValue( int );

signals:
    void valueChanged( int );
这三个成员构成了程序中widget与其他组件之间的接口.在这之前LCDRange还没有定义接
口.value()是一个public函数用以读取LCDRange的值.setValue()是第一个我们自定义的
slot,而valueChanged()是第一个自定义的signal.
Slot必须以正常方式定义(一个slot也就是一个C++成员函数).Signal是在二进制目标文件
(meta object file)中实现的,遵循C++ Protected函数规则,也就是说,只能被定义它们的
类或者继承的子类调用.
lcdrange.cpp:
这个文件基本上是从第六章的mian.cpp发展来的,改动如下:
connect( sBar, SIGNAL(valueChanged(int)), lcd, SLOT(display(int)) );
connect( sBar, SIGNAL(valueChanged(int)), SIGNAL(valueChanged(int)) );
这些代码摘自LCDRange的构造函数.
第一个connect函数在上一章已经见过了,现在注意第二个:它把sBar的valueChanged()信
号与自身的valueChanged链接起来.三个参数的connect()可以把一个对象中的signal,
slot链接起来.
这里并没有敲错,signal确实可以与另一个signal链接起来,当第一个signal发出时,第
二个signal跟着发出.
现在让我们看看当用户使用滚动条是会发生什么:当滚动条移动时发出valueChanged()
信号,而valueChanged()信号同时链接到lcd的diaplay()事件(slot)和LCDRange的
valueChanged()信号.
于是,当sBar的valueChanged()信号发出时,LCDRange的valueChanged()信号同时发出,
于是,QLCDNumber::display()被调用并显示新的数字.
注意,你无法做到精确保证signal的发出顺序.
int LCDRange::value() const
{
        return sBar->value();
}
这个函数简单的返回滚动条的当前值.
void LCDRange::setValue( int value )
{
        sBar->setValue( value );
}
这个函数使用setValue()设置滚动条的当前值,因为滚动条的数值与LCD的显示数字链
接所以LCD的数字会自动刷新.如果设置滚动条数值的时候超出了其有效值范围会自动
进行调整.
main.cpp:
for( int i = 0 ; i < 16 ; i++ )
{
    value[i] = new LCDRange( this );
        if ( i > 0 )
    connect( value[i], SIGNAL(valueChanged(int)),
             value[i - 1], SLOT(setValue(int)) );
}
虽然LCDRange被分成了两个文件,但这里才是main.cpp中真正有变化的地方.上一章中我
们创建了16个LCDRange对象,现在,我们使用signal/slot机制把它们链接起来.把每一个
LCDRange的valueChanged()信号与前一个LCDRange的setValue()事件链接起来,于是每当
LCDRange发出valueChanged()信号时又触发了下一个valueChanged()信号(你会吃惊吗?
至少我不会),这样我们得到了一个"signal/slot链".
程序行为
刚启动时这个程序与上一章的程序表现没有两样,但如果你试着动一动右下角的滚动条
的话...
练习
使用右下角的的滚动条把所有的LCD设置成30.然后用第二排最右边的滚动条把上一半LCD
设置成29.再用第二排从右边数第二个滚动条把前七个LCD设置成30.
现在点击一下右下角滚动条的左箭头.看看会发生什么?为什么??
你现在可以进入第八章了.
--


               〓〓★〓〓

        比别人飞的更高,更快,更强!

※ 修改:.pilot 于 May 13 20:47:18 修改本文.[FROM: dns.mtlab.hit.ed]
※ 来源:.紫 丁 香 bbs.hit.edu.cn.[FROM: dns.mtlab.hit.ed]
[百宝箱] [返回首页] [上级目录] [根目录] [返回顶部] [刷新] [返回]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:3.726毫秒