QT概述
Qt是一个1991年由QtCompany开发的跨平台C++图形用户界面应用程序开发框架。它既可以开发GUI程序,也可用于开发非GUI程序,比如控制台工具和服务器。Qt是面向对象的框架,使用特殊的代码生成扩展(称为元对象编译器(MetaObjectCompiler,moc))以及一些宏,Qt很容易扩展,并且允许真正地组件编程。
QT特征
- 面向对象:Qt具有模块化设计的特点,其控件或元素具备可重用性。一个控件无需了解其具体内容和用途,通过信号(signal)和槽(slot)机制与外界进行通信和交互。并且,所有Qt的控件都支持通过继承的方式进行拓展。
- 控件间的相互通信:Qt提供了信号(signal)和槽(slot)的概念,这是一种安全可靠的通信方法,它允许回调操作,并且支持对象之间在彼此不了解对方详细信息的情况下进行协同合作。这种特性使得Qt非常适合用于真正意义上的控件编程。
- 友好的联机帮助:Qt包含大量的联机参考文档,有超文本HTML格式、UNIX帮助页、man手册以及补充的指南。对于初学者而言,这些指南能够逐步解释Qt编程的相关内容。
- 用户自定义:其他的工具包在应用时普遍存在一个问题,即常常没有完全符合需求的控件,并且生成的自定义控件对于用户来说,就像是一个不透明的黑匣子,难以深入了解和修改。例如,在Motif手册中就讨论过用户自定义控件的相关问题。而在Qt中,用户能够更灵活地进行自定义操作。
- 跨平台优势:由于Qt是一种跨平台的图形用户界面(GUI)工具包,所以它对编程者隐藏了在处理不同窗口系统时可能遇到的潜在问题。为了让基于Qt的程序开发更加便捷,Qt包含了一系列类,这些类能够使程序员避免在文件处理、时间处理等方面受到操作系统细节的限制。
QT Creator
OtCreator是一个用于Ot开发的轻量级跨平台集成开发环境。QtCreator可带来两大关键益处:提供首个专为支持跨平台开发而设计的集成开发环境(IDE),并确保首次接触Qt框架的开发人员能迅速上手和操作。即使不开发Qt应用程序,OtCreator也是一个简单易用且功能强大的IDE。
QT6如何安装?
- 在线安装包下载地址:
- 官网:https://download.qt.io/official_releases/online_installers/
- 中国科学技术大学:https://mirrors.ustc.edu.cn/qtproject/official_releases/online_installers/
- 清华大学:https://mirrors.tuna.tsinghua.edu.cn/qt/official_releases/online_installers/
- 北京理工大学:https://mirror.bit.edu.cn/qtproject/official_releases/online_installers/
- 如果后续需要对组间进行添加、修改或删除可以在安装目录中运行
MaintenanceTool.exe
进行操作。
🔴如果需要下载旧版本的QT可以在 “类别” 栏勾选Archive
,然后点击筛选。
在安装过程中如果遇到下面问题:
下载“https://mirrors. aliyun.com/qtproject/online/qtsdkrepository/windows_x86/android/qt6_624_x86/2022-03-14-1156_meta.7z”时出现网络错误:Error transferringhttps://mirrors.aliyun.com/qtproject/online/qtsdkrepository/windows_x86/android/qt6_624_x86/2022-03-14-1156_meta.7z -server replied:Not Found。
解决办法:用cmd在安装文件下运行下面指令:
可执行文件名.exe --mirror https://mirrors.aliyun.com/qt
可执行文件名.exe -mirror https://mirrors.tuna.tsinghua.edu.cn/qt/
创建QT项目
点击“文件”->“New Project”后会弹出新建项目对话框,选择“Qt Widgets Application”:
其中“Qt Quick Application”是使用QML
语言进行编写的应用程序。
之后在“Details“栏需要注意Base class
的选择:
在使用中会发现代码的检测比较严格,经常出现报错警告,我们可以在“帮助”->“About Plugin”中取消勾选下面选项即可:
GUI 程序设计基础
- QMainWindow:适用于需要完整主窗口架构(如菜单栏、工具栏、状态栏等)的应用。
- QDialog:适用于临时弹出的窗口(如设置窗口、文件选择窗口等)。
- QWidget:适用于没有复杂窗口结构的控件,可以作为顶级窗口或嵌入其他控件中。
- QFrame、QStackedWidget 等其他派生类:根据具体需求来选择,
QFrame
可以提供可定制的边框效果,QStackedWidget
可以管理多个页面。
QDialog 类控件的创建示例
下面我们以 logindialog
类为例,展示如何创建一个继承自 QDialog
的对话框控件。
目录结构
在创建 Qt 项目时,通常会自动生成一套标准的文件结构:
└─ExamSystem
├─ExamSystem.pro # 项目的工程文件
├─头文件
│ └─logindialog.h # 对话框的头文件,用到了.ui文件
├─源文件
│ ├─logindialog.cpp # 对话框的实现文件
│ └─main.cpp # 程序入口
└─界面文件
└─logindialog.ui # Qt Designer 生成的界面文件,使用XML格式描述元件及布局的界面文件
.pro文件
*.pro
文件就是项目工程文件,它是qmake
自动生成的用于生产makefile的配置文件(cmake
生成的是.txt
文件)。之后想要打开已有的项目就可以在打开文件或项目中选择该文件即可。(QT6依然支持qmake
但更推荐使用cmake
)
- qmake是构建项目的软件,它根据.pro文件生成 Makefile 文件,然后C++编译器可以根据 Makefile 文件进行编译和链接.
- qmake还会自动生成MOC(meta-object compiler)和UIC(user interface compiler)生成构建规则.
- **$$**为替换函数的前缀
以下是 qmake 文件中的一些常用变量及其含义(更多参见 qmake Manual):
变量名 | 含义 |
---|---|
TARGET | 指定项目的目标输出文件名,通常是可执行文件或库文件的名称 |
TEMPLATE | 指定项目的类型,如app 表示应用程序,lib 表示库,subdirs 表示包含子项目的工程等 |
SOURCES | 列出项目中的源文件,包括 C++ 源文件(.cpp )和其他支持的文件类型 |
HEADERS | 指定项目中的头文件 |
LIBS | 指定项目中需要链接的库文件,可使用 Unix 样式的表示法来指定库和路径,如LIBS += -L/usr/local/lib -lmath |
INCLUDEPATH | 指定头文件搜索路径 |
CONFIG | 指定编译选项,如debug 表示调试模式,release 表示发布模式,还可以有shared (生成共享对象)、static (生成静态对象)等选项 |
DEFINES | 指定宏定义,用于在代码中进行条件编译等操作 |
DESTDIR | 指定编译输出目录,即编译完成后的可执行文件或库文件等的输出路径 |
FORMS | 要由用户界面编译器(uic)处理的 UI 文件列表 |
RESOURCES | 要包含在最终项目中的资源(.qrc )文件列表 |
QT | 指定项目中使用的 Qt 模块列表,如QT += widgets 表示使用 Qt 的widgets 模块 |
PWD | 当前项目文件(.pro 或.pri )所在的路径 |
PRO_FILE | 项目文件(.pro )的完整路径 |
PRO_FILE_PWD | 项目文件(.pro )所在的路径 |
QMAKE_HOST.arch | 计算机架构 |
QMAKE_HOST.os | 计算机系统 |
QMAKE_HOST.cpu_count | 计算机 CPU 核心数 |
QMAKE_HOST.name | 计算机名 |
QMAKE_HOST.version | 系统版本(数字形式) |
QMAKE_HOST.version_string | 系统版本(字符串形式) |
QMAKE_PRE_LINK | 编译链接前自动执行的命令 |
QMAKE_POST_LINK | 编译链接后自动执行的命令 |
MOC_DIR | moc 文件路径 |
OBJECTS_DIR | 中间产品目录 |
RCC_DIR | qrc 文件的编译输出路径 |
SUBDIRS | 包含子项目时,当前项目引用的子项目列表 |
UI_DIR | ui 文件路径 |
案例分析:
# 引入 Qt 的核心模块和 GUI 模块
QT += core gui
# 如果 Qt 版本大于 4,那么引入 widgets 模块
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
# 设置生成的可执行文件名为 01_logindialog
TARGET = 01_logindialog
# 设置项目类型为应用程序 (app)
TEMPLATE = app
# 要编译的源文件列表
SOURCES += \
main.cpp \
logindialog.cpp
# 要编译的头文件列表
HEADERS += \
logindialog.h
main.cpp(程序入口)
#include "logindialog.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv); // 初始化 QApplication 对象,Qt 应用程序的必备类
logindialog w; // 创建一个 logindialog 对象
w.show(); // 显示对话框
return a.exec(); // 进入事件循环,等待用户交互
}
解释:
Qt一个类对应一个头文件,类名和头文件名一致。
QApplication
应用程序类:
QApplication
是 Qt 中管理应用程序的核心类,负责处理应用的生命周期,包括初始化、事件循环以及退出时的清理工作。一个 Qt 图形界面应用程序必须至少有一个QApplication
对象,它将负责:
- 事件循环:Qt 提供的主消息循环(
a.exec()
)负责接收和调度所有事件消息。这是所有 Qt GUI 程序的关键机制,在事件循环中,Qt 会接收并处理来自系统和用户的事件(如鼠标点击、键盘输入等)。- 应用程序初始化与退出:
QApplication
在程序启动时进行初始化,并在程序退出时执行必要的清理。- 对话框和窗口管理:
QApplication
还负责窗口和对话框的管理,确保它们能够正确显示并与用户进行交互。
a.exec()
:调用exec()
启动 Qt 的事件循环。在这之后,程序会处于“等待状态”,等待用户输入或其他事件的发生。通过exec()
,Qt 会不断地接收并处理事件,直到调用QApplication::quit()
或者应用程序退出为止。
- 事件处理:Qt 在事件循环中会持续地监控用户与界面的交互(如点击按钮、输入文本等),并将这些事件传递给相应的控件。
- 退出机制:当事件循环结束时,
a.exec()
会返回,并且程序会继续执行并退出。通常,退出事件循环是通过用户关闭窗口或调用QApplication::quit()
来触发的。
logindialog.h(头文件)
#ifndef LOGINDIALOG_H
#define LOGINDIALOG_H
#include <QDialog>
QT_BEGIN_NAMESPACE
namespace Ui {
class logindialog;
}
QT_END_NAMESPACE
class logindialog : public QDialog
{
Q_OBJECT
public:
explicit logindialog(QWidget *parent = nullptr);
~logindialog();
private:
Ui::logindialog *ui;
};
#endif // LOGINDIALOG_H
namespace Ui
:其中定义的class logindialog
并不是下面用户手写的类,而是在 编译时 由 Qt 的uic
工具自动生成的类。它负责管理和初始化由Qt Designer
创建的界面元素(如按钮、文本框等)。Q_OBJECT
宏:这是 Qt 中元对象系统的核心,它启用信号与槽机制,用于对象间的通信。Ui::logindialog *ui
:指向可视化的界面
logindialog.cpp(实现文件)
#include "logindialog.h"
#include "ui_logindialog.h" // 引入自动生成的 UI 头文件
logindialog::logindialog(QWidget *parent)
: QDialog(parent),
ui(new Ui::logindialog)
{
ui->setupUi(this); // 设置界面
}
logindialog::~logindialog()
{
delete ui; // 释放资源
}
ui->setupUi(this)
:通过setupUi
方法,logindialog
类的 UI 由 Qt 自动生成的代码进行初始化,确保界面组件正确加载。实现了组件的各种设置、信号与槽的关联。
QT元对象系统 (Meta-Object-System)
一、元对象系统
元对象系统是一个基于标准C++扩展,为Qt提供了信号与槽机制、实时类型信息、动态属性系统。
元对象系统的三个基本条件:类必须继承自QObject
、类声明Q_OBJECT
宏(默认私有)、元对象编译器moc。
信号与槽机制是QT的核心机制,信号与槽式一种高级接口,应用于对象之间的通信,它是QT的核心特性,信号与槽是QT自行定义的一种通信机制,它独立于标准的 C/C++ 语言,要正确的处理信号与槽,必须借助一个称为
moc
(Meta-Object-Compiler),也就是"元对象编译器"。它为高层次的事件处理自动生成所需要的必要代码。Qt 程序在交由标准编译器编译之前,先要使用moc
分析 C++ 源文件。如果moc
发现在一个类头文件中包含了宏Q_OBJECT
,则会生成以 “moc_className.cpp” (自定义类名)的.cpp
文件。这个源文件中包含了Q_OBJECT
宏的实现代码。新的文件同样将进入编译系统,与原文件一起参与编译。构建生成的.o
文件包含moc
生成的cpp文件。
Q_OBJECT
定义在qobjectdefs.h
文件中:
/* qmake ignore Q_OBJECT */
#define Q_OBJECT \
public: \
QT_WARNING_PUSH \
Q_OBJECT_NO_OVERRIDE_WARNING \
static const QMetaObject staticMetaObject; \ // 静态元对象
virtual const QMetaObject *metaObject() const; \// 返回元对象指针
virtual void *qt_metacast(const char *); \ // 设置元对象
virtual int qt_metacall(QMetaObject::Call, int, void **); \
QT_TR_FUNCTIONS \ // 调用元对象的tr方法实现字符串翻译(tr()、trUtf8())
private: \
Q_OBJECT_NO_ATTRIBUTES_WARNING \
Q_DECL_HIDDEN_STATIC_METACALL static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **); \
QT_WARNING_POP \
QT_DEFINE_TAG_STRUCT(QPrivateSignal); \
QT_ANNOTATE_CLASS(qt_qobject, "")
/* qmake ignore Q_OBJECT */
Q_OBJECT
宏中定义的都是操作元对象,真正的信号与槽、属性等内容都是通过QMetaObject
这个元对象实现的:
struct Q_CORE_EXPORT QMetaObject
{
class Connection;
const char *className() const; // 类名
const QMetaObject *superClass() const; // 父类元对象
// 判断对象是否是QObject继承树上一个类的实例
bool inherits(const QMetaObject *metaObject) const noexcept;
QObject *cast(QObject *obj) const
{return const_cast<QObject *>(cast(const_cast<const QObject *>(obj)));}
const QObject *cast(const QObject *obj) const;
#if !defined(QT_NO_TRANSLATION) || defined(Q_QDOC)
QString tr(const char *s, const char *c, int n = -1) const;
#endif // QT_NO_TRANSLATION
......
// internal index-based connect
static Connection connect(const QObject *sender, int signal_index,
const QObject *receiver, int method_index,
int type = 0, int *types = nullptr); // 连接信号与槽
// internal index-based disconnect
static bool disconnect(const QObject *sender, int signal_index,
const QObject *receiver, int method_index); // 断开信号与槽
static bool disconnectOne(const QObject *sender, int signal_index,
const QObject *receiver, int method_index);
// internal slot-name based connect
static void connectSlotsByName(QObject *o); // 自动连接信号与槽
......
}
- Qt元对象系统为什么要继承自QObject?
class Q_CORE_EXPORT QObject
{
Q_OBJECT // Q_PROPERTY动态属性系统
Q_PROPERTY(QString objectName READ objectName WRITE setObjectName NOTIFY objectNameChanged
BINDABLE bindableObjectName)
Q_DECLARE_PRIVATE(QObject)
public:
Q_INVOKABLE explicit QObject(QObject *parent = nullptr);
virtual ~QObject();
virtual bool event(QEvent *event);
virtual bool eventFilter(QObject *watched, QEvent *event); // 事件过滤
#if defined(QT_NO_TRANSLATION) || defined(Q_QDOC)
static QString tr(const char *sourceText, const char * = nullptr, int = -1)
{ return QString::fromUtf8(sourceText); }
#endif // QT_NO_TRANSLATION
......
// 对象树机制管理QObject所有类及子类,children()返回所有当前对象的子对象
inline const QObjectList &children() const { return d_ptr->children; }
void setParent(QObject *parent);
......
QT_CORE_INLINE_SINCE(6, 6)
bool setProperty(const char *name, const QVariant &value); // 设置属性
inline bool setProperty(const char *name, QVariant &&value);
QVariant property(const char *name) const; // 读取属性
QList<QByteArray> dynamicPropertyNames() const;
QBindingStorage *bindingStorage() { return &d_ptr->bindingStorage; }
const QBindingStorage *bindingStorage() const { return &d_ptr->bindingStorage; }
......
通过分析QObject
类发现,之所以QObject
为对象系统的基类,是因为其提供了元对象系统很多支持工作:信号与槽、事件处理、属性设置、国际化支持(翻译)、对象树资源管理等等。
使用QObject
作为基类而不使用Q_OBJECT
宏和元对象代码是可以的,但是如果Q_OBJECT
宏没有被使用,那么这个类声明的信号和槽,以及其他特征描述都不会被调用。
一般建议在 QObject 的所有子类中使用 Q_OBJEC 宏,而不管它们是否使用了信号与槽。
二、信号与槽
GUI 用户界面中,当用户操作一个窗口部件时,需要其他窗口部件响应,传统方式经常使用 callback(回调机制)来实现。所谓回调即事先将函数指针作为参数传递另一个函数,然后在函数处理过程中适当地方调用回调函数回调机制有两个缺陷:类型不安全,不能保证调用函数过程中使用正确的参数;强耦合:处理函数必须知道调用哪个回调函数。Qt 的信号与槽机制是松耦合:类型安全的,更灵活,更方便。
信号与槽(Signal & Slot)是 Qt 编程的基础,也是 Qt 的一大创新。因为有了信号与槽的编程机制,在 Qt 中处理界面各个组件的交互操作时变得更加直观和简单。
信号(Signal)就是在特定情况下被发射的事件,例如 PushButton 最常见的信号就是鼠标单击时发射的
clicked()
信号。发射信号使用 Qt 的emit
关键字。Qt 的signals
关键字指出进入了信号声明区,随后即可声明自己的信号。槽(Slot)就是对信号相应的函数。槽就是一个函数,与一般的C++函数是一样的,可以声明在类的任何部分(public、private 或 protected),可以具有任何参数,也可以被直接调用。槽函数与一般的函数不同的是:槽函数可以与一个信号关联,当信号被发射时,关联的槽函数被自动执行。
Qt 的信号(
signals:
中的函数原型)在编译后并不是普通的函数,而是被moc
自动展开为一些内联函数与QMetaObject::activate()
的包装。当我们在代码中emit MySignal(...)
时,实际上是调用了一个由moc
生成的静态或内联函数,这个函数最终会调用QMetaObject::activate()
:void MyClass::MySignal(int value) { // 这段代码由 moc 自动生成,省略了细节 // ... QMetaObject::activate(this, &staticMetaObject, signal_index, ...) }
QMetaObject::activate()
会根据内部记录的连接(Connect)信息,依次调用与该信号相连的槽函数。在这个过程中,Qt 能够动态找到对应槽函数的地址,并将参数原样传递给槽函数,从而完成回调调用。
示例:
- 定义信号,如:
signals: void Comeon(QString& str); // 信号不能也无需实现
- 发射信号,如:
QString str = "信号发射";
emit Comeon(str);
- 声明三个槽方法,如:
private slots:
void ComeonA(QString& str);
void ComeonB(QString& str);
void ComeonC(QString& str);
- 槽方法中处理需要处理的工作,如:
void MainWindows::ComeonGuys(QString& str)
{
qDebug() << str; // 将str打印到输出控制台
}
注意:使用qDebug()
输出信息时注意添加头文件 #include <QDebug>
;
- 最后将信号与槽关联起来:
connect(this, SIGNAL(Comeon(QString&)), this, SLOT(ComeonGuys(QString&)));
// 也可以:
connect(this, &MainWindow::Comeon, this, &MainWindow::ComeonGuys);
GUI 程序设计的主要内容就是对界面上各组件的信号的响应,只需要知道什么情况下发射哪些信号,合理地去响应和处理这些信号就可以了。
信号与槽关联使用 QObject::connect()
函数实现:
static QMetaObject::Connection connect(
const QObject *sender, // 信号发送者
const char *signal, // 发送的信号
const QObject *receiver, // 信号接收者
const char *member, // 表示与信号连接方式的字符串,可以是槽或信号
Qt::ConnectionType = Qt::AutoConnection // 连接方式,默认为自动连接
);
常用格式:connect(sender, SIGNAL(signal()), receiver, SLOT(slot()));
用来表示信号和槽的参数都是字符串,Qt 提供了两个宏用于构造这样的字符串:对于信号使用
SIGNAL
,对于槽则使用SLOT
,用它们将函数的原型包围起来即可。注意connect
方法采用SIGNAL()
及SLOT()
时,这里的函数原型只能写出类型,不能有任何参数名,否则连接将会失败。
此外,信号的连接方式一般不填,使用默认的。具体的有:
-
Qt::AutoConnection
:(默认连接方式)自动方式,由系统自动选择连接方式。 -
Qt::DirectConnection
:直接方式,信号发射时,立即调用槽。 -
Qt::QueuedConnection
:队列方式,信号发射时产生一个事件进入队列,事件被处理时槽才能调用。 -
Qt::BlockQueuedConnection
:阻塞队列方式,信号发射时产生一个事件进入队列,然后当前线程进入阻塞状态,直到事件处理完毕,若接收方位于发送信号的线程中,则程序会死锁,故此连接方式仅用于多线程。
信号可以看做是特殊的函数,需要带括号,可带参数,信号无需实现也不能实现。槽函数需要带括号,有参数时还需要指明参数。当信号和槽函数带有参数时,在
connect()
函数里,要写明参数的类型。信号的参数需与槽的参数列表一致,允许比槽参数多**(槽函数的参数不能比信号函数的参数多)**。如果不匹配或参数过少,会出现编译错误或运行错误。在使用信号与槽的类中,必须在类的定义中加入宏
Q_OBJECT
。当一个信号被发射时,与其关联的槽函数通常被立即执行,就像正常调用一个函数一样。只有当信号关联的所有槽函数执行完毕后,才会执行发射信号处后面的代码。
信号与槽的关联方式有如下特点
信号、槽签名的匹配:
每个信号或槽在元对象系统中都有一个签名,包括函数名与参数类型列表。建立
connect()
时,Qt 会将信号与槽的签名进行匹配:
- 函数名要一致(例如
clicked()
对应clicked()
)。- 参数个数与类型必须兼容:如果信号参数是
(int, QString)
,槽至少要能接收(int, QString)
或更少的参数(只要前面类型都能匹配)。- 如果不匹配,老式写法(
SIGNAL()
/SLOT()
宏)会在运行时给出警告,新写法(函数指针)会在编译期直接报错,提供更好安全性。连接表(Connection Lists):
在对象内部,Qt 维护了一个 “连接表”,记录了该对象发出的每个信号分别连接到哪些槽(或信号)。当
emit
一个信号时,会根据该表逐个调用目标槽。
- 一个信号 -> 多个槽:连接表里该信号可能有多个条目。
- 多个信号 -> 一个槽:多个信号分别记录对应相同的槽地址。
- 信号 -> 信号:在底层也视作 “信号 -> 槽”,只不过被连接的 “槽” 是另一个信号的
moc
包装函数,最终会触发二次激活。
-
一个信号连接一个槽:
connect(sender, SIGNAL(single1()), receiver, SLOT(slotFun()));
如:
connect(this, SIGNAL(Comeon(QString&)), this, SLOT(ComeonGuys(QString&)));
// 也可以:
connect(this, &MainWindow::Comeon, this, &MainWindow::ComeonGuys);
-
一个信号连接一个信号:
connect(sender, SIGNAL(single1()), receiver, SIGNAL(single2()));
如:
connect(this, SIGNAL(sigSure(QString&)), this, SIGNAL(sigTest(QString&)));
connect(this, SIGNAL(sigTest(QString&)), this, SLOT(sigA(QString&)));
-
一个信号连接多个槽,关联信号的槽函数按照建立连接时的顺序依次执行:
connect(sender, SIGNAL(single1()), receiver1, SLOT(slotFun()));
connect(sender, SIGNAL(single1()), receiver2, SLOT(slotFun()));connect(sender, SIGNAL(single1()), receiver3, SLOT(slotFun()));
如:
connect(this, SIGNAL(sigSure(QString&)), this, SLOT(sigA(QString&)));
connect(this, SIGNAL(sigSure(QString&)), this, SLOT(sigB(QString&)));
-
多个信号连接一个槽:
connect(sender1, SIGNAL(single1()), receiver, SLOT(slotFun()));
connect(sender2, SIGNAL(single2()), receiver, SLOT(slotFun()));
connect(sender3, SIGNAL(single3()), receiver, SLOT(slotFun()));
如:
connect(this, SIGNAL(sigSure(QString&)), this, SLOT(sigA(QString&)));
connect(this, SIGNAL(sigCancel(QString&)), this, SLOT(sigA(QString&)));
-
信号与槽的自动关联:
ui_xxxx.h文件中
connectSlotsByName()
方法通过对象名支持信号与槽的自动关联。不采connect()
函数而是采用on_objectName_signal命名方式命名槽达到自动关联的效果。
如类头文件中声明槽方法:
private slots:
void on_OK_clicked();
void on_NO_clicked();
信号与槽的断开连接
QObject::disconnect(const QObject* sender,const char* signal,const QObject *receiver,const char* method);
- 断开与一个对象所有的信号的所有关联
disconnect(sender,0,0,0);
// 等价于:
sender->disconnect();
- 断开与一个指定信号的所有关联
disconnect(sender,0,0,0);
// 等价于:
sender->disconnect();
- 断开与一个指定接受者receiver的所有关联
disconnect(sender, 0, receiver, 0);
// 等价于:
sender->disconnect(SIGNAL(single1()));
- 断开指定信号与槽的关联:
disconnect(sender, SIGNAL(single1()), receiver, SLOT(slotFun()));
// 等价于:
disconnect(myConnection); //myConnection为connect()的返回值
如:
xxx.h文件中添加 m_res
变量用于保存 connect()
返回值。
private:
QMetaObject::Connection m_res;
连接信号与槽:
m_res = connect(this, SIGNAL(sigSure(QString&)), this, SLOT(sigA(QString&)));
断开该连接:
disconnect(m_res);
信号与槽机制的优越性:
- 信号与槽机制是类型安全的,相关联的信号与槽参数必须匹配
- 信号与槽是松耦合的,信号发送者不知道也不需知道接受者的信息。
- 信号与槽可以使用任意类型的任意数量的参数。
常见问题
为什么非要用 Q_OBJECT
宏?
- 没有
Q_OBJECT
宏,moc
不会生成辅助代码;即使你声明了signals
或slots
,也无法被 Qt 识别到并注册进元对象系统。
为什么说信号不需要实现?
- 因为
signals
中声明的函数会被moc
解析并创建“激活函数”来调用QMetaObject::activate()
。你自己写实现毫无意义,同时编译也会报错(moc
认为这是一个信号,不期望有用户定义的实现)。
信号可以直接当普通函数调用吗?
- 技术上,旧语法下
emit
是一个宏,实际上是空的,你也可以直接调用信号名,但这只会调用QMetaObject::activate()
;若你写了与信号同名的普通函数,反而会产生冲突或未定义行为。最佳实践是只用emit
触发信号。
效率如何?
- 在同一线程使用直接连接时,调用槽的开销相当于一次函数指针调用,多一层
moc
生成代码的跳转,通常可以忽略不计。 - 在多线程使用队列连接时,会多一步事件排队和线程切换,但这正是异步通信所需的成本,Qt 在这方面已经做了大量优化,性能通常足以满足 GUI 或多数通用场景。
三、动态属性系统
为什么需要 Q_PROPERTY
在标准 C++ 中,如果要让外部能够读写一个类的数据成员,通常会写成:
class MyClass {
private:
int m_value;
public:
void setValue(int val) { m_value = val; }
int value() const { return m_value; }
};
这样的封装可读可写。但要想在运行时对这个 “value” 属性做动态反射,比如 “列举对象有哪些属性”、“对属性进行 set/get 而无需知道类名或函数名”,C++ 自身是不支持的。
Qt 的元对象编译器(moc) 与 QMetaObject
联合使用,就能让这些属性出现在运行时的元对象信息里,并可以通过 QObject::property()
、QObject::setProperty()
等函数访问。这依赖于类中使用 Q_PROPERTY
宏,以及 Q_OBJECT
宏来启用 moc
对此类进行扫描、生成辅助代码。
Q_PROPERTY 宏做了什么
Q_PROPERTY()原型:
Q_PROPERTY(type name
(READ getFunction [WRITE setFunction] |
MEMBER memberName [(READ getFunction | WRITE setFunction)])
[RESET resetFunction]
[NOTIFY notifySignal]
[REVISION int | REVISION(int[, int])]
[DESIGNABLE bool]
[SCRIPTABLE bool]
[STORED bool]
[USER bool]
[BINDABLE bindableProperty]
[CONSTANT]
[FINAL]
[REQUIRED])
当 moc
处理这个头文件时,会将 Q_PROPERTY
中的这些信息提取到元数据表中。随后,运行时每个对象都会持有一个 QMetaObject
(元对象),其中包含:
- 该属性的名称(
name
) - 读函数的名称(
getFunction
),若有 - 写函数的名称(
setFunction
),若有 - 通知信号(
notifySignal
),若有 - 其他修饰符:
DESIGNABLE
,SCRIPTABLE
,STORED
,USER
等。
这些信息被
moc
放到一张静态数组或结构体中,最终编译进可执行文件。当调用QObject::property("属性名")
时,Qt 内部就会使用QMetaObject
找到 “读函数指针” 并进行一次动态调用,返回结果。调用setProperty("属性名", value)
时,则会找到 “写函数指针” 并进行动态调用,完成设置。
元对象与 QMetaProperty
在运行时,每个继承自 QObject
并含有 Q_OBJECT
宏的类,都有一个关联的 QMetaObject
。这个 QMetaObject
包含了:
- 类名
- 所有属性(Property)信息
- 所有信号与槽信息
- 枚举(Enum)信息
- 方法签名(函数)信息
对于属性而言,Qt 还提供 QMetaProperty
类来详细描述属性。我们可以通过:
const QMetaObject *metaObj = myObject->metaObject();
int count = metaObj->propertyCount();
for(int i = 0; i < count; ++i) {
QMetaProperty mp = metaObj->property(i);
qDebug() << mp.name() << mp.typeName();
}
来遍历这个对象拥有的全部属性名、类型名等,并可进一步做动态调用。这就是 Qt 属性反射的基础原理——所有信息都在 metaObject()
的元数据里。
示例:
- 新建桌面应用程序TestProperty,父类QWidget,其他采用默认。
- 右键单击项目添加自定义类MyPropertyClass,父类QObject.
- mypropertyclass.h文件中Q_OBJECT下方声明属性宏:
class mypropertyclass : public QObject
{
Q_OBJECT
Q_PROPERTY(type mask READ mask WRITE setMask NOTIFY maskChanged FINAL)
- READ: 指定属性读取函数,如
READ getFunction
;若没有提供READ
,也可用MEMBER myMember
指定直接和某个成员变量绑定,这样就无需专门写 getter 函数。- WRITE: 指定属性写入函数,如
WRITE setFunction
。- MEMBER: 指向类的一个成员变量,用于自动生成读写逻辑;如果同一个属性同时指定了
READ
/WRITE
与MEMBER
,则READ
/WRITE
优先级更高。- NOTIFY: 关联一个信号,例如
NOTIFY maskChanged
,这个信号通常在属性值变更时触发,既能让 QtQuick(QML) 进行属性绑定更新,也能让 C++ 层面监听属性变化。如果你仅仅声明了一个
Q_PROPERTY
而没有写NOTIFY maskChanged
,则在 QML 或者其它绑定场景下可能不知道何时属性发生改变。要想自动刷新界面,需要借助通知信号来触发更新。
- 声明属性读取、设置函数,定义属性变更时发送的信号,定义成员变量m_mask保存属性值。
public:
explicit mypropertyclass(QObject *parent = nullptr);
QString mask() const; // 读取属性
void setMask(QString strMaskNum); //设置属性
signals:
void maskChanged(QString str); // 属性变更时发送的信号
private:
QString m_mask; // 保存属性流
- widget.h添加槽函数声明
private slots:
void setChanged(QString str); // 响应属性变更信号
widget.cpp添加槽方法定义:
void Dialog::setChanged(QString str)
{
qDebug() << "测试信号" << str;
}
- Widget类构造函数中添加如下代码
mypropertyclass* mypC = new mypropertyclass;
connect(mypC, SIGNAL(maskChanged(QString)), this, SLOT(setChanged(QString)));
mypC->setMask("哔哔~"); // 属性WRITE写操作
qDebug() << "当前信号:" << mypC->mask(); // 属性READ读操作
QObject* obj = mypC;
qDebug() << "obj进行属性读取:" << obj->property("mask").toString();
qDebug() << "obj进行第二次属性读取:" << obj->property("mask").toString();
动态操作属性:property() 与 setProperty()
property() 机制
当我们调用:
QObject *obj = myPropertyObject;
QVariant val = obj->property("mask");
时,Qt 会先查找 obj->metaObject()
里的所有属性列表,找到名字为 “mask” 的 QMetaProperty
,然后再调用它的 “读函数指针” 或 “MEMBER 直接访问” 来获取值。这就是动态调用,无需知道类的名称、头文件、也不需要包含任何声明。
setProperty() 机制
类似地:
obj->setProperty("mask", "哔哔~");
Qt 会查找到 “mask” 属性的写函数指针或 MEMBER
写入功能,然后调用之/写入之。当写操作完成后,如果该属性声明了 NOTIFY
信号,Qt 会自动发射通知信号(前提是你在写函数中或者 MEMBER 改变时手动 emit maskChanged(...)
),以便让界面或其他模块获知这一变动。
何时触发 NOTIFY 信号
- 如果是使用 READ/WRITE:在
setMask()
函数里,你要手动检测值是否改变,然后emit maskChanged(newVal)
。这样 Qt 才能在外部监听到属性变更。 - 如果是使用 MEMBER 绑定并指定了
NOTIFY
:当你使用setProperty()
或者直接给这个成员赋值时,需要在合适时机手动emit maskChanged(m_mask)
。MEMBER 并不会自动生成更新检测逻辑,需要你自己在写代码时处理值变化。 - 如果属性没标注
NOTIFY
信号,则外部不知道何时发生变化(C++ 层面可以用property()
主动轮询,QML 层面没法自动刷新)。
Qt 对象树
父子关系与对象链表
当我们创建一个继承自 QObject
的对象时,如果在构造函数中传入了一个父对象(parent
),那么 Qt 会将该子对象加入到父对象内部维护的对象链表(QObjectList
)中。这样,父对象就会在析构时自动析构其所有子对象,开发者无需手动对每个子对象进行 delete
操作。
内存安全
- 自动析构:父对象析构时会自动销毁所有子对象,避免内存泄漏。
- 避免二次删除:Qt 保证不会重复
delete
同一个对象。如果你需要手动销毁一个对象,建议使用deleteLater()
而不是delete
,因为deleteLater()
可以在事件循环空闲时安排删除操作,多次调用也是安全的,而直接delete
多次则会导致不安全行为(崩溃或不可预期错误)。
层级结构
- 通过
children()
函数可以获取父对象所拥有的所有子对象列表。 - 如果一个对象有子对象,这些子对象又可以有自己的子对象,形成树形结构。
children()
只会返回 当前层级 的所有直接子对象,而不包含更深层级的孙子对象。
Qt的对象树机制是其内存管理的核心特性,下面为您详细解析并提供代码示例:
#include <QCoreApplication>
#include <QObject>
#include <QDebug>
class MyObject : public QObject
{
Q_OBJECT
public:
// 构造函数中可接受一个父对象
explicit MyObject(const QString &name, QObject *parent = nullptr)
: QObject(parent), m_name(name)
{
qDebug() << "Construct MyObject:" << m_name;
}
~MyObject() override
{
qDebug() << "Destruct MyObject:" << m_name;
}
private:
QString m_name;
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// 创建一个父对象
MyObject *parentObj = new MyObject("Parent");
// 创建子对象,并指定父对象
MyObject *childObj1 = new MyObject("Child1", parentObj);
MyObject *childObj2 = new MyObject("Child2", parentObj);
// 查看父对象的子对象列表
QObjectList childrenList = parentObj->children();
qDebug() << "Number of children:" << childrenList.size(); // 这里会输出 2
// 打印子对象指针(仅用于演示)
for (QObject *obj : childrenList) {
qDebug() << " -> Child object:" << obj;
}
// 手动释放资源的方式1:deleteLater()
// 只要调用一次就行,多次调用也不会报错,Qt 会在事件循环空闲时自动删除。
// childObj1->deleteLater();
// childObj2->deleteLater();
// 手动释放资源的方式2:直接 delete 父对象
// 父对象析构会自动析构所有子对象。此时不需要对 childObj1 和 childObj2 再做处理。
delete parentObj;
// 至此,Child1 和 Child2 也自动被销毁了
// 不建议同时对 childObj1, childObj2 再执行 delete 或 deleteLater
return 0;
}
窗口部件
一、默认部件基类
在 Qt 中,QMainWindow
、QDialog
和 QWidget
都继承自 QWidget
,并且它们的设计目的是为了满足不同类型的用户界面需求。
QWidget 类
QWidget
类堪称所有用户界面控件的根基,代表着各类窗口部件。它具备多方面关键能力:在事件处理层面,能够敏锐接收并妥善响应鼠标点击、键盘输入及其他各类事件;于绘制机制而言,承担着将控件外观精准绘制在屏幕上的重任;在布局管理方面,大力支持运用如QVBoxLayout
、QHBoxLayout
等布局管理器,实现对控件排列的高效把控。作为高度灵活的基类,QWidget
既可以作为任意类型窗口的父类,又能在用户界面中无缝嵌入其他QWidget
或派生控件,为构建复杂多样的用户界面提供了坚实基础。QMainWindow 类
QMainWindow
由QWidget
派生而来,为应用程序的主窗口搭建起标准架构。其内部包含多个重要组成部分:菜单栏,用于罗列应用程序的各项菜单选项,方便用户进行功能选择;工具栏,通常放置常用工具按钮,为用户提供便捷操作途径;状态栏,主要用于展示应用程序当前状态或提供相关帮助信息;中央区域则是应用程序主要内容的展示核心,借助setCentralWidget()
方法可轻松设置。在设计主窗口时,QMainWindow
凭借丰富的功能组件,成为构建复杂应用程序界面的不二之选,特别是对于那些依赖菜单、工具栏、状态栏协同工作的应用场景。QDialog 类
同样继承自QWidget
的QDialog
,专注于实现对话框窗口。对话框作为与用户交互的临时性窗口,广泛应用于诸多场景,像提示框、确认框这类用于简单信息提示与确认的窗口,以及登录窗口、设置窗口等需要用户输入特定信息的窗口。与QMainWindow
不同,QDialog
主要聚焦于完成短期任务,一旦任务结束,通常会自动关闭,以提升用户交互的流畅性与便捷性。基于 QWidget 创建控件的策略
- QMainWindow:适用于需要完整主窗口架构(如菜单栏、工具栏、状态栏等)的应用。
- QDialog:适用于临时弹出的窗口(如设置窗口、文件选择窗口等)。
- QWidget:适用于没有复杂窗口结构的控件,可以作为顶级窗口或嵌入其他控件中。
- QFrame、QStackedWidget 等其他派生类:根据具体需求来选择,
QFrame
可以提供可定制的边框效果,QStackedWidget
可以管理多个页面。
QWidget

QWidget
的构造函数有两个参数:
- QWidget *parent = 0:
parent
参数指定当前窗口部件的父窗口部件。如果为0
,表示当前窗口没有父窗口部件,它是一个顶级窗口。如果指定了parent
,则当前窗口部件是该父窗口的子部件。- 顶级窗口部件通常是应用程序的主窗口,而非顶级窗口部件则会被嵌入到父窗口部件中作为子部件显示。
- 如果没有父窗口,窗口会被系统视为独立的窗口;有父窗口时,窗口则会被视为嵌入在父窗口中的一个子窗口。
- Qt::WindowFlags f = 0:
f
参数是用来设置窗口的属性,类型是Qt::WindowFlags
,它是Qt::WindowType
枚举值的组合。Qt::WindowFlags
定义了窗口的行为和外观属性,比如是否可调整大小、是否为弹出窗口等。常见的窗口类型包括:Qt::Widget
:默认值,表示普通窗口。Qt::Dialog
:对话框窗口。Qt::FramelessWindowHint
:没有边框的窗口。Qt::WindowStaysOnTopHint
:窗口总是显示在其他窗口之上。
- 通过组合这些标志,用户可以定制窗口的各种行为。
- 顶级窗口:没有父窗口的窗口部件会被认为是顶级窗口部件。顶级窗口通常是整个应用程序的主窗口,如
QMainWindow
或QWidget
。 - 子窗口:如果指定了
parent
窗口部件,那么当前窗口部件就是该父窗口的子窗口部件。子窗口通常会在父窗口内作为嵌入的部分显示。
setWindowState()
方法用于设置窗口的状态。它接受一个参数,这个参数是Qt::WindowStates
枚举值的组合,表示窗口的不同状态。常见的窗口状态包括:
窗体状态 | 作用 |
---|---|
Qt::WindowNoState | 窗体为正常状态 |
Qt::WindowMinimized | 窗体最小化 |
Qt::WindowMaximized | 窗体最大化 |
Qt::WindowFullScreen | 窗体全屏显示 |
Qt::WindowActive | 窗体为活动窗体 |
QWidget设计模式属性设置
-
enabled:启用或禁用 widget,默认启用。
-
geometry:控件的位置和尺寸,表示为
(x, y, 宽度, 高度)
。 -
sizePolicy:设置widget在水平和垂直方向的伸缩策略以及伸缩因子(Stretch Factors),所谓伸缩策略实际就是widget对待部件大小提示的策略,需结合布局管理器一起使用。伸缩策略可通过调用setSizePolicy()方法设置,大小提示可通过sizeHint()函数返回值获取,也可重载sizeHint()方法进行重置。
拉伸因子描述了各个部件在进行拉伸时,部件间以指定的比例进行拉伸,如水平布局的三个按钮其拉伸因子分别设为1,2,3则表示该3个按钮将会以1:2:3的比例进行拉伸。
注意:1.当主窗口的大小不能按计算出来的比例容纳下所有子部件时,子部件不一定会按设计好的比例进行排列。
大小策略与拉伸因子之间的关系:
若部件的拉伸因子大于0,则按照拉伸因子的比例分配空间;若拉伸因子为0,则只有在其他部件不需要空间时才会获得空间;也就是说若一些部件拉伸因子大于0,而一些部件拉伸因子为0,则只有拉伸因子大于0的部件会被拉伸,而拉伸因子为0的部件不会被拉伸。若所有部件的拉伸因子都为0,则按照大小策略的规则对部件进行拉伸。注意:2.若部件的大小策略为Fixed,则即使设置了拉伸因子,该部件也不会被拉伸。故拉伸因子会使大小策略不起作用或失效(除了Fixed策略外)
属性值 | 作用 |
---|---|
Fixed | 尺寸不能改变,尺寸为sizeHint大小 |
Minimum | 尺寸可以拉伸,尺寸可变范围:≥sizeHint |
Maximum | 尺寸可以缩小,尺寸可变范围:minimumSizeHint ~ sizeHint |
Preferred | 可以变大缩小,尺寸可变范围:≥minimumSizeHint |
Expanding | 可以变大缩小,尺寸可变范围:≥minimumSizeHint,且部件有优先扩展权(注:优先扩展权表部件将尽可能多的占用空间,如Preferred与Expanding同时存在则优先分配空间给Expanding) |
MinimumExpanding | 尺寸可以拉伸,尺寸可变范围:≥minimumSizeHint,且部件有优先扩展权 |
Ignored | 任意变大缩小,尺寸可变范围:≥minimumSizeHint(若minimumSizeHint为0,则可缩小至0,此时部件不可见) |
- minimumSize:控件的最小尺寸,调整时不可缩小到此尺寸以下。
- maximumSize:控件的最大尺寸,调整时不可增大到此尺寸以上。
- palette:设置控件的调色板,控制控件的基本样式,如背景色、前景色等。
- mouseTracking:控件是否启用鼠标跟踪。启用时,控件可以接收鼠标移动事件,即使鼠标不在控件范围内。
- tableTracking:控件是否启用平板或手机的触摸屏追踪。
- focusPolicy:焦点策略,按钮可以通过NoFocus使虚线消失,lineedit这类文本编辑框必须能获得焦点。
属性值 | 作用 |
---|---|
NoFocus | 无法通过点击和键盘获得焦点 |
TabFocus | 鼠标无法获得焦点,Tab键获得焦点 |
ClickFocus | 鼠标点击获得焦点,Tab键无法获得焦点 |
StrongFocus | 鼠标和Tab键都可以获得焦点 |
WheelFocus | 通过滚轮获得焦点 |
- contextMenuPolicy:上下文菜单策略,指定菜单的显示方式。
属性值 | 作用 |
---|---|
NoContextMenu | 部件无菜单,菜单处理延迟到部件父亲 |
PreventContextMenu | 部件无菜单,菜单处理不延迟到父亲,而是传递到本身。 |
DefaultContextMenu | 调用部件的contextMenuEvent方法,默认处理方式为忽略上下文事件。 |
ActionsContextMenu | 部件菜单由 actions定义构成 |
CustomContextMenu | 部件菜单自定义,发送customContextMenuRequested信号 |
-
acceptDrops:设置部件是否接受拖拽事件,默认启用。
-
toolTip:设置部件的提示,鼠标悬浮时会显示。
-
toolTipDuration:设置widgettoolTip的显示持续时间,毫秒为单位,默认为-1,会一直显示。
-
statusTip:设置部件的状态提示,当窗口有statusBar时会显示在上面。
-
whatsThis:显示帮助信息,通常通过点击 “What’s This?” 按钮来获取相关信息。
-
accessibleName:辅助功能客户端应用程序所使用的控件名称。
-
accessibleDescription:主要用来为视力较差或盲人用户,提供更大的上下文,也可以使用上下文搜索或其他应用程序。。
-
layoutDirectionwidget:布局的方向,LeftToRight,RightToLeft,LayoutDirectionAuto,字面意思。
-
autoFillBackground:设置widget背景是否被画板颜色自动填充,默认不勾选。
-
styleSheet:设置控件的样式表(QSS),可以自定义控件的外观。
-
locale:设置widget的区域和语言,在将时间等信息转成字符串后,年月日几个字会显示不同语言。
-
inputMethodHint:设置widget输入时的屏幕键盘模式,有纯数字、纯字母等多种模式,用在手机端。
QDialog
QDialog
是各种对话框的基类,继承自 QWidget
。对话框有两种表现形式:模态对话框 和 非模态对话框。
模态对话框
模态对话框会阻塞应用程序中其他窗口的输入,直到当前对话框关闭,用户才能操作其它窗口。模态对话框有自己的本地事件循环。通过调用 exec()
方法,可以使对话框以模态方式运行。当对话框关闭时,exec()
返回一个值,并继续执行调用 exec()
之后的代码。
通常,将默认按钮(如 “OK”)连接到 accept()
槽,将 “Cancel” 按钮连接到 reject()
槽,从而关闭对话框并返回相应的值。也可以使用 done()
槽,传递 Accepted
或 Rejected
参数。
- 使用
exec()
方法:
QDialog dlg;
dlg.exec(); // 启动模态对话框
- 使用
setModal()
方法:
QDialog dlg;
dlg.setModal(true); // 设置为模态
dlg.show(); // 显示对话框
- 使用
setWindowModality()
设置窗口模态:
QWidget widget;
widget.setWindowModality(Qt::ApplicationModal); // 设置为应用级模态窗口
widget.show(); // 显示窗口
阻塞方式 | 阻塞效果 |
---|---|
Qt::ApplicationModal | 阻塞应用程序的所有窗口 |
Qt::WindowModal | 阻塞阻塞父窗口、祖先窗口及它们的子窗口 |
Qt::NoModal | 不阻塞,默认值 |
其他常用部件
1.QLabel - 用于显示文本、数字、图片和 GIF 动图
QLabel
是一个常用的控件,用于显示文本、数字、图片和 GIF 动图。它是 UI 中的静态显示元素。
- 显示文本:可以设置静态文本。
- 显示图片:可以用
setPixmap()
设置显示图片。 - 显示 GIF 动图:通过
setMovie()
设置 GIF 动画。
QLabel *label = new QLabel(this); // 创建 QLabel 对象
label->setText("Hello, World!"); // 设置文本
label->setPixmap(QPixmap("image.png")); // 显示图片
label->setMovie(new QMovie("animation.gif")); // 显示 GIF 动画
label->show();
与其他控件的伙伴关系:QLabel
可以与其他控件建立伙伴关系,用于设置快捷键。通过设置伙伴关系,您可以让标签成为其他控件(如按钮)的快捷键触发器。按下快捷键时,伙伴控件会响应(例如按钮被按下)。
QPushButton *button = new QPushButton("Click Me", this);
QLabel *label = new QLabel("Press Alt+P", this);
label->setBuddy(button); // 设置按钮为标签的伙伴
此时,当用户按下 Alt+P
快捷键时,按钮将被点击。
另外再设计模式下是伙伴关系的操作是这样的:
设计模式属性设置:
- frameShape:定义 QFrame 的框架形状,不同取值呈现各异的外观风格:
取值 | 说明 |
---|---|
NoFrame | 无框架,QFrame 如同普通容器,无边界装饰。 |
Box | 显示矩形盒子状边框,轮廓清晰。 |
Panel | 类似面板,通过边框营造内容区域的凹陷或凸起感。 |
HLine/VLine | 水平或垂直线,常用于分隔界面区域。 |
StyledPanel | 依当前样式(如 QSS)绘制框架,适配界面风格。 |
WinPanel | 模拟 Windows 风格的面板框架效果。 |
- frameShadow:控制框架阴影效果,影响立体感
取值 | 说明 |
---|---|
Plain | 无阴影,边框线条平实,无立体凹凸。 |
Raised | 框架凸起,从背景 “隆起”,层次感强。 |
Sunken | 框架凹陷,似嵌入背景,视觉层次不同。 |
-
lineWidth:设定框架边框宽度(像素单位)。值越大,边框越粗。例如
1
为细边框,5
则明显加粗,直接改变边框视觉粗细。 -
midLineWidth:用于特定框架形状(如
StyledPanel
)的中间线宽度。当框架含复杂线条结构时,调整此值改变中间分隔线粗细。若为0
,可能不显示中间线或按默认处理。 -
text:定义 QLabel 显示的文本内容,通过设置此属性确定标签展示的文字信息。
-
textFormat:规定文本显示格式。
取值 | 说明 |
---|---|
AutoText | 自动识别普通文本或富文本并显示 |
RichText | 按富文本(如带格式、超链接等)显示 |
PlainText | 仅显示无格式的普通文本 |
- pixmap:用于设置 QLabel 展示的像素图(如图片),使标签具备图像显示能力。
- scaledContents:控制内容是否缩放适配标签大小。
true
:像素图或内容缩放以适应 QLabel 尺寸;false
:内容按原始尺寸显示,超出部分可能隐藏。
- alignment:决定文本对齐方式,支持多种组合(如左对齐 + 垂直居中、右对齐 + 顶部对齐等),满足不同布局的排版需求。
- wordWrap:设置文本是否自动换行。启用后,文本超出标签宽度时自动换行显示。
- margin:指定文本与 QLabel 边框的空白距离,单位为像素,默认
0
表示无边距。 - indent:定义文本缩进量(像素单位)。值为
-1
时,遵循默认缩进规则。 - openExternalLinks:控制是否允许点击文本中的外部链接(如超链接)。启用后,点击链接可执行跳转操作。
- textInteractionFlags:定义文本交互行为。例如
LinksAccessibleByMouse
表示鼠标可点击访问链接,还可组合其他标志(如键盘交互),灵活控制文本交互方式。 - buddy:设置 QLabel 的伙伴控件,常将标签与输入类控件(如文本框)关联,使标签快捷键能聚焦到对应控件,提升交互便捷性。
2.QLineEdit - 用于接收用户输入
QLineEdit
是一个常用的输入框控件,用于接收用户输入的文本。
显示模式:QLineEdit
提供了几种不同的显示模式,允许对用户输入进行格式化显示。
显示模式 | 效果 |
---|---|
QLineEdit::Normal | 正常显示输入的字符 |
QLineEdit::NoEcho | 输入的字符不显示(用于密码等隐私信息) |
QLineEdit::Password | 输入的字符显示为星号(* ),常用于密码输入框 |
QLineEdit::PasswordEchoOnEdit | 编辑时正常显示,其他时候显示为星号(* ) |
QLineEdit *lineEdit = new QLineEdit(this);
lineEdit->setEchoMode(QLineEdit::Password); // 设置为密码输入框
输入掩码:QLineEdit
支持掩码功能,限制用户输入的内容。掩码中的字符定义了可接受的输入类型。
QLineEdit *lineEdit = new QLineEdit(this);
lineEdit->setInputMask("A9-99A"); // 输入格式:字母+数字-数字+字母
字符(必须输入) | 字符(可留空) | 含义 |
---|---|---|
A | a | 只能输入A-Z,a-z |
N | n | 只能输入A-Z,a-z,0~9 |
X | x | 可以输入任意字符 |
9 | 0 | 只能输入0-9 |
D | d | 只能输入1-9 |
# | 只能输入+,-,0-9 | |
H | h | 只能输入十六进制字符(0-9,a-f,A-F) |
B | b | 只能输入二进制字符(0,1) |
> | 后面的字母字符自动转换为大写 | |
< | 后面的字母字符自动转换为小写 | |
! | 停止字母字符的大小写转换 | |
\ | 将该表中的特殊字符正常显示用作分隔符 |
设计模式属性设置:
-
inputMask:设置输入掩码,强制用户按指定格式输入内容,例如约束输入 IP 地址、电话号码等固定格式数据。
-
text:获取或设置 QLineEdit 编辑框内的文本内容,即用户输入的文字或程序预设的文字信息。
-
maxLength:限制输入文本的最大字符数,默认值为
32767
,超出此长度后无法继续输入。 -
frame:控制 QLineEdit 是否显示边框。
-
echoMode:定义文本显示模式,适配不同场景,如下表:
取值 | 说明 |
---|---|
Normal | 正常显示输入字符,用于普通文本输入。 |
NoEcho | 完全隐藏输入内容,用于极高隐私场景(如密码输入但不显示任何符号)。 |
Password | 用占位符号(如圆点• )替代真实字符,常见于密码输入框。 |
PasswordEchoOnEdit | 编辑时显示字符,停止编辑后隐藏,平衡输入确认与隐私需求。 |
-
cursorPosition:获取或设置光标在文本中的位置(以字符索引表示),如
0
表示光标在文本开头。 -
alignment:设置文本在编辑框内的对齐方式(如 “左对齐,垂直中心对齐”),确保文本排版效果。
-
dragEnabled:允许用户拖拽选中文本,默认不勾选。
-
readOnly:编辑框为只读状态,用户无法修改内容,默认不勾选。
-
placeholderText:设置占位提示文本,编辑框无内容时显示,引导用户输入(如 “请输入邮箱”)。
-
cursorMoveStyle:定义光标移动逻辑风格,如
LogicalMoveStyle
表示按文本逻辑位置移动,影响光标操作行为。 -
clearButtonEnabled:编辑框右侧显示清除按钮,点击可一键清空内容,默认不勾选(隐藏清除按钮)。
3.QPushButton - 用于按钮控件
QPushButton
是一个常用的按钮控件,通常用于触发用户的操作或事件。
- 显示文字或图标:可以设置按钮的文本或图标。
- 连接信号和槽:当按钮被点击时,可以触发特定的操作。
代码示例:
QPushButton *button = new QPushButton("Click Me", this);
connect(button, &QPushButton::clicked, this, &Widget::onButtonClicked);
button->show();
设计模式属性设置:
-
text:设置按钮上显示的文本内容,用于定义按钮的标识文字,如 “确定”“提交” 等。
-
icon:为按钮添加图标,通过加载图片资源,使按钮具备可视化图标提示功能。
-
iconSize:规定图标显示尺寸,格式为 “宽 × 高”(如
20×20
),控制图标在按钮上的大小。 -
shortcut:配置按钮的快捷键,用户可通过键盘快捷键触发按钮功能,提升操作便捷性。
-
checkable:按钮支持勾选,具备选中 / 未选中两种状态。默认,按钮为普通点击模式,无勾选功能。
-
checked:按钮处于勾选状态(需配合
checkable=true
),默认,处于未勾选。 -
autoRepeat:按住按钮不放时,自动重复触发点击信号,默认,关闭。
-
autoExclusive:
true
:QRadioButton 默认启用自动独占特性,在同一组单选按钮中,确保仅有一个按钮可被选中,实现标准的单选互斥效果;false
:关闭自动独占特性,可能出现多个单选按钮同时选中的情况(不符合常规单选逻辑,实际开发中极少使用)。
-
autoRepeatDelay:设置自动重复触发的延迟时间(单位:毫秒),默认
300
,即按住按钮后延迟指定时间开始重复触发。 -
autoRepeatInterval:定义自动重复触发的时间间隔(单位:毫秒),默认
100
,控制每次重复触发的间隔时长。 -
autoDefault:默认,按钮自动成为默认按钮(如对话框中按回车可触发)。取消勾选则,无默认按钮特性。
-
default:将按钮设为默认按钮(需对话框等环境支持默认按钮机制)。默认,非默认按钮。
-
flat:按钮以扁平风格显示,隐藏立体效果。默认,按钮显示默认的立体样式。
4.QRadioButton - 单选按钮
QRadioButton
是单选按钮类,它通常用于提供多个选项,但只能选择其中一个。单选按钮通常被放置在一个分组框中,保证同一组中只有一个按钮可以被选中。
组内排他性:在同一组内,只能选择一个选项,其他选项会被自动取消选择。
QRadioButton *radioButton1 = new QRadioButton("Option 1", this);
QRadioButton *radioButton2 = new QRadioButton("Option 2", this);
radioButton1->setChecked(true); // 默认选中第一个选项
设计模式属性设置几乎与 QPushButton 相同。
5.QCheckBox - 多选按钮
QCheckBox
是多选按钮类,用户可以选择多个选项。每个复选框的选中状态是独立的。
- 多选功能:用户可以选择多个选项,而不像单选按钮那样具有排他性。
QCheckBox *checkBox1 = new QCheckBox("Option 1", this);
QCheckBox *checkBox2 = new QCheckBox("Option 2", this);
checkBox1->setChecked(true); // 选中第一个选项
- tristate:启用三态,支持选中、未选中、中间不确定状态。默认,仅两种状态(选中 / 未选中)。
QCheckBox checkBox;
// 启用三态,支持中间状态
checkBox.setTristate(true);
// 设置不同状态并获取判断
checkBox.setCheckState(Qt::Checked); // 设置为选中状态
if (checkBox.checkState() == Qt::Checked) {
qDebug() << "当前状态:Checked(选中)";
}
checkBox.setCheckState(Qt::Unchecked); // 设置为未选中状态
if (checkBox.checkState() == Qt::Unchecked) {
qDebug() << "当前状态:Unchecked(未选中)";
}
checkBox.setCheckState(Qt::PartiallyChecked); // 设置为中间状态,只有设置了tristate才会出现
if (checkBox.checkState() == Qt::PartiallyChecked) {
qDebug() << "当前状态:PartiallyChecked(中间状态)";
}