初识

Qt怎么安装,网上教程太多了,自己搜一搜看吧。个人觉得qt的编译器也挺好用的,没有必要再额外地使用vs作为编译器,当然这个根据个人习惯吧。

Qt Window Application中,主要有三部分组成,cpp文件,head文件,以及ui文件。新建的默认的目录组织格式如下图所示
image.png
其中,main函数入口的代码如下:

  1. #include "MainWindow.h"
  2. #include <QApplication>
  3. #include <QLabel>
  4. int main(int argc, char *argv[])
  5. {
  6. QApplication a(argc, argv);
  7. MainWindow w;
  8. w.show();
  9. return a.exec();
  10. }

还有一个比较重点的是ui的设计,再qt creator中自带一个UI的设计器,可以用来可视化编辑界面文件。产生的ui文件加入到Forms文件夹中,项目编译之后,uiname.ui 会产生一个对应的 ui_uiname.h 的头文件,在相应的 .cpp 文件中需要引入这个头文件。

以项目默认产生的代码为例:

  1. #include "MainWindow.h"
  2. #include "ui_MainWindow.h"
  3. MainWindow::MainWindow(QWidget *parent)
  4. : QMainWindow(parent)
  5. , ui(new Ui::MainWindow)
  6. {
  7. ui->setupUi(this);
  8. }
  9. MainWindow::~MainWindow()
  10. {
  11. delete ui;
  12. }

ui_MainWindow 中,声明了一个namespace 叫做 ui。同时定义了一个 Ui_MainWindow 类,里面是ui相关的信息。定义了 setupUi 函数,用来做相应类的初始化。

信号和槽机制

信号(Signal)就是在特定情况下被发射的事件,例如PushButton 最常见的信号就是鼠标单击时发射的 clicked() 信号。

槽(slot)函数,就是响应信号的时候的函数。它和一般的函数没有任何区别,可以定义在类的任何部分(public、private 或 protected),可以具有任何参数,也可以被直接调用。

信号与槽关联是用 QObject::connect() 函数实现的,其基本格式是:

  1. QObject::connect(sender, SIGNAL(signal()), receiver, SLOT(slot()));

connect() 是 QObject 类的一个静态函数,而 QObject 是所有 Qt 类的基类,在实际调用时可以忽略前面的限定符,所以可以直接写为:

  1. connect(sender, SIGNAL(signal()), receiver, SLOT(slot()));

其中,sender 是发射信号的对象的名称,signal() 是信号名称。信号可以看做是特殊的函数,需要带括号,有参数时还需要指明参数。receiver 是接收信号的对象名称,slot() 是槽函数的名称,需要带括号,有参数时还需要指明参数。
SIGNAL 和 SLOT 是 Qt 的宏,用于指明信号和槽,并将它们的参数转换为相应的字符串。

在 qt5 中,可以直接把四个参数变成相应的指针,例如:

  1. connect(ui->pushButton, &QPushButton::clicked, this, &MainWindow::setText);

可以举一个具体的实例:

  1. QObject::connect(btnClose, SIGNAL(clicked()), Widget, SLOT(close()));
  2. connect(spinNum, SIGNAL(valueChanged(int)), this, SLOT(addFun(int));

信号和槽之间并非一对一的关系,一个信号可以连接多个槽函数,一个槽函数也可以连接多个信号,甚至信号也可以连接其他的信号。

一个比较完整的窗口的实现

一个比较完整的窗口,包括控件的组织和信号与槽函数连接等

Qt元对象系统

元对象系统由以下三个基础组成:

  1. QObject 类是所有使用元对象系统的类的基类。
  2. 在一个类的 private 部分声明 Q_OBJECT宏,使得类可以使用元对象的特性,如动态属性、信号与槽。
  3. MOC(元对象编译器)为每个 QObject 的子类提供必要的代码来实现元对象系统的特性。

属性系统

Qt提供了一种利用宏的方式给类定义属性的方式。

属性定义

在 QObject 的子类中,用宏 Q_PROPERTY() 定义属性,其使用格式如下:

  1. Q_PROPERTY(type name (READ getFunction [WRITE setFunction] | MEMBER meznberName [(READ getFunction | WRITE setFunction)])
  2. [RESET resetFunction]
  3. [NOTIFY notifySignal]
  4. [REVISION int]
  5. [DESIGNABLE bool]
  6. [SCRIPTABLE bool]
  7. [STORED bool]
  8. [USER bool]
  9. [CONSTANT]
  10. [FINAL])

Q_PROPERTY 宏定义一个返回值类型为 type,名称为 name 的属性,用 READ、WRITE 关键字定义属性的读取、写入函数,还有其他的一些关键字定义属性的一些操作特性。属性的类型可以是 QVariant 支持的任何类型,也可以用户自定义类型。

Q_PROPERTY 宏定义属性的一些主要关键字的意义如下:

  • READ 指定一个读取属性值的函数,没有 MEMBER 关键字时必须设置 READ。
  • WRITE 指定一个设定属性值的函数,只读属性没有 WRITE 设置。
  • MEMBER 指定一个成员变量与属性关联,成为可读可写的属性,无需再设置 READ 和 WRITE。
  • RESET 是可选的,用于指定一个设置属性缺省值的函数。
  • USER变量表明属性是否被设计为面向用户的或用户可修改的类属性。通常,每个类只有一个USER属性。例如,QAbstractButton::checked是按钮类的用户可修改属性。注意QItemDelegate获取和设置widget的USER属性。
  • NOTIFY 是可选的,用于设置一个信号,当属性值变化时发射此信号。
  • DESIGNABLE 表示属性是否在 Qt Designer 里可见,缺省为 true。
  • CONSTANT 表示属性值是一个常数,对于一个对象实例,READ 指定的函数返回值是常数,但是每个实例的返回值可以不一样。具有 CONSTANT 关键字的属性不能有 WRITE 和 NOTIFY 关键字。
  • FINAL 表示所定义的属性不能被子类重载。

QWidget 类定义属性的一些例子如下:

  1. Q_PROPERTY(bool focus READ hasFocus)
  2. Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled)
  3. Q_PROPERTY(QCursor cursor READ cursor WRITE setCursor RESET unsetCursor)

属性的使用
不管是否用 READ 和 WRITE 定义了接口函数,只要知道属性名称,就可以通过 QObject::property() 读取属性值,并通过 QObject::setProperty() 设置属性值。例如:

  1. QPushButton *button = new QPushButton;
  2. QObject *object = button
  3. object->setProperty("flat", true);
  4. bool isFlat= object->property ("flat");

QObject::property() 方法返回的是一个 Qvarient

QT数据类型

QList

是一种泛型容器类,用链表的方式存储一组值,可以进行快速索引,快速插入和删除等操作。
其API是基于索引的,比Qvector的速度要快一些。
可以使用两种方法在 QList的后面追加数据。提供了一系列比较方便的操作符来操作其中的元素

  1. QList<int> intList
  2. // 追加数据
  3. intList.append(3);
  4. intList << 5;
  5. intList.insert(); //插入数据
  6. intList.replace();
  7. intList.removeAt();
  8. intList.swap();
  9. intList.prepend();
  10. intList.removeFirst();
  11. intList.removeLast();
  12. intList.removeAll();
  13. intList.removeOne();

访问其中的数据的时候,可以使用 []操作符来进行访问,对于非 const的链表,返回的是元素的以用。如果用于只读的话,可以使用at()方法。
QList的另一个常用用法是,从链表中取出一个元素(元素不再在链表之中),并对该元素进行操作。QList提供了以下操作来实现此功能:
takeAt()、takeFirst()、takeLast()

QStringList

QStringListQList的用法有很多地方是一样的,QList的方法它都可以使用。不过还有一些比较特殊的方法。
合并字符串

  1. QStringList codeLanguages;
  2. codeLanguages << "PHP"
  3. << "C++";
  4. qDebug() << codeLanguages;
  5. QString joint = codeLanguages.join(", ");
  6. qDebug() << joint;

查找字符串位置

  1. QStringList qstrList;
  2. qstrList<<"Java" << "Android" << "Qt Creator" << "Java" << "C++";
  3. int index = qstrList.indexOf("Java");//返回 0
  4. int index = qstrList.indexOf("Java");//返回 3

替换

  1. QStringList files;
  2. files << "$file/src/moc/moc.y" << "$file/src/moc/moc.l" << "$file/include/qconfig.h";
  3. files.replaceInStrings("$file", "/usr/file");

过滤

  1. QStringList list;
  2. list << "Bill Murray" << "John Doe" << "Bill Clinton";
  3. QStringList result;
  4. result = list.filter("Bill");
  5. // result: ["Bill Murray", "Bill Clinton"]
  6. //比较字符串的时候
  7. //Qt::CaseSensitive 搜索区分大小写
  8. //Qt::CaseInSensitive 不区分大小写
  9. result = list.filter("bill",Qt::CaseInSensitive);
  10. // result: ["Bill Murray", "Bill Clinton"]

QString

std::stringQString,使用QString::fromStdString()方法。

QT 独有机制

QDebug

最好引入头文件#include <QDebug>。使用的时候如下

  1. # include <QDebug>
  2. qDebug().noquote().nospace() << QString::fromStdString(std::string);
  3. // 如果不用noquote()的话,输出的时候会自动加上引号

QDir

官网文档

moc(meta object compiler)

moc 全称是 Meta-Object Compiler,也就是“元对象编译器”。Qt 程序在交由标准编译器编译之前,先要使用 moc 分析 C++ 源文件。如果它发现在一个头文件中包含了宏 QOBJECT,则会生成另外一个 C++ 源文件。这个源文件中包含了 Q_OBJECT 宏的实现代码。这个新的文件名字将会是原文件名前面加上 moc 构成。
这个新的文件同样将进入编译系统,最终被链接到二进制代码中去。因此我们可以知道,这个新的文件不是“替换”掉旧的文件,而是与原文件一起参与编译。另外,我们还可以看出一点,moc 的执行是在预处理器之前。因为预处理器执行之后,Q_OBJECT 宏就不存在了。
Qt 将源代码交给标准 C++ 编译器,如 gcc 之前,需要事先将这些扩展的语法去除掉。完成这一操作的就是 moc。
在命令行下输入moc yourfilename.h -o moc_youfilename.cpp生成不带Q_OBJENT的源文件。

总结起来就是:

  • moc 就是“元对象编译器”;
  • Qt程序在交给标准编译器预编译之前要使用 moc 分析 C++ 源文件;
  • 如果有宏 Q_OBJECT,则生成一个包含Q_OBJECT 宏的实现代码的C++源文件;
  • 新生成的源文件参与到标准编译器的编译中;
  • 编译过程中如果找不到对应的moc文件就会出现链接错误,此时要添加上对应的moc文件;