初识
Qt怎么安装,网上教程太多了,自己搜一搜看吧。个人觉得qt的编译器也挺好用的,没有必要再额外地使用vs作为编译器,当然这个根据个人习惯吧。
Qt Window Application中,主要有三部分组成,cpp文件,head文件,以及ui文件。新建的默认的目录组织格式如下图所示
其中,main函数入口的代码如下:
#include "MainWindow.h"
#include <QApplication>
#include <QLabel>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
还有一个比较重点的是ui的设计,再qt creator中自带一个UI的设计器,可以用来可视化编辑界面文件。产生的ui文件加入到Forms文件夹中,项目编译之后,uiname.ui
会产生一个对应的 ui_uiname.h
的头文件,在相应的 .cpp
文件中需要引入这个头文件。
以项目默认产生的代码为例:
#include "MainWindow.h"
#include "ui_MainWindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
在 ui_MainWindow
中,声明了一个namespace 叫做 ui
。同时定义了一个 Ui_MainWindow
类,里面是ui相关的信息。定义了 setupUi
函数,用来做相应类的初始化。
信号和槽机制
信号(Signal)就是在特定情况下被发射的事件,例如PushButton 最常见的信号就是鼠标单击时发射的 clicked() 信号。
槽(slot)函数,就是响应信号的时候的函数。它和一般的函数没有任何区别,可以定义在类的任何部分(public、private 或 protected),可以具有任何参数,也可以被直接调用。
信号与槽关联是用 QObject::connect() 函数实现的,其基本格式是:
QObject::connect(sender, SIGNAL(signal()), receiver, SLOT(slot()));
connect() 是 QObject 类的一个静态函数,而 QObject 是所有 Qt 类的基类,在实际调用时可以忽略前面的限定符,所以可以直接写为:
connect(sender, SIGNAL(signal()), receiver, SLOT(slot()));
其中,sender 是发射信号的对象的名称,signal() 是信号名称。信号可以看做是特殊的函数,需要带括号,有参数时还需要指明参数。receiver 是接收信号的对象名称,slot() 是槽函数的名称,需要带括号,有参数时还需要指明参数。
SIGNAL 和 SLOT 是 Qt 的宏,用于指明信号和槽,并将它们的参数转换为相应的字符串。
在 qt5 中,可以直接把四个参数变成相应的指针,例如:
connect(ui->pushButton, &QPushButton::clicked, this, &MainWindow::setText);
可以举一个具体的实例:
QObject::connect(btnClose, SIGNAL(clicked()), Widget, SLOT(close()));
connect(spinNum, SIGNAL(valueChanged(int)), this, SLOT(addFun(int));
信号和槽之间并非一对一的关系,一个信号可以连接多个槽函数,一个槽函数也可以连接多个信号,甚至信号也可以连接其他的信号。
一个比较完整的窗口的实现
Qt元对象系统
元对象系统由以下三个基础组成:
- QObject 类是所有使用元对象系统的类的基类。
- 在一个类的 private 部分声明 Q_OBJECT宏,使得类可以使用元对象的特性,如动态属性、信号与槽。
- MOC(元对象编译器)为每个 QObject 的子类提供必要的代码来实现元对象系统的特性。
属性系统
属性定义
在 QObject 的子类中,用宏 Q_PROPERTY() 定义属性,其使用格式如下:
Q_PROPERTY(type name (READ getFunction [WRITE setFunction] | MEMBER meznberName [(READ getFunction | WRITE setFunction)])
[RESET resetFunction]
[NOTIFY notifySignal]
[REVISION int]
[DESIGNABLE bool]
[SCRIPTABLE bool]
[STORED bool]
[USER bool]
[CONSTANT]
[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 类定义属性的一些例子如下:
Q_PROPERTY(bool focus READ hasFocus)
Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled)
Q_PROPERTY(QCursor cursor READ cursor WRITE setCursor RESET unsetCursor)
属性的使用
不管是否用 READ 和 WRITE 定义了接口函数,只要知道属性名称,就可以通过 QObject::property() 读取属性值,并通过 QObject::setProperty() 设置属性值。例如:
QPushButton *button = new QPushButton;
QObject *object = button;
object->setProperty("flat", true);
bool isFlat= object->property ("flat");
QObject::property()
方法返回的是一个 Qvarient
QT数据类型
QList
是一种泛型容器类,用链表的方式存储一组值,可以进行快速索引,快速插入和删除等操作。
其API是基于索引的,比Qvector
的速度要快一些。
可以使用两种方法在 QList
的后面追加数据。提供了一系列比较方便的操作符来操作其中的元素
QList<int> intList;
// 追加数据
intList.append(3);
intList << 5;
intList.insert(); //插入数据
intList.replace();
intList.removeAt();
intList.swap();
intList.prepend();
intList.removeFirst();
intList.removeLast();
intList.removeAll();
intList.removeOne();
访问其中的数据的时候,可以使用 []
操作符来进行访问,对于非 const
的链表,返回的是元素的以用。如果用于只读的话,可以使用at()
方法。QList
的另一个常用用法是,从链表中取出一个元素(元素不再在链表之中),并对该元素进行操作。QList提供了以下操作来实现此功能:takeAt()、takeFirst()、takeLast()
QStringList
QStringList
和QList
的用法有很多地方是一样的,QList
的方法它都可以使用。不过还有一些比较特殊的方法。
合并字符串
QStringList codeLanguages;
codeLanguages << "PHP"
<< "C++";
qDebug() << codeLanguages;
QString joint = codeLanguages.join(", ");
qDebug() << joint;
查找字符串位置
QStringList qstrList;
qstrList<<"Java" << "Android" << "Qt Creator" << "Java" << "C++";
int index = qstrList.indexOf("Java");//返回 0
int index = qstrList.indexOf("Java");//返回 3
替换
QStringList files;
files << "$file/src/moc/moc.y" << "$file/src/moc/moc.l" << "$file/include/qconfig.h";
files.replaceInStrings("$file", "/usr/file");
过滤
QStringList list;
list << "Bill Murray" << "John Doe" << "Bill Clinton";
QStringList result;
result = list.filter("Bill");
// result: ["Bill Murray", "Bill Clinton"]
//比较字符串的时候
//Qt::CaseSensitive 搜索区分大小写
//Qt::CaseInSensitive 不区分大小写
result = list.filter("bill",Qt::CaseInSensitive);
// result: ["Bill Murray", "Bill Clinton"]
QString
std::string
转QString
,使用QString::fromStdString()
方法。
QT 独有机制
QDebug
最好引入头文件#include <QDebug>
。使用的时候如下
# include <QDebug>
qDebug().noquote().nospace() << QString::fromStdString(std::string);
// 如果不用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文件;