上一节我们详细分析了connect()
函数。使用connect()
可以让我们连接系统提供的信号和槽。但是,Qt 的信号槽机制并不仅仅是使用系统提供的那部分,还会允许我们自己设计自己的信号和槽。这也是 Qt 框架的设计思路之一,用于我们设计解耦的程序。本节将讲解如何在自己的程序中自定义信号槽。
信号槽不是 GUI 模块提供的,而是 Qt 核心特性之一。因此,我们可以在普通的控制台程序使用信号槽。
经典的观察者模式在讲解举例的时候通常会举报纸和订阅者的例子。有一个报纸类Newspaper
,有一个订阅者类Subscriber
。Subscriber
可以订阅Newspaper
。这样,当Newspaper
有了新的内容的时候,Subscriber
可以立即得到通知。在这个例子中,观察者是Subscriber
,被观察者是Newspaper
。在经典的实现代码中,观察者会将自身注册到被观察者的一个容器中(比如subscriber.registerTo(newspaper)
)。被观察者发生了任何变化的时候,会主动遍历这个容器,依次通知各个观察者(newspaper.notifyAllSubscribers()
)。
下面我们看看使用 Qt 的信号槽,如何实现上述观察者模式。注意,这里我们仅仅是使用这个案例,我们的代码并不是去实现一个经典的观察者模式。也就是说,我们使用 Qt 的信号槽机制来获得同样的效果。
//!!! Qt5 #include <QObject> ////////// newspaper.h class Newspaper : public QObject { Q_OBJECT public: Newspaper(const QString & name) : m_name(name) { } void send() { emit newPaper(m_name); } signals: void newPaper(const QString &name); private: QString m_name; }; ////////// reader.h #include <QObject> #include <QDebug> class Reader : public QObject { Q_OBJECT public: Reader() {} void receiveNewspaper(const QString & name) { qDebug() << "Receives Newspaper: " << name; } }; ////////// main.cpp #include <QCoreApplication> #include "newspaper.h" #include "reader.h" int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); Newspaper newspaper("Newspaper A"); Reader reader; QObject::connect(&newspaper, &Newspaper::newPaper, &reader, &Reader::receiveNewspaper); newspaper.send(); return app.exec(); }
当我们运行上面的程序时,会看到终端输出 Receives Newspaper: Newspaper A 这样的字样。
下面我们来分析下自定义信号槽的代码。
这段代码放在了三个文件,分别是 newspaper.h,reader.h 和 main.cpp。为了减少文件数量,可以把 newspaper.h 和 reader.h 都放在 main.cpp 的main()
函数之前吗?答案是,可以,但是需要有额外的操作。具体问题,我们在下面会详细说明。
首先看Newspaper
这个类。这个类继承了QObject
类。只有继承了QObject
类的类,才具有信号槽的能力。所以,为了使用信号槽,必须继承QObject
。凡是QObject
类(不管是直接子类还是间接子类),都应该在第一行代码写上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 的处理的。解决方法是,我们手动调用 moc 工具处理 main.cpp,并且将 main.cpp 中的#include "newspaper.h"
改为#include "moc_newspaper.h"
就可以了。不过,这是相当繁琐的步骤,为了避免这样修改,我们还是将其放在头文件中。许多初学者会遇到莫名其妙的错误,一加上Q_OBJECT
就出错,很大一部分是因为没有注意到这个宏应该放在头文件中。
Newspaper
类的 public 和 private 代码块都比较简单,只不过它新加了一个 signals。signals 块所列出的,就是该类的信号。信号就是一个个的函数名,返回值是 void(因为无法获得信号的返回值,所以也就无需返回任何值),参数是该类需要让外界知道的数据。信号作为函数名,不需要在 cpp 函数中添加任何实现(我们曾经说过,Qt 程序能够使用普通的 make 进行编译。没有实现的函数名怎么会通过编译?原因还是在 moc,moc 会帮我们实现信号函数所需要的函数体,所以说,moc 并不是单纯的将 Q_OBJECT 展开,而是做了很多额外的操作)。
Newspaper
类的send()
函数比较简单,只有一个语句emit newPaper(m_name);
。emit 是 Qt 对 C++ 的扩展,是一个关键字(其实也是一个宏)。emit 的含义是发出,也就是发出newPaper()
信号。感兴趣的接收者会关注这个信号,可能还需要知道是哪份报纸发出的信号?所以,我们将实际的报纸名字m_name
当做参数传给这个信号。当接收者连接这个信号时,就可以通过槽函数获得实际值。这样就完成了数据从发出者到接收者的一个转移。
Reader
类更简单。因为这个类需要接受信号,所以我们将其继承了QObject
,并且添加了Q_OBJECT
宏。后面则是默认构造函数和一个普通的成员函数。Qt 5 中,任何成员函数、static 函数、全局函数和 Lambda 表达式都可以作为槽函数。与信号函数不同,槽函数必须自己完成实现代码。槽函数就是普通的成员函数,因此作为成员函数,也会受到 public、private 等访问控制符的影响。(我们没有说信号也会受此影响,事实上,如果信号是 private 的,这个信号就不能在类的外面连接,也就没有任何意义。)
main()
函数中,我们首先创建了Newspaper
和Reader
两个对象,然后使用QObject::connect()
函数。这个函数我们上一节已经详细介绍过,这里应该能够看出这个连接的含义。然后我们调用Newspaper
的send()
函数。这个函数只有一个语句:发出信号。由于我们的连接,当这个信号发出时,自动调用 reader 的槽函数,打印出语句。
这样我们的示例程序讲解完毕。我们基于 Qt 的信号槽机制,不需要观察者的容器,不需要注册对象,就实现了观察者模式。
下面总结一下自定义信号槽需要注意的事项:
- 发送者和接收者都需要是
QObject
的子类(当然,槽函数是全局函数、Lambda 表达式等无需接收者的时候除外); - 使用 signals 标记信号函数,信号是一个函数声明,返回 void,不需要实现函数代码;
- 槽函数是普通的成员函数,作为成员函数,会受到 public、private、protected 的影响;
- 使用 emit 在恰当的位置发送信号;
- 使用
QObject::connect()
函数连接信号和槽。
Qt 4
下面给出 Qt 4 中相应的代码:
//!!! Qt4 #include <QObject> ////////// newspaper.h class Newspaper : public QObject { Q_OBJECT public: Newspaper(const QString & name) : m_name(name) { } void send() const { emit newPaper(m_name); } signals: void newPaper(const QString &name) const; private: QString m_name; }; ////////// reader.h #include <QObject> #include <QDebug> class Reader : public QObject { Q_OBJECT public: Reader() {} public slots: void receiveNewspaper(const QString & name) const { qDebug() << "Receives Newspaper: " << name; } }; ////////// main.cpp #include <QCoreApplication> #include "newspaper.h" #include "reader.h" int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); Newspaper newspaper("Newspaper A"); Reader reader; QObject::connect(&newspaper, SIGNAL(newPaper(QString)), &reader, SLOT(receiveNewspaper(QString))); newspaper.send(); return app.exec(); }
注意下 Qt 4 与 Qt 5 的区别。
Newspaper
类没有什么区别。
Reader
类,receiveNewspaper()
函数放在了 public slots 块中。在 Qt 4 中,槽函数必须放在由 slots 修饰的代码块中,并且要使用访问控制符进行访问控制。其原则同其它函数一样:默认是 private 的,如果要在外部访问,就应该是 public slots;如果只需要在子类访问,就应该是 protected slots。
main()
函数中,QObject::connect()
函数,第二、第四个参数需要使用SIGNAL
和SLOT
这两个宏转换成字符串(具体事宜我们在上一节介绍过)。注意SIGNAL
和SLOT
的宏参数并不是取函数指针,而是除去返回值的函数声明,并且 const 这种参数修饰符是忽略不计的。
下面说明另外一点,我们提到了“槽函数是普通的成员函数,作为成员函数,会受到 public、private、protected 的影响”,public、private 这些修饰符是供编译器在编译期检查的,因此其影响在于编译期。对于 Qt4 的信号槽连接语法,其连接是在运行时完成的,因此即便是 private 的槽函数也是可以作为槽进行连接的。但是,如果你使用了 Qt5 的新语法,新语法提供了编译期检查(取函数指针),因此取 private 函数的指针是不能通过编译的。
144 评论
下面给出 Qt 4 中【响应】的代码:
应该是【相应】吧
已经修改过来,感谢指出!
感谢分享~
感谢指出,已经修改过了!
Qt4的代码newspaper.h的信号应为 void newPaper(const QString &name) const ; 少了一个const
错误信息:passing 'const Newspaper' as 'this' argument of 'void Newspaper::newPaper(const QString&)' discards qualifiers
btw,有错别字 “所谓,为了使用信号槽,必须继承 QObject”应该是“所以”吧。
ps:感谢豆子
我使用的是Ubuntu+Qt5Beta1
QObject::connect函数貌似不接受const成员函数指针
另外,const函数不能调用非const成员函数
以下函数的声明需要修改
in class Newspaper:
void send() const; -> void send();
in class Reader:
void receiveNewspaper(cosnt QString & name) const; -> void receiveNewspaper(const QString &name);
已经修改过了,多谢指出!
qt4 代码里有两处笔误:
void receiveNewspaper(cosnt QString & name) 这里的cosnt...字母被调换了
void newPaper(const QString &name); 这个,后面忘记加 const了吧.
void newPaper(const QString &name) const;
已经修改过了,谢谢指出!
Qt5的代码在我这编译不了,环境:Win7(32bit)+QtCreator+qt5_msvc2010
总是报Lnk2001的错误,不知为何?
编译截图在这里:
截图在这
这个错误一般是少加了 Q_OBJECT 宏,如果你在之后添加的,要先运行下 qmake 重新生成 makefile 才可以的
我直接复制你的代码,应该不会有问题啊
换了一个开发环境:
Ubuntu12.04 (32bit)+ Qt5
同样复制代码,测试通过。可能是我windows的vc2010一些设置有问题吧。
看评论很重要,构建前先运行一下qmake,觉得可以在前面的章节对qmake进行简单介绍,毕竟挺重要的
找到解决办法了,贴在这里,为以后碰到同样问题的兄弟省点时间:
先qmake一下再构建项目就可以了。
具体可以参考这里:http://stackoverflow.com/questions/8247404/why-does-this-simple-qt-application-not-link
有时候就是因为没有重新运行 qmake,导致 makefile 版本较旧。一般先尝试重新运行 qmake 再编译看看。
是的,只要qmake一下就好。谢谢
看了好久不知道如何qmake一下
如果使用 Qt Creator,就在项目上面点右键,有运行 qmake 的选项。
菜单栏有一个`构建(B)`,点击`执行qmake`
文中一处笔误"比如 newspaper.h 将生成 moc_newspaper.h",应该是生成moc_newspaper.cpp。
感谢指出哦!
最近在学习Qt,感谢作者精彩的教程!
问一个可能非常粗浅的问题:
这句“Newspaper(const QString & name):m_name(name)”
应该是个构造函数,那“:m_name(name)”是什么意思?
这个是 C++ 语法,叫做“成员初始化列表”,例如 m_name(name) 作用相当于在构造函数中添加这么一句:m_name = name。但是,初始化列表的执行是在构造函数的实际语句之前进行的。如果对此不理解,需要重新看看 C++ 基础了哦~
在后面的章节,也有人问,看到解答了,没见过这种初始化的方式,回去翻了下《C++ primer》,还真有:shock:
写得真好,想通过qt平台做点事情和学习一下软件设计
嘿嘿,过奖过奖
槽函数就是普通的成员函数,因此也会受到 public、private 等访问控制符的影响。
博主,是怎么的影响呢?我好想发现把slots声明为private的,没有影响。
同时我把slots函数声明为虚函数(此时为private的)。派生出新的类DReader,在派生类没有实现虚函数。
在connect的时候,接受的对象是DReader的对象,代码同样正确运行的。
不是很清楚这个slots是public,private是否有什么影响。
首先说明,public、private 这样的访问修饰符是供编译器检查的,对于运行期连接的信号槽,不会受到这些修饰符的影响。也就是说,槽函数作为成员函数,会受到 public、private 等访问控制符的影响;作为槽,始终是 public 的。另外需要说明一点,由于 Qt5 的新语法提供了编译期检查,因此 private 的槽函数在 Qt5 的新语法情况下是无法通过编译的。
很感谢豆子的分享。。。。
“作为槽,始终是 public 的”的说法是否有误?
如果connect()是成员函数的话,槽好像可以为private的。
例如第8章,添加动作里的槽open()。
这里说的是,当这个函数作为槽使用,也就是在 connect() 函数中使用的时候,用 Qt 4 的语法,会无视 private 限定符的(意思是,你可以在 connect() 函数中将信号与 private 的槽函数连接起来,这不会报错)。当然,你也可以使用 private 修饰槽函数,只是这个 private 修饰符只在直接调用函数时起作用。然而在 Qt 5 中,如果使用新语法就不能这样使用了。
作为槽,始终是 public 的。哦,原来这样子
博主,我想问下,main.cpp里你包含的头文件是,而我用也可以通过并成功运行,请问这两者有什么需要注意的地方吗
你的头文件没有写出来哦,用 < 和 > 进行转义
你好,请问:为什么我复制您的代码却仍有问题:显示连接错误:LNK2001,这是为什么呢?
LNK2001 是链接错误,应该是库没有找到或者其它什么问题。
你好,谢谢豆子,后来使用了手动执行qmake,就可以了编译通过了,但是在我这边必须手动执行qmake才可以呢?
你的 Q_OBJECT 宏是不是编译过之后才加上上的,或者其它什么原因,导致不是一开始就有这个宏?如果是这样的话,后来加上这个宏的话都必须重新手动 qmake 一下才能检测到新增加的宏。
豆子,你人太好了,同样的错误,你都会不厌其烦地回答
啊,原来是这个原因,谢谢豆子~~~ 😀
写的真好 😛
QT控制台和纯C/C++的那种控制台有何区别。
QCoreApplication a(argc,argv)做了什么处理?干啥用的。
如果你想知道
QCoreApplication
做了哪些工作,最好去看看 Qt 源代码,这不是很容易就能解释清楚的。不过它与纯 C/C++ 的控制台程序很大的一个区别是在main()
函数的最后一句:return a.exec();
。Qt 会开启一个事件循环,监听程序发出的事件;普通 C/C++ 控制台程序,除非你自己编写事件循环,否则是没有这个实现的。问了一出想一出, 🙄 刚接触这个玩意。中文乱码怎么治
之前用QT写纯C的时候乱码。网友说用GBK,还真行。不过在QT控制台里就不行了。
使用 GBK 没有乱码是因为中文 Windows 平台默认编码是 GBK。这个问题没有很通用的解决方案,原因在于 C++ 编译器没有规定文件编码和字符串内部编码,二者一旦不一致就会出现乱码。这个只能由开发人员约定,整个工程使用 UTF-8(一般不是 GBK)文件编码,并且文件中不出现中文。所有中文均通过
tr()
函数提取字符串翻译获得。这样一般不会有编码问题了。楼主你好,我想问个很浅的问题,我断断续续学了很久C++,对于程序内的int main(int argc, char *argv[]),这个的含义一直模糊不清,望楼主告知,知道程序都需要这个,但是不懂
这个问题你应该看看 C++ 的基础。简单来说,main() 函数是所有程序的入口点。一个程序启动所执行的函数就是 main() 函数。它的两个参数是执行程序时所传入的命令行参数。
/home/hysteria/qt/newspaper/main.cpp:14: 错误:undefined reference to `Newspaper::newPaper(QString const&)'
这是怎么回事阿?代码直接复制你的。。
QMAKE_CXXFLAGS += -std=c++0x加入这句就没问题了,为什么加入这个就行了呢?
gcc 默认不使用 C++11 标准进行编译,如果要使用 C++11 编译就需要增加编译器参数 -std=c++0x;另外也可以在 pro 文件中增加 CONFIG += c++11,这个需要自己测试下那个能成功
D:\Qtproject\untitled1\main.cpp:46: 错误:newspaper.h: No such file or directory
D:\Qtproject\untitled1\main.cpp:47: 错误:reader.h: No such file or directory
想问一下为什么这样?菜鸟来的,谢谢
看看你的文件是不是在同一目录?一般会是路径的问题。
速度好快啊!原来忘记加入头文件了。谢谢啊
Sorry, I'm very very new to Qt——
我在编译过程中出现了"newspaper.h"&"reader.h" no such file or directory 的问题,按照豆子老师在所有相关评论里的解决方案做问题都依然存在。
1、路径里没有中文,且文件都在同一路径下
2、无论Q_OBJECT添加与否,都无法通过通过编译,且重新添加Q_OBJECT后手动执行QMAKE依然没用
3、在.pro文件中添加HEADERS += "newspaper.h"和 HEADERS += " reader.h",或者在mainwindow.h中添加 #include "newspaper.h"和#include "reader.h"也都没有用
4、将main.cpp中的改为"moc_newspaper.h"和"moc_reader.h"也没有用。
5、另,.pro文件中已添加QT += widgets
苯宝宝要炸了,,求豆子教教我!!
我知道自己错哪里了。。。
最后是怎么解决的啊?我也遇到这样的问题了。谢谢
爱死这篇文章了。。。帮俺解决了一大串链接问题!!!
Qt5不再需要Q_OBJECT宏,已验证
如果你的类里面需要发出信号的话,还是必须使用 Q_OBJECT 宏的,并且 Q_OBJECT 宏不仅仅用于信号槽机制。所以,只要是 QObject 的子类,不管有没有用到信号槽,最好都加上 Q_OBJECT 宏。
我有两台电脑装了QT,reader.h将Q_OBJECT 宏注释掉后,在一台编译通过另一台编译未通过。同意你的说法,最好都加上。
可能都是不可用的,之所以有的可以编译通过,可能是因为有之前生成的 moc 之后的文件。你可以完全重新构建一下试试是不是可以。
注释掉Q_OBJECT宏,清除并重新构建也可以编译过,并正常运行。就是编译多了一句“reader.h(0): Note: No relevant classes found. No output generated.”
那这个的确比较奇怪的
豆哥,Qt4的代码你是这样写的,
QObject::connect(&newspaper, SIGNAL(newPaper(QString)),
&reader, SLOT(receiveNewspaper(QString)));
是不是应该这样?
QObject::connect(&newspaper, SIGNAL(newPaper(const QString)&),
&reader, SLOT(receiveNewspaper(const QString&)));
两种写法都是可以的。Qt 4 的 SLOT 和 SIGNAL 宏在处理之前有一个“标准化”的步骤,会将参数的 const 和引用都去除,所以结果其实是一样的。
通常新建项目时,QT都为我们在main.cpp里面自动添加一个QAPPLICATION,你的代码里面使用的是QCoreApplication。我查了下文档,他们的继承关系是QApplication : QCoreApplication : QObject,那么请问有什么特殊的原因要使用QCoreApplication吗?
QApplication 支持 GUI 应用,QCoreApplication 仅支持控制台应用。由于示例代码没有使用 GUI,所以使用了 QCoreApplication。如果在使用 Qt Creator 创建控制台项目时,默认也会给出 QCoreApplication 的。
为什么按照楼主的代码打进去,编译出了很多错误,这是什么原因?
D:\Qt\5.2.0\msvc2012\include\QtCore\qpair.h:50: error: C2143: 语法错误 : 缺少“;”(在“'template<'”的前面)
D:\Qt\workspace\5_Slot\main.cpp:20: error: C2628: “Reader”后面接“int”是非法的(是否忘记了“;”?)
D:\Qt\workspace\5_Slot\main.cpp:21: error: C3874: “main”的返回类型应为“int”而非“Reader”
D:\Qt\workspace\5_Slot\main.cpp:30: error: C2664: “Reader::Reader(const Reader &)”: 不能将参数 1 从“int”转换为“const Reader &”
原因如下: 无法从“int”转换为“const Reader”
无构造函数可以接受源类型,或构造函数重载决策不明确
D:\Qt\WorkSpace\5_Slot\reader.h:17: error: C3867: “QMessageLogger::debug”: 函数调用缺少参数列表;请使用“&QMessageLogger::debug”创建指向成员的指针
D:\Qt\WorkSpace\5_Slot\reader.h:17: error: C2297: “<<”: 非法,右操作数包含“const char [18]”类型
D:\Qt\WorkSpace\5_Slot\reader.h:17: error: C2678: 二进制“<<”: 没有找到接受“int”类型的左操作数的运算符(或没有可接受的转换)
D:\Qt\5.2.0\msvc2012\include\QtCore/qchar.h(521): 可能是“QDataStream &operator <<(QDataStream &,QChar)”
D:\Qt\5.2.0\msvc2012\include\QtCore/qbytearray.h(607): 或 “QDataStream &operator <<(QDataStream &,const QByteArray &)”
D:\Qt\5.2.0\msvc2012\include\QtCore/qstring.h(1189): 或 “QDataStream &operator <<(QDataStream &,const QString &)”
D:\Qt\5.2.0\msvc2012\include\QtCore/qobject.h(549): 或 “QDebug operator <<(QDebug,const QObject *)”
d:\qt\5.2.0\msvc2012\include\qtcore\qcoreapplication.h(270): 或 “QDebug operator <<(QDebug,const MSG &)”
D:\Qt\5.2.0\msvc2012\include\QtCore/qiodevice.h(174): 或 “QDebug operator <<(QDebug,QIODevice::OpenMode)”
D:\Qt\5.2.0\msvc2012\include\QtCore/qregexp.h(122): 或 “QDataStream &operator <<(QDataStream &,const QRegExp &)”
D:\Qt\5.2.0\msvc2012\include\QtCore/qregexp.h(127): 或 “QDebug operator <<(QDebug,const QRegExp &)”
D:\Qt\5.2.0\msvc2012\include\QtCore/qstringlist.h(250): 或 “QDataStream &operator <<(QDataStream &,const QStringList &)”
D:\Qt\5.2.0\msvc2012\include\QtCore/qvariant.h(448): 或 “QDebug operator <<(QDebug,const QVariant &)”
D:\Qt\5.2.0\msvc2012\include\QtCore/qvariant.h(537): 或 “QDataStream &operator <<(QDataStream &,const QVariant &)”
D:\Qt\5.2.0\msvc2012\include\QtCore/qvariant.h(539): 或 “QDataStream &operator <<(QDataStream &,const QVariant::Type)”
D:\Qt\5.2.0\msvc2012\include\QtCore/qvariant.h(835): 或 “QDebug operator <<(QDebug,const QVariant::Type)”
D:\Qt\5.2.0\msvc2012\include\QtCore/qlocale.h(966): 或 “QDataStream &operator <<(QDataStream &,const QLocale &)”
D:\Qt\5.2.0\msvc2012\include\QtCore/qlocale.h(971): 或 “QDebug operator <<(QDebug,const QLocale &)”
D:\Qt\5.2.0\msvc2012\include\QtCore/qtextstream.h(228): 或 “QTextStream &operator <<(QTextStream &,QTextStreamFunction)”
D:\Qt\5.2.0\msvc2012\include\QtCore/qtextstream.h(231): 或 “QTextStream &operator <<(QTextStream &,QTextStreamManipulator)”
D:\Qt\5.2.0\msvc2012\include\QtCore/qpoint.h(106): 或 “QDataStream &operator <<(QDataStream &,const QPoint &)”
D:\Qt\5.2.0\msvc2012\include\QtCore/qpoint.h(206): 或 “QDebug operator <<(QDebug,const QPoint &)”
D:\Qt\5.2.0\msvc2012\include\QtCore/qpoint.h(266): 或 “QDataStream &operator <<(QDataStream &,const QPointF &)”
D:\Qt\5.2.0\msvc2012\include\QtCore/qpoint.h(395): 或 “QDebug operator <<(QDebug,const QPointF &)”
尝试匹配参数列表“(int, const QString)”时
D:\Qt\workspace\MakeFile\build-5_Slot-Desktop_Qt_5_2_0_MSVC2012_32bit-Debug\debug\moc_newspaper.cpp:21: error: C2236: 意外的标记“struct”。是否忘记了“;”?
D:\Qt\workspace\MakeFile\build-5_Slot-Desktop_Qt_5_2_0_MSVC2012_32bit-Debug\debug\moc_newspaper.cpp:21: error: C2143: 语法错误 : 缺少“;”(在“{”的前面)
等等
代码可能有错误,比如少了分号之类。这个从错误信息看不出来。
之前用的是VS编译的,刚刚重装了一下,用MinGW编译就没有问题了。还是谢谢楼主
这个不大清楚,我也是用 VS 编译的,没有问题。可能哪里有错误
qQebug()输出时连双眼号一起输出了,如何解决?
这是 qDebug() 输出的,貌似没有办法解决。不过这一点一般也不需要解决的吧,调试信息格式并不面对用户。
恩,这倒也是。不过我是看见你上面说输出格式是:Receives Newspaper: Newspaper A 这样,还以为我哪里弄错了,所以有此一问
之前看过《C++GUI Qt4编程》,现在豆子把qt5与qt4的代码进行比较,非常好,感谢!
ps:现在网页依然不是满屏呀。
感谢支持!话说主题已经是响应式布局了,完全撑满也不好看的吧~
您好,我想问一下,为什么我的程序第一次运行可以正常运行,再点一次运行就会出现error? 下面是错误信息:
collect2.exe:-1: error: error: ld returned 1 exit status
未找到文件:collect2.exe。
找到原因了,因为之前运行的程序没有关闭,这时如果改变代码再执行程序时就会出现这种错误。
解决方法:关闭QTCreator重新打开就可以了,网上听说还可以通过关闭程序进程的方式,但是我一直没有找到相关的进程。
所以注意及时关闭程序就可以了~
这个只要关闭 Qt Creator 下方那个红色方块按钮就可以了。这是由操作系统运行机制决定的,在 Windows 上就会有这个问题,在 Mac OS X 上就没有。
嗯是这样的,但是我这里在没有点红色关闭按钮时立即第二次运行就没有那个红色按钮了,这时候又报错,所以只能关闭creator了。
豆子你好,我想问下信号槽中参数使用引用或指针的时候,connect的连接方式是Qt::QueuedConnection时,Qt是否会把参数拷贝一份放到队列?如果参数是局部变量的话,槽函数中是否会报错?如果参数全局变量的话,能够利用引用或指针的方式在槽函数中达到更改这个全局变量的目的呢?谢谢
Qt::QueuedConnection 主要用于多线程的情况。貌似并没有详细说明会不会将参数加入队列。这种情况下值得注意的是对写参数的加锁这类多线程中需要注意的问题,其它倒是没有注意。至于全局变量的话,就不需要通过参数传递,不是直接可以获得了吗?
还有我看你的Qt4的示例代码中,信号和槽的声明中参数都是引用类型,但是在connect的时候,两个函数的参数都变成QString类型了,这样也可以吗?
这是可以的,因为 Qt 的 SLOT 和 SIGNAL 宏的连接有一个“格式化”的过程,具体是将 const、引用等修饰全部去除,只有一个最简单的类型作为参数。所以这种连接之后二者其实是等价的。
Newspaper(const QString & name) :
m_name(name)
{
}
这个构造函数,里面为什么用const QString & name有什么意义?我一直搞不懂!
这是标准 C++ 的内容,说明参数是一个引用,并且函数不改变参数值。这种写法符合软件工程的要求,并且能够提高性能。
原来这样。感谢!
我也是Qt初学者,学生党,我用的是vs2013+Qt5.3.1来编译的,我也遇到了LNK2001的问题,看了很多博客什么的还是不知道怎么办,您说的qmake一下,我还是不知道该怎么操作。
后来问题解决了,情况是这样的,我原来建了一个Qt应用项目名为hello,建好后它本身就有一个hello.h和hello.cpp的文件,我于是又添加了两个头文件reader.h和newspaper.h后来发现编译过程中这两个新的头文件都没有起作用,就算没有include原来的那个hello.h文件,运行的时候程序还总是先mocing hello.h文件,我很不解,然后我把两个头文件里的 东西放到hello.h里之后就能运行了。(感觉都是很深的原理方面的东西,笨笨的我就是搞不明白)还有就是vs2013里面的Qt项目没有pro文件,想知道vcxproj文件是否可以代替,要修改.pro文件的时候总是很烦恼。。。
求指教(>﹏<)
编译器是根据 makefile 进行编译的。手动编写 makefile 非常复杂,因此 Qt 提供了 qmake 工具,根据 pro 文件生成 makefile。有时候增加或删除了头文件,pro 文件没有更新,或者 pro 文件更新了,但是 makefile 没有更新,都会造成类似的结果。因此,重新运行 qmake 的目的是为了生成最新的 makefile
WOW,原来是这样,不过我在vs2013里面找不到qmake的选项。。。。(囧)对了,好像豆子哥您也是用vs的吧,我用的是一个add-in的插件把Qt加到vs2013里面的,在这里面对一个Qt项目可以把它转换成QMake Generated project(这样的话就不能使用生成.pro文件的功能),还能转换成Qt Add-in project我不知道这两者的区别并且都没有找到qmake的位置,不过发现了能生成.pro 文件还是很开心的。
求豆子哥指教(作为一个初学者能看到这么一份教程感觉甚是欣慰,十分感谢豆子哥的倾情奉献)
我也一直没有用 VS,都是使用的 Qt Creator 配合 VS 编译器。至于两种项目类型的区别还真不大清楚,看起来 QMake Generated Project 是基于 QMake 的;Qt Add-in Project 则是直接使用了 VS 的项目结构。
如何根据pro生成makefile或者是dsp,这样可以用VS2010打开了,求指教
当你运行过 qmake 之后,就已经生成 makefile 了。另外,如果安装了 Qt 插件,VS 应该是能够打开的吧(没有测试,不是很了解这部分)。
VS2010没有安装成功qt5.1.1插件,可以安装qt4.8.5,很纠结,qt4不支持串口通信
我的情况跟你差不多 单独定义reader.h 和 newspaper.h 然后vs报错 找不到moc_read.cpp moc_newspaper.cpp 后来我直接把两个头文件内容放到我一开始新建项目自动生成的头文件中 ok 不知道什么原因....
刚才又试了一下 只要把 那两个头文件先单独编译一下即可....
博主您好。在我的Mac上必须在.pro文件中加上
LIBS += -framework CoreFoundation
才能链接成功。您的PC不需要吗?
CoreFoundation 是 Mac 上面的类库,PC 是不需要的
原来如此,谢谢博主!
豆子你好:),我是用VS2013+Qt5.4.0编译的您的代码,但是现在遇到了一个问题。
提示信息如下:
warning MSB8027: Two or more files with the name of moc_reader.cpp will produce outputs to the same location. This can lead to an incorrect build result. The files involved are GeneratedFiles\Debug\moc_reader.cpp, GeneratedFiles\Release\moc_reader.cpp.
Google了一下,有人说是Qt的BUG,但是没有找到解决方案,求解。
谢谢。
虽然是Warning,但是导致了下面的情况,所以无法进行了:
fatal error C1083: 无法打开源文件: “GeneratedFiles\Release\moc_reader.cpp”: No such file or directory
原因没找到,但是现在解决了问题,重新创建的工程,选择的命令行Qt
const QString &name 是什么意思?&name 不是获得指针吗?
引用形参,c++正在学习中,之前听说c++有很多的缺陷都没怎么看过,这回要认真看一下
当出现在参数中,& 代表引用传递,而不是按值传递。请参考 C++ 基础有关参数传递的部分。
Ot5.4 creator 分成reader.h newspaper.h
运行时提示
QObject::connect: No such slot Reader::receiveNewspaper(QString) in ..\HelloWorld\main.cpp:47
我用VS2013+QT5.3.2,在VS2013中创建QT工程,包含两个cpp和一个头文件,我新添加了一个newspaper头文件,最后编译的时候,提示
错误 1 error C1083: 无法打开源文件: “GeneratedFiles\Debug\moc_newspaper.cpp”: No such file or directory c:\Users\Napchat\documents\visual studio 2013\Projects\槽\槽\c1xx 槽
Qt 的项目路径中不允许出现中文,如果确定没有中文,可以尝试 rebuild 一下试试。
确实如此,中文会显示乱码,已解决,谢豆子
另外问一个问题,就是我现在用QT创建了A、B两个窗体(做界面),准备把B中的信号发到A中的槽里,但我的整个代码是不需要主函数的,我应该把connect函数写在哪里?写在A的cpp里吗?
信号槽主要用于解耦,发出信号的对象不应该知道是谁使用了这个信号;基于这个原则,connect() 函数应该在槽函数的类中。所以按照这个案例,应该是在 A 中更合适一些。
谢谢豆子!
博主大大,文章写的不错赞一个,就是能不能把博文的字体变成黑体。
黑体主要是怕无法显示加粗,网页上面的显示可能会有不方便的地方;浏览器应该是能够设置默认显示字体的吧?
工程编译后qdebug无法打印出相应信息?
在工程文件.pro的最后添加:CONFIG += console 才能打印出qdebug信息 ,新手百度出来的。
默认 debug 信息是输出到 Qt Creator 的控制台,而不是系统运行界面的
豆子,这章可以写写信号函数和槽函数参数的一些内容
我开始迷惑于槽函数的参数怎么接收,因为在connect里槽函数参数是函数原型,不知道他如何接收的name参数
第一点:当信号与槽函数的参数数量相同时,它们参数类型要完全一致。
第二点:当信号的参数与槽函数的参数数量不同时,只能是信号的参数数量多于槽函数的参数数量,且前面相同数量的参数类型应一致,信号中多余的参数会被忽略。
此外,在不进行参数传递时,信号槽绑定时也是要求信号的参数数量大于等于槽函数的参数数量。这种情况一般是一个带参数的信号去绑定一个无参数的槽函数。
你能否讲一下参数传递的内部机制
另:感谢教程,非常好
信号槽具体的实现机制可以参考这里:https://www.devbean.net/2012/12/how-qt-signals-and-slots-work/以及https://www.devbean.net/2012/12/how-qt-signals-and-slots-work-qt5/
为啥你构造的类里面没有析构函数呢??
不显式添加析构函数,编译器会自动添加一个默认析构函数,就像自动添加默认构造函数一样。
fatal error C1083: 无法打开源文件: “GeneratedFiles\Release\moc_reader.cpp”: No such file or directory
fatal error C1083: 无法打开源文件: “GeneratedFiles\Release\moc_newspaper.cpp”: No such file or directory
在VS2013中遇到这个问题,上面有俩位同学也遇到了,不过用他们的方法也解决不了。。。挣扎了好一会,,结果,,结果,,只要把Q_OBJECT这个标记删了重新复制一遍就好了。。。。我的真的无语!!为什么会这样
豆子老师,问你一个关于函数指针的问题,QT里的connect函数中函数指针是函数名前面加上&,但是我查了一下相关资料,对于函数指针,有没有加&是不影响的。可是我把代码中的&去掉之后就会报错,能解释一下吗,我的C++学的不太好请不要见怪。
请教豆子:&Newspaper::newPaper --newPaper并不是静态函数啊,直接取地址是怎么取到的?
这是取成员函数地址的语法,并不是静态函数
豆子老师,为什么我把三段代码全放在main.cpp中(已去除重复include的部分),他就会报错:
/home/zdk/singleSlot/main.cpp:46: error: undefined reference to `Newspaper::newPaper(QString const&)'
我没看完就评论了,请无视。。。。。。
刚买了C++GUI QT4,感觉没有这个教程清晰呢;
现在看你的教程了
这套教程不错
评论中如何插入图片呢?
遇到问题,Qt5.5.1 MSVC2013 x86,运行完这段程序退出Qt Creator时,弹窗显示
“程序仍然在运行。
强制关闭吗?”
前面的例程未出现这个状况,是否缺少一个关闭机制之类的?
如果是窗口界面,当窗口关闭时,Qt 会释放资源;如果是控制台,默认不会释放。你看看在 Qt Creator 运行这里会有红色停止按钮,就是有运行的程序没有关闭。
2012年的好文, 更难能可贵的死2017年豆子仍在回复, 感谢豆子的坚持
豆子哥,我vs2015编译的,编译通过,控制台没输出Receives Newspaper: Newspaper A ,看任务进程,任务正在后台运行。为什么呢?
类名是Newspaper,为啥头文件是newspaper
头文件是物理文件名,与类名不一定一致。
豆子大大我拷贝了您的代码以后,编译出了问题,提示说 ‘newspaper.h’file not found 和 ‘reader.h’file not found
就不知道怎么解决了
新建这两个文件,然后将对应注释下面的代码复制过去就好了
undefined reference to "Newspaper::newPaper(QString const&)"
undefined reference to 'Newspaper::staticMetaObject'
连续六个报错未定义。。在pro文件中加了CONFIG += c++11。。代码是直接复制你的,求教 T T
怎么通过的 老哥 我也是这个错误
不好意思我没有仔细看,编译通过了····
很系统,很完整,很有意义的教程,感谢博主!^_^
使用Cmake,报LNK2019和LNK2001错,请问有没有遇到
博主 复制了一下你的代码 编译遇到个问题 求教
/home/shiverzhao/tools/qt5.12.9/5.12.9/gcc_64/include/QtCore/qobject.h:211: error: expected unqualified-id before ‘=’ token
211 | static QMetaObject::Connection =connect(const QObject *sender, const QMetaMethod &signal,
| ^
哎 莫名奇妙 用的qt4 重新装了一遍
使用的 qt 6.3.1, 如果 Reader.h 中自带默认的构建函数,在 main.cpp 中使用 Reader reader; 会触发报错:
error: call to constructor of 'Reader' is ambiguous