在我的博文《在VS2008中使用Qt编程》中,介绍了在VS2008中搭建QT开发环境,并举了一个简单的例子。此篇将介绍信号与槽的使用。

    信号与槽的使用,需要用到moc(即meta object compiler)。
    这是因为:当要在GUI中用到信号与槽,就需在.h文件中的类里写入Q_OBJECT宏。而任何含有Q_Object的类都必须使用Qt的moc工具生成对应的cpp文件,然后在项目里面包含这个cpp,编译才能成功,否则会出错链接错误,如下。
    总结:
    1) Qt中的元对象系统是用来处理对象间通讯的信号/槽机制、运行时的类型信息和动态属性系统。
    2) moc读取C++源文件(应该是.h头文件吧)。如果它发现其中包含一个或多个类的声明中含有Q_OBJECT宏,它就会给含有Q_OBJECT宏的类生成另一个含有元对象代码的C++源文件。这个生成的源文件可以被类的源文件包含(#include)到或者和这个类的实现一起编译和连接。

    下面开始编程:
    A 编写 main.cpp

 
  
  1. #include "stdafx.h"  
  2. #include <QtGui/QApplication>   
  3. #include "hello.h"  
  4.  
  5. int _tmain(int argc, _TCHAR* argv[])  
  6. {  
  7.     QApplication app(argc,argv);        
  8.     Widget w;  
  9.     w.show();  
  10.     return app.exec();    
  11. }

 

   B 编写 hello.h

 
  
  1. #ifndef WIDGET_H  
  2. #define WIDGET_H  
  3. #include <QWidget>  
  4. #include <stdio.h>  
  5.  
  6. namespace Ui {  
  7.     //class Widget;  //1 把Widget换成Form (共3处)  
  8.     class Form;  
  9. }  
  10.  
  11. class Widget : public QWidget {  
  12.     Q_OBJECT  
  13. public:  
  14.     Widget(QWidget *parent = 0);  
  15.     ~Widget();  
  16.  
  17. protected:  
  18.     void changeEvent(QEvent *e);  
  19.  
  20. private:  
  21.     //Ui::Widget *ui; //2 把Widget换成Form (共3处)  
  22.     Ui::Form *ui;  
  23. public slots:  
  24.     void on_pushButton_clicked(void);  
  25.  
  26. };  
  27.  
  28. #endif // WIDGET_H 

   C 编写 hello.cpp

 
  
  1. #include "stdafx.h"  
  2. #include "hello.h"  
  3. #include "ui_hello.h"  
  4. //#include "moc_hello.cpp"   //不是头文件,不可以加此句  
  5.  
  6. Widget::Widget(QWidget *parent) :  
  7.     QWidget(parent),  
  8.     //ui(new Ui::Widget)  //3 把Widget换成Form (共3处)  
  9.     ui(new Ui::Form)  
  10. {  
  11.     ui->setupUi(this);  
  12. }  
  13.  
  14. Widget::~Widget()  
  15. {  
  16.     delete ui;  
  17. }  
  18.  
  19. void Widget::changeEvent(QEvent *e)  
  20. {  
  21.     QWidget::changeEvent(e);  
  22.     switch (e->type()) {  
  23.     case QEvent::LanguageChange:  
  24.         ui->retranslateUi(this);  
  25.         break;  
  26.     default:  
  27.         break;  
  28.     }  
  29. }  
  30.  
  31. void Widget::on_pushButton_clicked(void)  
  32. {  
  33.     ui->label->setText("liangbing8612.blog.51cto.com");  
  34.     printf("liangbing8612.blog.51cto.com");  

    D 制作.ui文件,并生成ui_hello.h
    打开designer,拖入一个pushButton,一个label。保存为hello.ui。然后生成ui_hello.h,并添加到项目的源文件中。

    编译运行,则出现如下错误:
错误 1 error LNK2001: 无法解析的外部符号 "public: virtual struct QMetaObject const * __thiscall Widget::metaObject(void)const " (?metaObject@Widget@@UBEPBUQMetaObject@@XZ
错误 2 error LNK2001: 无法解析的外部符号 "public: virtual void * __thiscall Widget::qt_metacast(char const *)" (?qt_metacast@Widget@@UAEPAXPBD@Z
错误 3 error LNK2001: 无法解析的外部符号 "public: virtual int __thiscall Widget::qt_metacall(enum QMetaObject::Call,int,void * *)" (?qt_metacall@Widget@@UAEHW4Call@QMetaObject@@HPAPAX@Z)
错误 4 fatal error LNK1120: 3 个无法解析的外部命令  
   
    这是因为在源文件中没有添加上moc_hello.cpp文件。
    解决方法:右击hello.h,选择“自定义生成步骤”,“常规”
    命令行:moc.exe hello.h -o moc_hello.cpp
    输出:moc_hello.cpp
    附加依赖项:moc.exe hello.h
    确定,然后,右击hello.h,选择 “编译”,则在文件夹中生成moc_hello.cpp,再将其添加到源文件中。
   
    然后,运行程序,出现错误:
    错误  fatal error C1010: 在查找预编译头时遇到意外的文件结尾。是否忘记了向源中添加“#include "stdafx.h"”? 
    则在moc_hello.cpp文件的开头添加上:#include "stdafx.h"。
    然后,再运行。仍然出现上面错误。这是因为当运行程序,又重新生成了moc_hello.cpp文件(这个新的文件的开头显然是没有#include "stdafx.h"),覆盖了已经修改过的文件。
    解决方法:右击hello.h,选择“自定义生成步骤”,“常规”
    清空“命令行” “输出” “附加依赖项” 里对应的内容。这样在运行程序时就不会再生成新的moc_hello.cpp文件了。然后确定。
 
    这样再次运行程序,可以成功运行。运行结果如图。