Post

Qt学习第三章——Qt框架功能概述

Qt框架中的模块

Qt框架中的模块分为两大类:

  • Qt基本模块(Qt Essentials):提供了Qt 在所有平台上的基本功能。
  • Qt附加模块(Qt Add-Ons):实现一些特定功能的模块。

基本模块:

模块描述
Qt CoreQt框架的核心,定义了元对象系统
Qt GUI设计 GUI 界面的基础类,包括事件处理、文字处理等
Qt Widgets提供用于创建GUI的各种界面组件类
Qt D-BusD-Bus是实现进程间通信 (inter process communication,IPC)和远程过程调用(remote procedure call,RPC)的一种通信协议
Qt Test提供一些对应用程序和库进行单元测试的类
Qt QML提供用QML编程的框架,定义了QML和基础引擎
Qt Quick用于开发QML应用程序的标准库,提供创建UI的一些基本类型
Qt Quick Controls提供一套基于Qt Quick的控件,可用于创建复杂的UI
Qt Quick Dialogs提供通过QML使用系统对话框的功能
Qt Quick Layouts提供用于管理界面布局的QML类型
Qt Quick Test提供QML应用程序的单元测试框架

其他模块都依赖于Qt Core模块,使用qmake 构建GUI项目时,qmake项目配置文件中会自动加入QT += core gui

其他模块一般不会被自动加入,在项目中使用某个模块,可以在项目配置中添加:

1
2
QT += multimedia multimediawidgets
QT += sql

全局定义

<QtGlobal>头文件(Qt类的头文件都会包含该文件)包含了Qt类库的一些全局定义:

  • 基本数据类型
  • 函数

全局函数定义

函数功能
T aABS(const T &value)返回变量 value 的绝对值
const T &qBound(const T &min, const T &value, const T &max)返回value限定在 min 至 max 范围之内的值
T qExchange(T &obj, U &&newValue)将obj的值用newValue替换,返回obj的旧值
int qFpClassify(double val)返回val的分类(非数、正或负无穷、零)
bool qFuzzyCompare(double p1, double p2)若p1和p2近似相等,返回true
bool qFuzzyIsNull(double d)如果参数 d 约等于0,返回 true
double qInf()返回无穷大的数
double qIsFinite(double d)若d是一个有限的数,返回 true
bool qIsInf(double d)若d是一个无限大的数,返回true
bool qIsNaN(double d)若d 不是一个数,返回true
const T &qMax(const T &value1, const T &value2)返回 value1 和 value2 中较大的值
const T &qMin(const T &value1, const T &value2)返回 value1 和 value2 中较小的值
qint64 qRound64(double value)将 value 近似最接近的 qint64 整数
int qRound(double value)将 value 近似为最接近的 int 整数

还有一些基础的数学运算函数在<QtMath>头文件中定义,比如三角运算函数、弧度与角度之间的转换函数等。

宏定义

  • qDebug(const char *message, ...):在debugger窗体显示信息

  • QT_VERSION:这个宏展开为数值形式0xMMNNPP(MM = major,NN = minor,PP = patch)表示Qt编译器的版本

1
2
QString str;
qDebug() << str.asprintf("0X%oX", QT_VERSION)
  • QT_VERSION_STR:这个宏展开为Qt版本号的字符串
1
qDebug() << QT_VERSION_STR;
  • Q_BYTE_ORDERQ_BIG_ENDIANQ_LITTLE_ENDIAN:分别表示系统内存中数据的字节序,大端字节序、小端字节序。在需要判断系统字节序时会用到
1
2
3
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
	...
#endif
  • Q_DECL_IMPORTQ_DECL_EXPORT:在使用或设计共享库时,用于导入或导出库的内容,后续章节有其使用的实例
  • Q_UNUSED(name):这个宏用于在函数中定义不在函数体里使用的参数
1
2
3
4
5
void MainWindow::on_imageSaved(int id, const QString &fileName)
{
  Q_UNUSED(id);
  LabInfo->setText("图片保存为:" + fileName);
}
  • foreach(variable, container):用于容器类的遍历
1
2
foreach(const QString &codecName, recorder->supportedAudioCodecs())
  ui->comboCodec->addItem(codecName);

Qt的元对象系统

概述

  • QObject类是所有使用元对象系统的类的基类
  • 必须在一个类的开头部分插入宏Q_OBJECT,才可以使用元对象系统的特性。当MOC发现类中定义了Q_OBJECT宏时,会为其生成相应的C++源文件
  • 元对象编译器(Meta-Object Compiler,MOC)是一个预处理器,先将Qt的特性程序转换为标准C++程序,在由标准C++编译器进行编译

Object类

  • 元对象(meta object):每个QObject及其子类的实例都有一个元对象(静态变量staticMetaObject)。函数metaObject()可以返回它的指针。
  • 类型信息:QObjectinherits()函数可以判断继承关系。
  • 动态翻译:函数tr()返回一个字符串的翻译版本。
  • 对象树:表示对象间从属关系的树状结构。QObject提供了parent()children()findChildren()等函数。对象树中的某个对象被删除时,它的子对象也将被删除。
  • 信号和槽:对象间的通信机制。
  • 属性系统:可以使用宏Q_PROPERTY定义属性,Q0bjectsetProperty会设置属 性的值或定义动态属性;property函数会返回属性的值。

QMetaObject类

  • 元对象是对类的描述,包含类信息、方法、属性等元数据。
  • QObjectQMetaObject提供了一些函数接口,可以获取运行时类型信息,类似标准C++中的RTTI(run time type information)

属性系统

在OObject的子类中可以通过Q_PROPERTY宏定义属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int main(int argc, char *argv[])
{
  QApplication a(argc, argv);
  QPushButton *button = new QPushButton;
  QObject *object = button;
  
  bool isFlat = object->property("flatee").toBool();
  qDebug()<<isFlat; //类不存在,无法打印
  object->setProperty("flatee", true); //setProperty可以在运行时为类定义一个新属性,称之为动态属性
  isFlat = object->property("flatee").toBool();
  qDebug()<<isFlat;
  
  const QMetaObject *meta = object->metaObject(); //QMetaProperty是属性元数据类型
  int index = meta->indexOfProperty("down");
  QMetaProperty prop = meta ->property(index);
  bool res = prop.isWritable();
  qDebug()<<" down(QPushButton) isWritable?: "<<res;
}

信号与槽

信号与槽是元对象系统支持的,对象间通信所采取的机制。

connect函数的使用

1
2
3
4
5
6
7
QMetaObject::Connection QObject::connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnetionType = Qt::AutoConnection)
//如果信号与槽函数带有参数,需要注明参数类型
connect(spinNum, SIGNAL(valueChanged(int)), this, SLOT(updateStatus(int)));

QMetaObject::Connetion QObject:connect(const QObject *sender, const QMetaMethod &signal, const QObject *receiver, const QMetaMethod &member, Qt::ConnetionType = Qt::AutoConnection)
//对于信号或槽函数名称是唯一的,不存在歧义的情况下可以省略参数
connect(lineEdit, &QLineEdit::textChanged, this, &widget::on_textChanged);

如果信号和槽函数都存在重载的情况,则需要使用qOverload<参数类型>进行指定:

1
2
3
4
5
void do_click(bool checked);
void do_click();

connect(ui->checkBox, &QCheckBox::clicked, this,qOverload<bool>(&Widget::do_click));
connect(ui->checkBox, &QCheckBox::clicked, this,qOverload<>(&Widget::do_click));

Qt::ConnectionType表示了信号与槽之间的关联方式

Qt::AutoConnection(缺省值):自动确定关联方式

Qt::DirectConnection:信号被发射时,槽立即执行,槽函数与信号在同一线程

Qt::DirectConnection:事件循环回到接收者线程后执行槽,槽与信号在不同线程

Qt::BlockingQueueConnection:与Qt:QueuedConnection相似,信号线程会被阻塞直到槽执行完毕。当槽函数与信号在同一线程,会造成死锁。

上面的形式都是静态函数版本,无法获得对象本身的this指针。还有成员函数的版本:this->connection(spinNum, SIGNAL(valueChanged(int)),SLOT(updateStatus(int)));

disconnect()函数的使用

  1. 解除与一个sender所有signal的连接:
1
2
disconnect(myObject, nullptr, nullptr, nullptr); //静态函数
myObject->disconnect(); //成员函数
  1. 解除与一个特定信号的所有连接:
1
2
disconnect(myObject, SIGNAL(mySignal()), nullptr, nullptr); //静态函数
myObject->disconnect(SIGNAL(mySignal())); //成员函数
  1. 解除与一个特定receiver的所有连接:
1
2
disconnect(myObject, nullptr, myReceiver, nullptr); //静态函数
myObject->disconnect(myReceiver); //成员函数
  1. 解除一对特定的信号和槽的连接:
1
disconnect(lineEdit, &QLineEdit::textChanged, label, &QLabel::setText); 

sender函数

在槽函数里,使用QObject::sender()可以获取信号发射者的指针

1
QSpinBox *spinbox = qobject_cast<QSpinBox*>(sender());

自定义信号及其使用

信号函数必须无返回值,但可以有输入参数;信号函数无需实现,只需在某些条件下发射信号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class QPerson:public QObject
{
  Q_OBJECT
private: 
  int m_age = 10;
public:
  void incAge();
signals:
  void ageChanged(int value);
}

void QPerson::incAge() {
  m_age++; 
  emit ageChanged(m_age); //发射信号
}

对象树

Q0bject以对象树的形式组织自己,其构造函数里有一个parent参数。当用另一个对象作为父对象创建一个QObject时,它会被添加到父对象的children()列表中,而当父对象被删除时是时,它会被删除。这种方法非常适合GUI对象的需求。例如,QShortcut(键盘快捷键)是相关窗口的子对象,因此当用户关闭该窗口时,快捷键也会被删除。

children函数

1
2
3
4
5
//返回对象的子对象列表
const QObjectList &QOject::children()
  
//函数的返回类型是QObject类型指针列表
typedef QList<QObject*> QObjectList;

例如,修改所有按钮的名称:

1
2
3
4
5
6
7
8
9
10
11
12
QObjectList objList = ui->groupBox_Btns->children();
for(int i = 0; i < objList.size(); i++)
{
  const QMetaObject *meta = objList.at(i)->metaObject();
  QString className = QString(meta->className());
  if(className == "QPushButton")
  {
    QPushButton *btn = qobject_cast<QPushButton*>(objList.at(i));
    QString str-btn->text();
    btn->setText(str+"*");
  }
}

findChild函数

返回此对象的一个具有给定名称的子对象,该子对象可以转换为类型T,如果没有该对象,则返回nullptr。

1
2
3
4
5
6
template <typename T> T QObject::findChild(const QString &name = QString(), Qt::FindChildOptions options = Qt::FindChildrenRecursively)

const QPushButton *button = parentWidget->findChild<QPushButton*>("button1");

//返回parentWidget的一个子按钮QPushButton,名为“button1”
//即使这个按钮不是父元素的直接元素

省略name将匹配所有对象。搜索是递归执行的,除非options指定为FindDirectChildrenOnly

findChildren函数

返回此对象的所有具有给定名称的子对象,这些子对象可以转换为类型T,如果没有此类对象,则返回一个空列表。省略name参数将匹配所有对象的名称。搜索是递归执行的,除非options指定了FindDirectChildrenOnly选项。

1
QList<QWidget*> widget = parentWidget.findChildren<QWidget*>("widgetname");

元对象系统功能测试示例

Qt的元对象系统(Meta-Object System)是Qt编程的基础,它对标准C++语言进行了扩展,支持信号与槽、属性等特性。本示例基于QObject自定义了一个类TPerson,演示元对象系统一些特性的使用,包括定义类信息,定义属性,定义动态属性,自定义信号等。

This post is licensed under CC BY 4.0 by the author.