首页 Qt 学习之路 2 Qt 学习之路 2(4):信号槽

Qt 学习之路 2(4):信号槽

123 15

信号槽是 Qt 框架引以为豪的机制之一。熟练使用和理解信号槽,能够设计出解耦的非常漂亮的程序,有利于增强我们的技术设计能力。

所谓信号槽,实际就是观察者模式。当某个事件发生之后,比如,按钮检测到自己被点击了一下,它就会发出一个信号(signal)。这种发出是没有目的的,类似广播。如果有对象对这个信号感兴趣,它就会使用连接(connect)函数,意思是,用自己的一个函数(成为槽(slot))来处理这个信号。也就是说,当信号发出时,被连接的槽函数会自动被回调。这就类似观察者模式:当发生了感兴趣的事件,某一个操作就会被自动触发。(这里提一句,Qt 的信号槽使用了额外的处理来实现,并不是 GoF 经典的观察者模式的实现方式。)

为了体验一下信号槽的使用,我们以一段简单的代码说明:

// !!! Qt 5
#include <QApplication>
#include <QPushButton>

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    QPushButton button("Quit");
    QObject::connect(&button, &QPushButton::clicked, &QApplication::quit);
    button.show();

    return app.exec();
}

这里再次强调,我们的代码是以 Qt 5 为主线,这意味着,有的代码放在 Qt 4 上是不能编译的。因此,豆子会在每一段代码的第一行添加注释,用以表明该段代码是使用 Qt 5 还是 Qt 4 进行编译。读者在测试代码的时候,需要自行选择相应的 Qt 版本。

我们按照前面文章中介绍的在 Qt Creator 中创建工程的方法创建好工程,然后将main()函数修改为上面的代码。点击运行,我们会看到一个按钮,上面有“Quit”字样。点击按钮,程序退出。

按钮在 Qt 中被称为QPushButton。对它的创建和显示,同前文类似,这里不做过多的讲解。我们这里要仔细分析QObject::connect()这个函数。

在 Qt 5 中,QObject::connect()有五个重载:

QMetaObject::Connection connect(const QObject *, const char *,
                                const QObject *, const char *,
                                Qt::ConnectionType);

QMetaObject::Connection connect(const QObject *, const QMetaMethod &,
                                const QObject *, const QMetaMethod &,
                                Qt::ConnectionType);

QMetaObject::Connection connect(const QObject *, const char *,
                                const char *,
                                Qt::ConnectionType) const;

QMetaObject::Connection connect(const QObject *, PointerToMemberFunction,
                                const QObject *, PointerToMemberFunction,
                                Qt::ConnectionType)

QMetaObject::Connection connect(const QObject *, PointerToMemberFunction,
                                Functor);

这五个重载的返回值都是QMetaObject::Connection,现在我们不去关心这个返回值。下面我们先来看看connect()函数最常用的一般形式:

// !!! Qt 5
connect(sender,   signal,
        receiver, slot);

这是我们最常用的形式。connect()一般会使用前面四个参数,第一个是发出信号的对象,第二个是发送对象发出的信号,第三个是接收信号的对象,第四个是接收对象在接收到信号之后所需要调用的函数。也就是说,当 sender 发出了 signal 信号之后,会自动调用 receiver 的 slot 函数。

这是最常用的形式,我们可以套用这个形式去分析上面给出的五个重载。第一个,sender 类型是const QObject *,signal 的类型是const char *,receiver 类型是const QObject *,slot 类型是const char *。这个函数将 signal 和 slot 作为字符串处理。第二个,sender 和 receiver 同样是const QObject *,但是 signal 和 slot 都是const QMetaMethod &。我们可以将每个函数看做是QMetaMethod的子类。因此,这种写法可以使用QMetaMethod进行类型比对。第三个,sender 同样是const QObject *,signal 和 slot 同样是const char *,但是却缺少了 receiver。这个函数其实是将 this 指针作为 receiver。第四个,sender 和 receiver 也都存在,都是const QObject *,但是 signal 和 slot 类型则是PointerToMemberFunction。看这个名字就应该知道,这是指向成员函数的指针。第五个,前面两个参数没有什么不同,最后一个参数是Functor类型。这个类型可以接受 static 函数、全局函数以及 Lambda 表达式。

由此我们可以看出,connect()函数,sender 和 receiver 没有什么区别,都是QObject指针;主要是 signal 和 slot 形式的区别。具体到我们的示例,我们的connect()函数显然是使用的第五个重载,最后一个参数是QApplication的 static 函数quit()。也就是说,当我们的 button 发出了clicked()信号时,会调用QApplicationquit()函数,使程序退出。

信号槽要求信号和槽的参数一致,所谓一致,是参数类型一致。如果不一致,允许的情况是,槽函数的参数可以比信号的少,即便如此,槽函数存在的那些参数的顺序也必须和信号的前面几个一致起来。这是因为,你可以在槽函数中选择忽略信号传来的数据(也就是槽函数的参数比信号的少),但是不能说信号根本没有这个数据,你就要在槽函数中使用(就是槽函数的参数比信号的多,这是不允许的)。

如果信号槽不符合,或者根本找不到这个信号或者槽函数的话,比如我们改成:

QObject::connect(&button, &QPushButton::clicked, &QApplication::quit2);

由于 QApplication 没有 quit2 这样的函数的,因此在编译时,会有编译错误:

'quit2' is not a member of QApplication

这样,使用成员函数指针,我们就不会担心在编写信号槽的时候会出现函数错误。

借助 Qt 5 的信号槽语法,我们可以将一个对象的信号连接到 Lambda 表达式,例如:

// !!! Qt 5
#include <QApplication>
#include <QPushButton>
#include <QDebug>

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    QPushButton button("Quit");
    QObject::connect(&button, &QPushButton::clicked, [](bool) {
        qDebug() << "You clicked me!";
    });
    button.show();

    return app.exec();
}

注意这里的 Lambda 表达式接收一个 bool 参数,这是因为QPushButtonclicked()信号实际上是有一个参数的。Lambda 表达式中的qDebug()类似于cout,将后面的字符串打印到标准输出。如果要编译上面的代码,你需要在 pro 文件中添加这么一句:

QMAKE_CXXFLAGS += -std=c++0x

然后正常编译即可。

Qt 4 的信号槽同 Qt 5 类似。在 Qt 4 的 QObject 中,有三个不同的connect()重载:

bool connect(const QObject *, const char *,
             const QObject *, const char *,
             Qt::ConnectionType);

bool connect(const QObject *, const QMetaMethod &,
             const QObject *, const QMetaMethod &,
             Qt::ConnectionType);

bool connect(const QObject *, const char *,
             const char *,
             Qt::ConnectionType) const

除了返回值,Qt 4 的connect()函数与 Qt 5 最大的区别在于,Qt 4 的 signal 和 slot 只有const char *这么一种形式。如果我们将上面的代码修改为 Qt 4 的,则应该是这样的:

// !!! Qt 4
#include <QApplication>
#include <QPushButton>

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    QPushButton button("Quit");
    QObject::connect(&button, SIGNAL(clicked()),
                     &app,    SLOT(quit()));
    button.show();

    return app.exec();
}

我们使用了SIGNALSLOT这两个宏,将两个函数名转换成了字符串。注意,即使quit()QApplication的 static 函数,也必须传入一个对象指针。这也是 Qt 4 的信号槽语法的局限之处。另外,注意到connect()函数的 signal 和 slot 都是接受字符串,因此,不能将全局函数或者 Lambda 表达式传入connect()。一旦出现连接不成功的情况,Qt 4 是没有编译错误的(因为一切都是字符串,编译期是不检查字符串是否匹配),而是在运行时给出错误。这无疑会增加程序的不稳定性。

信号槽机制是 Qt 的最大特性之一。这次我们只是初略了解了信号槽,知道了如何使用connect()函数进行信号槽的连接。在后面的内容中,我们将进一步介绍信号槽,了解如何设计自己的信号槽等等。

123 评论

大米 2012年9月1日 - 21:50

好文。拜读了

回复
smile 2013年1月6日 - 13:19

关于Qt5的示例代码,有个地方不太明白:
QObject::connect(&button, &QPushButton::clicked, &QApplication::quit);
这里connect是个静态方法,只穿了sender没有传receiver,怎么还能实现效果呢?

回复
DevBean 2013年1月6日 - 14:25

quit() 函数是 static 的,因此不需要 receiver,只需要传递函数指针就可以了。

回复
fzyz_sb 2014年11月7日 - 16:03

static意味着this指针的存在,即函数中默认用this为接收者,符合重载的概念,所以忽略不计,当然可以把this显示写入,或者直接用app(这里等价于this)来当做参数。

回复
豆子 2014年11月12日 - 14:39

static一般都是没有this指针的吧?

回复
杨晋 2018年8月7日 - 10:07

静态函数终归属于类,不属于某个特定的对象,没有this指针

回复
vae 2017年1月2日 - 09:59

c++this指针

回复
vae 2017年1月2日 - 10:07

查阅,静态函数无this指针。

回复
暴雪 2017年3月9日 - 16:30

你说的这个方法成员函数, 有一个隐式传递的this指针。这是c++的相关知识。

回复
zz 2022年3月2日 - 09:54

connect只是为了发送信号时,能够找到slot函数,之所以要传receiver,是因为如果槽函数是成员函数,第一个参数是this指针,所以拿到对象指针才可以调用,但是全局静态函数是不需要的,所以不用。(对QT不熟,只是用观察者模式来回答)

回复
smile 2013年1月7日 - 16:46

您好,关于Qt5的后两个 QObject::connect() 重载我有一些疑问。
我看了一下qobject.h的源码,发现只有定义了 Q_QDOC 这个宏才有这两个重载。源码如下:
#ifdef Q_QDOC
static QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, const QObject *receiver, PointerToMemberFunction method, Qt::ConnectionType type);
static QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor);
#else
...

然后用下面的代码测试,发现 Q_QDOC 这个宏没有定义:#ifdef Q_QDOC
qDebug()<<"QDOC";
#else
qDebug()<<"none";
#endif

那么为什么这两个重载存在呢?辛苦您解答一下。

回复
豆子 2013年1月7日 - 17:34

Q_QDOC 宏是在生产文档时才会被设置。也就是说,你所列的这两个函数只有在提取文档时才使用了这个签名,而实际的 Qt 库中并没有这两个函数。其对应的函数应该是后面的两个:

//Connect a signal to a pointer to qobject member function
template 
static inline QMetaObject::Connection connect(const typename QtPrivate::FunctionPointer::Object *sender, Func1 signal,
                                              const typename QtPrivate::FunctionPointer::Object *receiver, Func2 slot,
                                              Qt::ConnectionType type = Qt::AutoConnection)
{
    // ...
}

//connect to a function pointer  (not a member)
template 
static inline typename QtPrivate::QEnableIf::ArgumentCount) >= 0, QMetaObject::Connection>::Type
        connect(const typename QtPrivate::FunctionPointer::Object *sender, Func1 signal, Func2 slot)
{
    // ...
}

这是 Qt 的一个“小把戏”,文档中给出的两个函数并不存在,你是用的其实是后面两个函数,只不过如果在提取文档时,会提取出那么两个函数签名。这回让你以为这两个函数的确是这个样子。归根究底,是不想让你知道下面那种“丑陋的”写法。

回复
smile 2013年1月7日 - 18:05

太感谢了,我看了一下Qt5的文档,发现文档上QObject::connect()确实只有您写的这5种重载。
我是一个C++新手,可能问的一些问题比较弱智,望见谅。

回复
KiDD 2014年10月17日 - 18:39

豆子大大好,关于connect的最后两个重载,我也有一个小小的疑问。
再使用类似:
QMetaObject::Connection connect(const QObject *, PointerToMemberFunction,
const QObject *,
PointerToMemberFunction,
Qt::ConnectionType)

这样的方法的时候,是编译器感觉参数自动推断了模板的类型了么,事实上我们并没有显式的指定template 啊。

我刚开始学校模板元编程,问的问题比较弱智,望指教。

回复
豆子 2014年10月19日 - 10:25

模板函数一般是可以自动推断类型的,这一点与模板类有区别。

回复
littleWhite 2019年6月18日 - 17:20

请问 connect模板函数的PointerToMemberFunction参数是根据调用的实参的类型自动推断的,但是在connect的函数定义内是怎么使用这个参数的啊,这个pointer形参又不知道它对应的函数指针指向的函数有几个参数,什么类型返回值?
而向STL中的例如sort()它就明确了Compare类型的functor是个二元谓词。

hwb 2013年1月13日 - 17:32

你好,有个问题想问你.
QObject::connect(&button, &QPushButton::clicked, [](bool) {
qDebug() << "You clicked me!";
});
这个bool是用来干嘛的?怎么就只有类型? “这里的 Lambda 表达式接收一个 bool 参数,这是因为 QPushButton 的 clicked() 信号实际上是有一个参数的。” 有点不懂,clicked()不是返回void吗? 在这,bool 只是类型,没有对象来接受啊?
为什么不是
QObject::connect(&button, &QPushButton::clicked, [](bool judge) {
if(true==judge)
{
qDebug() << "You clicked me!";
}
});

回复
豆子 2013年1月14日 - 09:18

bool 是 clicked() 信号的参数。信号槽的连接是参数要对应起来,因为 clicked() 信号有一个 bool 类型的参数,因此在连接时需要给 Lambda 表达式对应的参数。这与返回值没有关系。C++ 中允许只写参数类型,不写参数名称,这种情况意味着有这么个参数,但是你没有办法使用它。如果你写上了参数名且没有使用,编译器通常会给出一个警告,这种写法主要用于避免这种警告。

回复
hwb 2013年1月14日 - 11:57

知道了,但是如果我不传参数也可以啊,如你所说的,“信号槽要求信号和槽的参数一致,所谓一致,是参数类型一致。如果不一致,允许的情况是,槽函数的参数可以比信号的少,即便如此,槽函数存在的那些参数的顺序也必须和信号的前面几个一致起来。” 我看了下文档,那个bool是有默认实参的,但是,如果我在Lamba中不传参数,也可以运行.

回复
豆子 2013年1月14日 - 15:42

是的,不写参数也是可以的,这个不是必须的。

回复
gavin 2013年1月25日 - 20:02

想学Qt很久了,一直没找到合适的教程,你这个太棒了!
另外我想问下,你的wordPress主题是什么啊?挺不错的

回复
豆子 2013年1月27日 - 21:12

感谢!Wordpress 的主题一般可以在站点的最下方版权处找到,本站的主题是 zBench

回复
紫铭 2013年2月9日 - 16:31

我运行了quit程序,出现void QAbstractButton::clicked(bool) is protected,within this context

回复
豆子 2013年2月9日 - 21:39

你是运行的 Qt4 的版本吗?应该没有问题的

回复
紫铭 2013年2月10日 - 11:01

现在的Qt 5.0.1 for Windows 32-bit (MinGW 4.7, 823 MB) 是不是直接下载安装就行了?

回复
豆子 2013年2月10日 - 16:30

是的,5.0.1 已经有 mingw 版本的了,直接可以使用了

回复
紫铭 2013年2月10日 - 20:47

在win7上可以,咋在win8上就出问题呢(安装没问题),编译时出现问题,谢谢指导~~

豆子 2013年2月11日 - 16:21

这个我也不清楚,没有 Windows 8 的环境。首先你先看看,mingw 4.7 能不能支持 Windows 8。到晚上看看有没有类似的情形呗

提问 2013年2月15日 - 21:20

我用Qt5碰到了点问题:
#include
#include
#include
#include

int main(int argc,char *argv[])
{
QApplication App(argc,argv);
QWidget Window;
Window.resize(200,100);

QSpinBox Spb(&Window);
QSlider Sld(Qt::Horizontal,&Window);
Sld.setRange(0,999);
Spb.setRange(0,999);

Spb.setGeometry(0,0,100,30);
Sld.setGeometry(110,0,80,30);

QObject::connect(&Sld, &QSlider::valueChanged, &Spb, &QSpinBox::setValue); //这个没问题

QObject::connect(&Spb, &QSpinBox::valueChanged, &Sld, &QSlider::setValue); //这个有问题

Window.show();
return App.exec();
}

第一个信号槽,没问题,可以编译通过,第二个提示:
E:\QTApp\project5\project5.cpp:22: 错误:C2665: “QObject::connect”: 3 个重载中没有一个可以转换所有参数类型
E:\QT\5.0.0\msvc2010\include\QtCore/qobject.h(211): 可能是“QMetaObject::Connection QObject::connect(const QObject *,const char *,const QObject *,const char *,Qt::ConnectionType)”
E:\QT\5.0.0\msvc2010\include\QtCore/qobject.h(214): 或 “QMetaObject::Connection QObject::connect(const QObject *,const QMetaMethod &,const QObject *,const QMetaMethod &,Qt::ConnectionType)”
E:\QT\5.0.0\msvc2010\include\QtCore/qobject.h(218): 或 “QMetaObject::Connection QObject::connect(const QObject *,const char *,const char *,Qt::ConnectionType) const”
尝试匹配参数列表“(QSpinBox *, overloaded-function, QSlider *, void (__thiscall QAbstractSlider::* )(int))”时

回复
豆子 2013年2月15日 - 22:02

关于这一点我在后面的一篇《深入 Qt5 信号槽新语法》中介绍过,可以参考一下:https://www.devbean.net/2012/09/qt-study-road-2-deep-qt5-signals-slots-syntax/

回复
提问 2013年2月17日 - 03:13

谢谢了,这个信号槽的问题解决了。

回复
mi 2013年5月15日 - 13:00

我想问问
用qt5.02编译 "You clicked me!"那个程序点击后无反应
是什么原因?

回复
豆子 2013年5月15日 - 13:07

我测试是正常的,是不是哪里写错了?

回复
mi 2013年5月15日 - 13:21

可以了
是我的手抖没码好
ps好快的回复速度,感谢

回复
轨迹 2013年5月28日 - 10:08

博主,我想问一下,这几个connect函数,有没有区别那些有更好的性能的问题呢?

回复
豆子 2013年5月28日 - 10:20

这个没有区别,一般只是调用同一个底层的函数,参数的不同应该在函数中进行适配。这点儿影响直接忽略就好了

回复
newbie 2013年6月7日 - 19:02

很好的教程,请检查下,前一章节我这边打不开,似乎链接有问题。

回复
豆子 2013年6月8日 - 08:53

谢谢哦~哪一章有问题?我检查一下链接。

回复
漂木 2013年8月16日 - 10:29

在Mac上面用C++11特性那块,报了一个'initializer_list' file not found。查了下,要用:
QMAKE_CXXFLAGS = -mmacosx-version-min=10.7 -std=gnu0x -stdlib=libc++
CONFIG +=c++11

回复
豆子 2013年8月16日 - 17:27

感谢提醒,没有使用 Mac 测试代码

回复
Clark 2016年5月3日 - 16:06

我测试了下,发现只需要添加
CONFIG += c++11
即可。

回复
junekai 2013年9月2日 - 09:41

感谢博主的教程,我在尝试lambda那块报错了..
C:\Qt\workspace\learn\main.cpp:13: 错误:invalid use of incomplete type 'class QDebug'
qDebug() << "You clicked me!";

这是我的代码: ^
int main(int argc, char *argv[])
{
QApplication app(argc, argv);

QPushButton button("Quit");
// QObject::connect(&button, &QPushButton::clicked, &QApplication::quit);

QObject::connect(&button, &QPushButton::clicked, [](bool){
qDebug() << "You clicked me!";
});

button.show();

return app.exec();

在pro里面我也最下面添加了..
QMAKE_CXXFLAGS += -std=c++0x

请问是怎么回事呢,版本是5.1for android, win8,谢谢了

回复
豆子 2013年9月2日 - 14:28

你的错误是:invalid use of incomplete type ‘class QDebug’,这是因为你的文件没有 include ,与 lambda 表达式无关。

回复
junekai 2013年9月2日 - 14:40

对,大意了没看清,谢谢了..

回复
liuy 2014年7月17日 - 16:51

请问一下,文件没有include是什么意思啊?第一次学QT

回复
豆子 2014年7月20日 - 15:45

缺少必要的头文件,比如需要 include <QDialog> 之类

回复
liuy 2014年7月17日 - 19:09

我已经知道了,好弱智啊!

回复
搁浅的贝 2013年9月27日 - 15:30

请问你写的这些代码是选择的什么项目,QT控制台或者是别的?

QT控制台是怎么回事呢?运行也没有弹出什么框?

回复
豆子 2013年9月28日 - 15:17

这个没有刻意选择,Qt 控制台就是命令行界面的程序,不过我在 Windows 上测试是有 cmd 窗口弹出的。

回复
latecomers 2013年12月19日 - 09:16

QT菜鸟,这两天正在学QT,目前看了一半您之前写的《QT学习之路》。就是想问问,我是从您之前的那个看起,还是直接看现在的2版本呢?有什么学习推荐呢?

回复
豆子 2013年12月19日 - 09:20

建议直接看这一系列就可以了。《Qt学习之路2》基本包含了《Qt学习之路》的全部内容,并且适用于 Qt 5,讲解也更详细一些。

回复
latecomers 2013年12月19日 - 11:51

非常感谢,但是我想从1系列看起(差不多看完了),到时重新再看2系列巩固一遍,持续期待您对QT的后续部分的精彩讲解。

回复
laser杨万荣 2014年2月17日 - 18:41

博主的文章写得不错,值得一看。

回复
scutlpf 2014年2月26日 - 16:15

你好,请问信号和槽可以在不同的线程中吗

回复
豆子 2014年2月27日 - 13:50

可以的

回复
abird 2014年3月12日 - 16:35

请教楼主:
关于文中提到的那句
QObject::connect(&button, &QPushButton::clicked, &QApplication::quit);
看到你说qt4无法编译通过,我特意在qt4试了。
错误提示说clicked是保护的?可我看源代码protected:在它下面呢~上面明明是public Q_SLOTS:
请教这是咋回事?不解~

回复
豆子 2014年3月13日 - 09:26

Qt 4 里面没有这个 connect() 函数,不支持使用函数指针的语法。这个语法是 Qt 5 提供的。鉴于 C++ 编译器的错误信息通常比较晦涩,所以这主要是因为 Qt 4 不支持这种语法,具体还不一定是哪里的代码出现问题。

回复
Edward 2014年4月20日 - 15:52

非常感谢您的文章
另外想请教一下:
我之前在图书馆借了本QT4的书,上面有关于自动关联的方法就是把函数名改为 on_XXX_clicked() 这种,但是我在QT5上运行后没用,点了按钮没反应,这也是QT4与QT5不同的地方之一吗?感觉有好多地方不一样,比如需要在pro文件里输入QT += widgets

回复
豆子 2014年4月21日 - 11:10

自动关联一般是由 UI 文件生成的,如果你这么写了,不确定自己添加的是不是可以执行自动关联。一般 on_RECEIVER_SIGNAL() 都不是自己添加的。至于 QT += widgets 是 Qt5 新增的内容。这一点在之前的文章中应该有说明的。

回复
rrrdr 2014年4月21日 - 16:32

这个问题好像曾经研究过,要自动关联好像还需要再写两三个项工作,其中一项是setobjectname什么的,其它就忘记了,找度娘吧

回复
blackTIME 2014年5月24日 - 23:59

学生党,拜读了!
刚刚接触QT,恨不得把博主写的全背下来!

回复
ngz 2014年7月29日 - 08:01

qt的信号槽属于异步通信啊
qt将异步通信作为核心特征,这是qt 区别于标准 c++ 的重要特性
还有 用qScriptConnect可以实现qt与js异步通信,虽然是入门章节 也要简单提一下啊
QScriptEngine eng;
QLineEdit *edit = new QLineEdit(...);
QScriptValue handler = eng.evaluate("(function(text) { print('text was changed to', text); })");
qScriptConnect(edit, SIGNAL(textChanged(const QString &)), QScriptValue(), handler);
qt可是基于c++和js两种语言的哦,这一点应该从一开始的章节就开始强调,否则读者是无法体会到qt的真正强大之处的

回复
豆子 2014年7月29日 - 09:21

个人认为,一开始就介绍跨语言的整合会让初学者感到迷茫,所以先从最基本的 C++ 入手。感谢您的宝贵意见。

回复
MuhammadWang 2014年8月10日 - 21:34

在Ubuntu 14.04, Qt 5.3.1下用Lambda编译有问题, 需要在.pro文件里需要加一个CONFIG += c++11

回复
MuhammadWang 2014年8月10日 - 22:24

啊, 原来下面已经写了QMAKE_CXXFLAGS += -std=c++0x了 -_-

回复
豆子 2014年8月11日 - 09:52

Qt 4 需要添加 QMAKE_CXXFLAGS,Qt 5 只要添加 CONFIG += c++11 就好了

回复
Tony 2014年8月25日 - 12:56

connect函数第二个参数像这样加个括号就会报错:call to non-static member function without an object argument。请教是什么原因,C++学的不扎实还请见谅
QObject::connect(&button, &(QPushButton::clicked), &QApplication::quit);

回复
豆子 2014年8月26日 - 09:50

这里是取函数指针,加上括号后编译器需要先执行括号里面的语句,而这个语句是非法的,所以报错

回复
暴雪 2017年3月9日 - 20:23

QObject::connect(&button, &QPushButton::clicked, &QApplication::quit);
上述QPushButton::clicked已经是函数地址了, 为什么还要加上取地址符号呢(&)??, 这样不是会创建一个临时的指针变量吗, 多次一举。后两个取地址符号去掉后同样通过编译了。 求楼主答疑解惑, 是否有深层我没有想到的作用?

回复
豆子 2017年3月9日 - 21:41

不知道你用的什么编译器,我使用 VC2015 测试的,编译 QObject::connect(&button, QPushButton::clicked, QApplication::quit); 会有一个错误:C3867: “QAbstractButton::clicked”: 非标准语法;请使用 "&" 来创建指向成员的指针;因此,按照标准的表达,应该使用 & 取址才是正确的。至于你通过编译,可能是因为使用的编译器比较宽容的原因。

回复
暴雪 2017年3月10日 - 20:19

我也在vs2015测试了, 跟你回复的内容一致。 查了相关资料, 可以不使用&的编译器是要与C语言的用法一致。 以前指向成员函数的指针几乎用不到, 也就生疏了。
另外,豆子, 怎么才能方便快速的看到你给我的回复呢????

豆子 2017年3月13日 - 22:47

快速查看回复这个我也不大了解,不知道 wordpress 有没有类似的功能

xin 2019年6月12日 - 14:13

您好,这里clicked不是静态成员函数,为什么能通过类名加域操作符访问?不是应该先创建一个对象吗?

回复
豆子 2019年6月23日 - 15:42

这并不是访问静态成员函数(注意后面没有小括号,也就不是一个函数调用),而是取的成员函数指针。

回复
Wuhen 2014年11月26日 - 17:17

你好,我使用的是Qt 5.3.2 x86版本,在Windows 8.1 Pro系统下,使用的是Visual Studio 2013。安装了官方的插件。

我在对“You clicked me !”这个程序测试的时候,发现点击按钮之后没有任何反应,是因为现在版本更新了亦或是Windows的问题呢?

回复
Wuhen 2014年11月26日 - 17:31

问题已经解决了,调用qDebug()是输出到底部的调试里面了。

非常抱歉提了一个这么傻的问题。

回复
Joey 2015年1月8日 - 13:51

我想请问一下,信号槽机制,信号传给槽的参数是引用还是值?

回复
piaodonkey 2015年4月10日 - 09:44

这样创建的按钮和在ui里拖动创建的有什么区别,为什么ui里(edit signals/slots)的我看不到代码

回复
豆子 2015年4月21日 - 12:29

基本没有区别。UI 文件是基于 XML 的描述性文件。Qt 使用特殊的编译器(uic)将 UI 文件翻译成 C++ 源代码,然后再进行编译。使用 UI 文件可以进行快速开发,而直接使用代码创建则更方便进行控制。所以这个选择权在你手中

回复
piaodonkey 2015年4月25日 - 18:17

明白了,新手让您见笑了,谢谢豆子哥

回复
那个谁上山 2015年4月14日 - 21:48

请问用vs2013建qt项目,没有pro文件,怎么办?

回复
豆子 2015年4月21日 - 12:27

使用VS插件创建好像是看不到 pro 文件,但是在菜单中可以看到,或者直接到工程目录找找看。

回复
那个谁上山 2015年4月21日 - 12:30

Add-in里边可以生成pro文件,再用creator 打开,谢谢回复噢

回复
那个谁上山 2015年4月25日 - 21:41

豆子老师,我要处理一张图片中的某一部分,用鼠标一点击,拉一个矩形框出来,只处理矩形框中的内容。并且处理完后返回点的坐标还是位于原图像坐标系下。请问这个要怎么实现呢?

回复
豆子 2015年4月27日 - 09:13

这个没有既定的算法,需要自己实现。个人思路是,记录下选区位置,然后将算法内容仅应用于该选区。

回复
那个谁上山 2015年4月27日 - 10:33

拉矩形框这个动作需要用到哪些类呢?

修罗 2015年4月24日 - 11:25

豆子老师,我初学Qt,问你一个问题可以吧,我写了一个编辑框,如果我用鼠标选中了一段文字,这样能返回它的起始点和选择长度呢?谢谢啊。

回复
豆子 2015年4月24日 - 16:33

QTextCursor有关于 selection 的相关函数,例如selectionStart()

回复
修罗 2015年4月26日 - 11:23

恩,找到相关的函数了,已经解决,谢谢。

回复
wangdapao 2015年5月6日 - 12:38

豆子老师我想问,就是下载qt creater的时候,有好多个 有opengl的 mingw的还有,我们新手应该下载那个?还有就是这个qt creater 是不是还得跟计算机的位数匹配啊?

回复
wangdapao 2015年5月8日 - 22:25

豆子老师我想问,就是下载qt creater的时候,有好多个 有opengl的 mingw的还有,我们新手应该下载那个?还有就是这个qt creater 是不是还得跟计算机的位数匹配啊?

回复
豆子 2015年6月3日 - 11:15

对于新手来说基本没有区别,可以忽略。Qt Creator 的位数是否匹配也没有太大关系,只是 32 位系统不能安装 64 位应用,反之则都可以。

回复
SixDay 2015年6月15日 - 19:55

你好~
有一个问题想问一下
QObject::connect(&news,&newspaper::newPaper,&readers,&reader::receieveNewspaper)
signal(newPaper)和slot(receieveNewspaper)要求应当是一个指针指向函数的地址吧?
函数的名称就是函数的地址,可是为什么&newspaper::newPaper还要再取一次地址?
我直接编译newspaper::newPaper不能通过,可以解答一下嘛?

回复
豆子 2015年6月17日 - 23:47

你可以理解成“函数的名称就是函数的地址”,但是在 C++ 中,取函数地址必须使用 & 运算符,所以这里必须这样写的。

回复
pigball 2015年7月15日 - 23:29

Qt 4 的connect()函数与 Qt 5 最大的区别在于,Qt 4 的 signal 和 slot 只有const char *这么一种形式,可是Qt4有三种重载bool connect(const QObject *, const QMetaMethod &,
const QObject *, const QMetaMethod &,
Qt::ConnectionType);,不是有函数么?

回复
蓝色 2015年11月6日 - 22:18

请问,QMetaMethod类型是用来做啥用的?

回复
豆子 2015年11月9日 - 10:13

QMetaMethod 用于实现反射机制,即利用函数名调用一个函数这类功能

回复
赵鑫 2015年11月9日 - 18:02

QMetaMethod 还是没明白是干什么的 ,利用函数名调用 一个函数这类功能,能具体举一个例子吗

回复
豆子 2015年11月9日 - 21:04

这个一般是一些高级用法,比如 Qt 4 中的信号槽,实际是用两个字符串进行关联,即 Qt 会记录下,当信号 aSIGNAL 发出时,调用 aSLOT 函数。此时,aSIGNAL 和 aSLOT 在 Qt 内部以字符串的形式存储。当运行时,Qt 发现了 aSIGNAL 信号,则找到其对应的 aSLOT 字符串,此时就利用 QMetaMethod 去调用这个字符串对应的函数。如果感兴趣,可以详细了解下反射机制(不仅是 Qt,可以看看 Java 之类的,会更清晰一些,因为 C++ 语言级别的反射比较弱,所以 Qt 按照 Java 的方式做了增强)。

回复
Chan 2015年12月25日 - 19:23

豆子哥,您好,本人刚开始看Qt(并且C++功底不好,C++是自学的)
对于以下语句有疑问:
MetaObject::Connection connect(const QObject *, PointerToMemberFunction,
Functor);//这是第五个重载函数
QObject::connect(&button,&QPushButton::clicked,&QApplication::quit);//对吗?
QObject::connect(&button, QPushButton::clicked, QApplication::quit);
两个都可以运行,并不出错~~~好奇怪!

回复
lichuanming166 2016年2月18日 - 17:25

编译会出现下面的错误,为什么啊,我刚开始学。。。求指导D:\Qt_project_test\signalslot\signalslot\main.cpp:11: error: no matching function for call to 'QObject::connect(QPushButton*, void (QAbstractButton::*)(bool), qMain(int, char**)::)'
qDebug() << "You clicked me!";});
^

回复
豆子 2016年2月22日 - 22:20

这是因为没有 include <QDebug> 这个类

回复
过堂风 2016年5月10日 - 16:18

很不错,很早就写的东西了到现在依然适用

回复
canid 2016年7月22日 - 16:11

const QObject * 这不是指针吗?实际为什么传的是引用?

回复
豆子 2016年7月23日 - 11:01

什么叫实际传的是引用?

回复
canid 2016年7月23日 - 15:59

加个&符号不是引用吗?

回复
豆子 2016年7月23日 - 17:30

在这里 & 是取址符号,究竟是引用还是取址,是根据上下文来判断的。引用的话是必须初始化的

回复
Leo 2016年8月26日 - 10:28

你好,刚开始看大神的文章,请问是直接看《QT学习之路2》,还是需要先看《Qt学习之路》?

回复
豆子 2016年8月26日 - 11:01

可以直接看《QT学习之路2》,这个与《Qt学习之路》内容差不多,但是直接基于 Qt 5 的。

回复
小中中 2016年10月17日 - 17:37

为什么我的QT打开了一个项目并运行之后,打开另外一个项目运行的还是前面一个项目的结果

回复
豆子 2016年10月17日 - 22:21

这个需要看代码吧,不大清楚是怎么回事

回复
Tom 2017年1月24日 - 17:52

加载新工程后,需要右键,设置为活动项目

回复
William 2016年10月19日 - 14:28

文章太棒了,以前一直用的MFC,看过一些简单的QT书,这系列教程我会一直看下去,谢谢!

回复
fancymax 2016年12月15日 - 15:45

写的清晰易用,我一个完全不懂QT的人一看就懂了。

回复
LIu 2017年3月8日 - 16:33

受益匪浅,真正的每一个字都要认真读,我看了有4,5遍。才发现每次都有重要的词被略过,博主不错。知乎上看到的,良心讲师啊。

回复
yx 2019年5月16日 - 09:24

博主,感觉教程十分详尽,但是我自己用vs编译出来的程序点击quick没有反应,不会出现文字,是为什么呀

回复
Force 2019年7月3日 - 15:28

我也是,第一次运行没反应。后来加了Pro里的那句重新编译运行,就可以了。然后再删了那句还是可以。不知道什么原因

回复
wansst 2020年4月2日 - 17:22

注意这里的 Lambda 表达式接收一个 bool 参数,这是因为QPushButton的clicked()信号实际上是有一个参数的。Lambda 表达式中的qDebug()类似于cout,将后面的字符串打印到标准输出。如果要编译上面的代码,你需要在 pro 文件中添加这么一句:

直接创建了一个 qtwidget application 然后直接用了 没修改pro文件, 字符串正常显示。版本是 5.3.5

回复
LLLYYY 2020年4月11日 - 22:15

新手膜拜老师

回复
yitao 2020年7月23日 - 22:06

豆子老师你好,我在使用QTimer刷新的时候,报错了,请问是什么地方有问题呢?
代码如下:
_timer = new QTimer(this);
connect(_timer, &QTimer::timeout, this, &QMainWindow::updata);
_timer->start(10);
报错如下:
C2664:‘QMetaObject::Connection QObject::connect(const QObject *, const char *, Qt::ConnectionType) const' : cannot conver argument 2 from void (_cdcl QTimer::QPrivateSignal)' to ' const char *

回复
wsy 2021年11月3日 - 11:36

是不是用了老的qt版本

回复
dillcn 2022年6月13日 - 16:43

感谢豆子!!很好的教程。本文最末的“下一篇”怎么是《QT 学习之路 2》目录?此外,目录页是空的,网页也没有侧边的目录,记得以前好像是有的。

回复
豆子 2022年6月13日 - 16:45

上一篇下一篇是按照发布时间自动生成的,所以可能并不是按照系列的顺序。目录页的话可能是由于主题修改的问题,等我先看下

回复

发表评论

关于我

devbean

devbean

豆子,生于山东,定居南京。毕业于山东大学软件工程专业。软件工程师,主要关注于 Qt、Angular 等界面技术。

主题 Salodad 由 PenciDesign 提供 | 静态文件存储由又拍云存储提供 | 苏ICP备13027999号-2