connect(ui.verticalSlider_1, SIGNAL(valueChanged(int)), ui.spinBox_1, SLOT(setValue(int …"> connect(ui.verticalSlider_1, SIGNAL(valueChanged(int)), ui.spinBox_1, SLOT(setValue(int …" />

8、带参数的信号槽和自定义信号槽

Par @Martin dans le
Tags :

带参数的信号/槽

首先看一个程序

这个程序的功能是: 拖动滑块动态改变微调框内的数值.

<font face="M+ 1mn regular">connect(ui.verticalSlider_1, SIGNAL(valueChanged(<span style="color: #0000ff">int</span>)), ui.spinBox_1, SLOT(setValue(<span style="color: #0000ff">int</span>)));</font>

实现功能的就是这句代码, 可以看到带参数的信号/槽要明确的在 connect 函数中写出参数的类型.


自定义信号槽

Qt 的信号槽机制并不仅仅是使用系统提供的那部分, 还会允许我们自己设计自己的信号和槽.

看下面这段代码, 它实现了一个报纸类 Newspaper, 一个订阅者类 Reader, 当 Newspaper有了新的内容的时候, Reader 可以立即得到通知.

<font face="M+ 1mn regular"><span style="color: #008000">//</span><span style="color: #008000">!!! Qt5</span>
#include <QObject>

<span style="color: #808080">/////////</span><span style="color: #008000">/ newspaper.h</span>
<span style="color: #0000ff">class</span></font><font face="M+ 1mn regular"><span style="color: #000000"> Newspaper
    : </span><span style="color: #0000ff">public</span></font><font face="M+ 1mn regular"><span style="color: #000000"> QObject {
    Q_OBJECT
</span><span style="color: #0000ff">public</span></font><font face="M+ 1mn regular"><span style="color: #000000">:
    Newspaper(</span><span style="color: #0000ff">const</span> QString &</font><font face="M+ 1mn regular"><span style="color: #000000"> name) :
        m_name(name) {
    }

    </span><span style="color: #0000ff">void</span></font><font face="M+ 1mn regular"><span style="color: #000000"> send() {
        emit newPaper(m_name);
    }

signals:
    </span><span style="color: #0000ff">void</span> newPaper(<span style="color: #0000ff">const</span> QString &</font><font face="M+ 1mn regular"><span style="color: #000000">name);

</span><span style="color: #0000ff">private</span></font><font face="M+ 1mn regular"><span style="color: #000000">:
    QString m_name;
};

</span><span style="color: #808080">/////////</span><span style="color: #008000">/ reader.h</span>
#include <QObject></font><span style="color: #000000">
<font face="M+ 1mn regular">#include </font></span><font face="M+ 1mn regular"><QDebug>

<span style="color: #0000ff">class</span></font><font face="M+ 1mn regular"><span style="color: #000000"> Reader
    : </span><span style="color: #0000ff">public</span></font><font face="M+ 1mn regular"><span style="color: #000000"> QObject {
    Q_OBJECT
</span><span style="color: #0000ff">public</span></font><font face="M+ 1mn regular"><span style="color: #000000">:
    Reader() {}

    </span><span style="color: #0000ff">void</span> receiveNewspaper(<span style="color: #0000ff">const</span> QString &</font><font face="M+ 1mn regular"><span style="color: #000000"> name) {
        qDebug() </span><< <span style="color: #800000">"</span><span style="color: #800000">Receives Newspaper: </span><span style="color: #800000">"</span> <<</font><font face="M+ 1mn regular"><span style="color: #000000"> name;
    }
};

</span><span style="color: #808080">/////////</span><span style="color: #008000">/ main.cpp</span>
#include <QCoreApplication></font><span style="color: #000000">

<font face="M+ 1mn regular">#include </font></span><font face="M+ 1mn regular"><span style="color: #800000">"</span><span style="color: #800000">newspaper.h</span><span style="color: #800000">"</span></font><span style="color: #000000">
<font face="M+ 1mn regular">#include </font></span><font face="M+ 1mn regular"><span style="color: #800000">"</span><span style="color: #800000">reader.h</span><span style="color: #800000">"</span>

<span style="color: #0000ff">int</span> main(<span style="color: #0000ff">int</span> argc, <span style="color: #0000ff">char</span> *</font><font face="M+ 1mn regular"><span style="color: #000000">argv[]) {
    QCoreApplication app(argc, argv);

    Newspaper newspaper(</span><span style="color: #800000">"</span><span style="color: #800000">Newspaper A</span><span style="color: #800000">"</span></font><font face="M+ 1mn regular"><span style="color: #000000">);
    Reader reader;
    QObject::connect(</span>&newspaper, &Newspaper::newPaper, &reader, &</font><font face="M+ 1mn regular"><span style="color: #000000">Reader::receiveNewspaper);

    newspaper.send();

    </span><span style="color: #0000ff">return</span></font><span style="color: #000000"><font face="M+ 1mn regular"> app.exec();
}</font></span>

只有继承了QObject类的类, 才具有信号槽的能力.

凡是 QObject类(不管是直接子类还是间接子类), 都应该在第一行代码写上 **Q_OBJECT**, 不管是不是使用信号槽,都应该添加这个宏, Q_OBJECT 只能被添加在头文件中.

这个宏的展开将为我们的类提供信号槽机制、国际化机制以及 Qt 提供的不基于 C++ RTTI 的反射能力.

这个宏将由 moc 做特殊处理(我们会在后面章节中介绍 moc, 这里可以将其理解为一种预处理器, 是比 C++ 预处理器更早执行的预处理器), 不仅仅是宏展开这么简单. moc 会读取标记了 Q_OBJECT 的头文件,生成以 moc_ 为前缀的文件,比如 newspaper.h 将生成 moc_newspaper.cpp. 注意, 由于 moc 只处理头文件中的标记了 Q_OBJECT 的类声明,不会处理 cpp 文件中的类似声明. 因此, 如果我们把 Newspaper 和 Reader 类的代码直接写于 main.cpp 中, 是无法得到 moc 的处理的.

Newspaper 类的新加了一个 signals 块, 所列出的就是该类的信号. 信号就是一个返回值是 void 的函数名, 参数是该信号需要传递的数据, 信号函数是不需要函数体的. Newspaper 类的 send() 函数比较简单, 只有一个语句 emit newPaper(m_name), emit 是 Qt 对 C++ 的扩展, 是一个关键字(其实也是一个宏), emit 的含义是发出, 也就是发出 newPaper() 信号, 感兴趣的接收者会关注这个信号.

Reader 类更简单, 因为这个类需要接受信号, 所以我们将其继承了QObject, 并且添加了Q_OBJECT宏. 与信号函数不同, 槽函数必须自己完成实现代码, 槽函数就是普通的成员函数, 因此作为成员函数, 也会受到 public、private 等访问控制符的影响.


总结一下自定义信号槽需要注意的事项:

发送者和接收者都需要是 QObject的 子类;

使用 signals 标记信号函数, 信号是一个函数声明, 返回 void, 不需要实现函数代码;

槽函数是普通的成员函数, 作为成员函数, 会受到 public、private、protected 的影响;

使用 emit 在恰当的位置发送信号;

使用 QObject::connect() 函数连接信号和槽. lo0