第七章- 一个事物领导另一个

在这里插入图片描述

这个例子显示了如何使用信号和槽来创建自定义窗口部件,和如何使用更加复杂的方式把它们连接起来。

  • lcdrange.h包含LCDRange类定义。
  • lcdrange.cpp包含LCDRange类实现。
  • main.cpp包含MyWidget和main。

一行一行地解说

lcdrange.h:
这个文件主要利用了第六章的main.cpp,在这里只是说明一下改变了哪些。

#ifndef LCDRANGE_H
#define LCDRANGE_H

这里是一个经典的C语句,为了避免出现一个头文件被包含不止一次的情况。如果你没有使用过它,这是开发中的一个很好的习惯。#ifndef需要把这个头文件的全部都包含进去。

#include <QWidget>

QWidget被包含了。LCDRange继承了QWidget,所以父类的头文件必须被包含。我们在前几章里面偷了一点懒,我们通过包含其它一些头文件,比如Qpushbutton,这样就可以间接地包含QWidget

class QSlider;

这里是另外一个小伎俩,但是没有前一个用的多。因为我们在类的界面中不需要QSlider,仅仅是在实现中,我们在头文件中使用一个前置的类声明,并且在.cpp文件中包含一个QSlider的头文件。 (点这里详细说明)

这会使编译一个大的项目变得更快,因为当一个头文件改变的时候,很少的文件需要重新编译。它通常可以给大型编译加速两倍或两倍以上。

class LCDRange : public QWidget
{
    Q_OBJECT
public:
    explicit LCDRange(QWidget *parent = 0, Qt::WindowFlags name = Qt::WindowFlags());

meta object file. 注意Q_OBJECT。这个宏必须被包含到所有使用信号和/或槽的类。如果你很好奇,它定义了在元对象文件中实现的一些函数。

    int value() const;
public slots:
    void setValue(int);
signals:
    void valueChanged(int);

这三个成员函数构成了这个窗口部件和程序中其它组件的接口。直到现在,LCDRange根本没有一个真正的接口。

value()是一个可以访问LCDRange的值的公共函数。setValue()是我们第一个自定义槽,并且valueChanged()是我们第一个自定义信号。

槽必须按通常的方式实现(记住槽也是一个C++成员函数)。信号可以在元对象文件中自动实现。信号也遵守C++函数的保护法则(比如,一个类只能发射它自己定义的或者继承来的信号)。

LCDRange的值发生变化时,valueChanged()信号就会被使用——你从这个名字中就可以猜到。这将不会是你将会看到的命名为somethingChanged()的最后一个信号。


lcdrange.cpp:
这个文件主要利用了t6 main.cpp,在这里只是说明一下改变了哪些。

    connect(slider, SIGNAL(valueChanged(int)), lcd, SLOT(display(int)));
    connect(slider, SIGNAL(valueChanged(int)), SIGNAL(valueChanged(int)));

这个代码来自LCDRange的构造函数。

第一个connect和你在上一章中看到的一样。第二个是新的,它把滑块的valueChanged信号和这个对象LCDRangevalueChanged信号连接起来了。带有三个参数的connect()函数连接到this对象的信号或槽。

是的,这是正确的。信号可以被连接到其它的信号。当第一个信号被发射时,第二个信号也被发射。

让我们来看看当用户操作这个滑块的时候都发生了些什么。滑块看到自己的值发生了改变,并发射了valueChanged()信号。这个信号被连接到QLCDNumberdisplay()槽和LCDRangevalueChanged()信号。

所以,当这个信号被发射的时候,LCDRange发射它自己的valueChanged()信号。另外,QLCDNumber::display()被调用并显示新的数字。

注意你并没有保证执行的任何顺序——LCDRange::valueChanged()也许在QLCDNumber::display()之前或者之后发射,这是完全任意的。

int LCDRange::value() const
{
    return slider->value();
}

value()的实现是直接了当的,它简单地返回滑块的值。

void LCDRange::setValue( int value )
{
    slider->setValue( value );
}

setValue()的实现也是相当直接了当的。注意因为滑块和LCD数字是连接的,设置滑块的值就会自动的改变LCD数字的值。另外,如果滑块的值超过了合法范围,它会自动调节。


main.cpp :

    LCDRange *previous = 0;

    for(int r = 0 ; r < 4 ; r++)
    {
        for(int c = 0 ; c < 5 ; c++)
        {
            LCDRange* lcdRange = new LCDRange;
            grid->addWidget(lcdRange, r, c);
            if (previous)
            {
                connect(lcdRange, SIGNAL(valueChanged(int)), previous, SLOT(setValue(int)));
            }
            previous = lcdRange;
        }
    }

main.cpp中所有的部分都是上一章复制的,除了MyWidget的构造函数。当我们创建20个RCDRange对象时,我们现在使用信号/槽机制连接它们。每一个的valueChanged()信号都和前一个的setValue()槽连接起来了。因为当LCDRange的值发生改变的时候,发射一个valueChanged()信号(惊奇!),我们在这里创建了一个信号和槽的“链”。


**lcdrange.h **

#ifndef LCDRANGE_H
#define LCDRANGE_H
#include <QWidget>

class QSlider;

class LCDRange : public QWidget
{
    Q_OBJECT
public:
    explicit LCDRange(QWidget *parent = 0, Qt::WindowFlags name = Qt::WindowFlags());
    int value() const;

public slots:
    void setValue(int);

signals:
    void valueChanged(int);

private:
    QSlider *slider;
};

#endif // LCDRANGE_H

lcdrange.cpp

#include "lcdrange.h"
#include <QLCDNumber>
#include <QSlider>

LCDRange::LCDRange(QWidget *parent, Qt::WindowFlags name)
    : QWidget(parent, name)
{
    QLCDNumber *lcd  = new QLCDNumber(2);
    slider = new QSlider(Qt::Horizontal);
    slider->setRange(0, 99);
    slider->setValue(0);

    connect(slider, SIGNAL(valueChanged(int)), lcd, SLOT(display(int)));
    connect(slider, SIGNAL(valueChanged(int)), SIGNAL(valueChanged(int)));

    QVBoxLayout* layout = new QVBoxLayout;
    layout->addWidget(lcd);
    layout->addWidget(slider);
    setLayout(layout);
}

int LCDRange::value() const
{
    return slider->value();
}

void LCDRange::setValue(int value)
{
    slider->setValue(value);
}

**main.cpp **

#include <QApplication>
#include <Qpushbutton>
#include <QVBoxLayout>
#include <QGridLayout>
#include <QLCDNumber>
#include <QSlider>
#include <Qfont>

#include "lcdrange.h"

class MyWidget : public QWidget
{
public:
    MyWidget(QWidget *parent=0, Qt::WindowFlags name = Qt::WindowFlags());
};

MyWidget::MyWidget(QWidget *parent, Qt::WindowFlags name)
    : QWidget(parent, name)
{
    QPushButton *quit = new QPushButton("Quit");
    quit->setFont(QFont("Times", 18, QFont::Bold));
    connect(quit, SIGNAL(clicked()), qApp, SLOT(quit()));

    QGridLayout *grid = new QGridLayout;
    LCDRange *previous = 0;

    for(int r = 0 ; r < 4 ; r++)
    {
        for(int c = 0 ; c < 5 ; c++)
        {
            LCDRange* lcdRange = new LCDRange;
            grid->addWidget(lcdRange, r, c);
            if (previous)
            {
                connect(lcdRange, SIGNAL(valueChanged(int)), previous, SLOT(setValue(int)));
            }
            previous = lcdRange;
        }
    }

    QVBoxLayout* layout = new QVBoxLayout;
    layout->addWidget(quit);
    layout->addLayout(grid);
    setLayout(layout);
}

int main(int argc, char **argv)
{
    QApplication a(argc, argv);

    MyWidget w;
    w.show();
    return a.exec();
}
  • 15
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值