C++ QT 简介
介绍
安装社区版本,多种下载方式
常用的快捷键
使用Clion 开发QT
https://zhuanlan.zhihu.com/p/461896034
信号槽
- 信号槽是 Qt 框架引以为豪的机制之一。熟练使用和理解信号槽,能够设计出解耦的非常漂亮的程序,有利于增强我们的技术设计能力。
- 所谓信号槽,实际就是观察者模式。当某个事件发生之后,比如,按钮检测到自己被点击了一下,它就会发出一个信号(signal)。这种发出是没有目的的,类似广播。如果有对象对这个信号感兴趣,它就会使用连接(connect)函数,意思是,用自己的一个函数(成为槽(slot))来处理这个信号。也就是说,当信号发出时,被连接的槽函数会自动被回调。这就类似观察者模式:当发生了感兴趣的事件,某一个操作就会被自动触发。(这里提一句,Qt 的信号槽使用了额外的处理来实现,并不是 GoF 经典的观察者模式的实现方式。)
connect(sender,SIGNAL(signal),receiver,SLOT(slot),Qt::DirectConnection);
sender :发送信号指针(对象)
SIGNAL: 信号(函数)
signals:
//发射信号,不需要实现
void ageChanged(unsigned value);
receiver: 接收指针(对象)
SLOT(slot): 槽(执行函数)
QT::DirectConnection :链接方式,默认:QT::DirectConnection
Qt::DirectConnection参数 参数含义
Qt::AutoConnection 默认值,使用这个值则连接类型会在信号发送时决定。如果接收者和发送者在同一个线程,则自动使用Qt::DirectConnection类型。如果接收者和发送者不在一个线程,则自动使用Qt::QueuedConnection类型。
Qt::DirectConnection 槽函数会在信号发送的时候直接被调用,槽函数运行于信号发送者所在线程。效果看上去就像是直接在信号发送位置调用了槽函数。这个在多线程环境下比较危险,可能会造成奔溃。
Qt::QueuedConnection 槽函数在控制回到接收者所在线程的事件循环时被调用,槽函数运行于信号接收者所在线程。发送信号之后,槽函数不会立刻被调用,等到接收者的当前函数执行完,进入事件循环之后,槽函数才会被调用。多线程环境下一般用这个。
Qt::BlockingQueuedConnection 槽函数的调用时机与Qt::QueuedConnection一致,不过发送完信号后发送者所在线程会阻塞,直到槽函数运行完。接收者和发送者绝对不能在一个线程,否则程序会死锁。在多线程间需要同步的场合可能需要这个。
Qt::UniqueConnection 这个flag可以通过按位或(|)与以上四个结合在一起使用。当这个flag设置时,当某个信号和槽已经连接时,再进行重复的连接就会失败。也就是避免了重复连接。
5种信号槽实现方式
元对象系统
QObject *obj = new QPushButton;
obj->metaObject()->className(); //返回类名称:"QPushButton"
QTimer *timer = new QTimer; // QTimer 是一个QObject的子类
timer->inherits("QTimer"); // timer是不是一个QTimer类或者QTimer指针, true
timer->inherits("QObject");// true
timer->inherits("QAbstrctButton"); //false
属性系统
定义属性
Q_PROPERTY(type name READ name WRITE setname NOTIFY nameChanged)
Q_PROPERTY(bool enabled,READ isEnabled WRITE setenabled NOTIFY enabledChanged)
type 类型 C++的基础类型
READ 读取属性的函数
WRITE 设置属性的函数
NOTIFY 通知函数(属性改变了会调用这个函数)
动态设置属性
bool setProperty(const char *name, const QVariant &value);
QObject obj;
obj.setProperty("flat",true);
类的附加信息
Q_CLASSINFO("AUTHOR","WANGLAOJU")
Q_CLASSINFO("AUTHOR","WANGLAOJU")
Q_CLASSINFO("AUTHOR","WANGLAOJU")
Q_CLASSINFO("AUTHOR","WANGLAOJU")
//获取属性
this->metaObject()->classInfo(2).name();
this->metaObject()->classInfo(2).value();
QtGolbal 全局定义
forever{
//无限循环
}
Qt容器类
QList<QString> asList;
asList.append("Moday");
asList.append("TuesDay");
asList.append("Wdnesday");
QString str = asList[0]; // "Moday"
qDebug() << str;
QList<QString> list;
list << "noe" << "two" << "three";
QString str1 = list[1]; // "two"
QString str0 = list.at(0); // one
qDebug()<< "str1 = " << str1 << ",str0 = " << str0;
//QLinkedList 和 QVector 类似QList函数接口
//QVector 访问性能更高,QLinkedList添加删除效率更高
QStack<int> stack ;
stack.push(1231);
stack.push(1);
stack.push_front(23);
qDebug()<< stack.pop() << "->" << stack.pop() << "->" << stack.pop();
QQueue<int> queue;
queue.enqueue(10);
queue.enqueue(20);
queue.enqueue(30);
while(!queue.isEmpty()){
qDebug()<<queue.dequeue();
}
QSet<QString> set;
set << "dgo" << "cat" << "tiger";
if(set.contains("cat")){
qDebug()<< "the set hash cat ";
}
QMap<QString,int> map; // key不可重复
map["1"] = 1;
map["2"] = 2;
map["3"] = 3;
qDebug()<< map["1"] <<map.value("2") << map.value("4",40);
QMultiMap<QString,int> mutableMap; // key value 可以重复
mutableMap.insert("1",2);
mutableMap.insert("1",20);
mutableMap.insert("1",230);
mutableMap.insert("1",22300);
qDebug() << "mutableMap.value('1') == " << mutableMap.value("1");
foreach (int i, mutableMap) {
qDebug() << "mutableMap=>" << i;
}
容器迭代
容器 只读遍历器 | 读写遍历器 |
---|---|
QList,QQueue | QListIterator QMutableListIterator |
QLinkedList | QLinkedListIterator QMutableLinkedListIterator |
QVector,QStack | QVectorIterator QMutableVectorIterator |
QSet QSetIterator | QMutableSetIterator |
QMap<key, t="">,QMultiMap<key, t=""> | QMapIterator QMutableMapIterator |
QHash<key, t="">,QMultiHash<key, t=""> | QHashIterator QMutableHashIterator |
容器 | 只读遍历器 | 读写遍历器 |
---|---|---|
QList,QQueue | QList::const_iterator | QList::iterator |
QLinkedList | QLinkedList::const_iterator | QLinkedList::iterator |
QVector,QStack | QVector::const_iterator | QVector::iterator |
QSet | QSet::const_iterator | QSet::iterator |
QMap<key, t="">,QMultiMap<key, t=""> | QMap<key, t="">::const_iterator | QMap<key, t="">::iterator |
QHash<key, t="">,QMultiHash<key, t=""> | QHash<key, t="">::const_iterator | QHash<key, t="">::iterator |
Qt核心模块
名称 | 功能 |
---|---|
Qt Core | 非图形相关的基础类。 |
Qt GUI | 图形界面相关的基础类。 |
Qt Multimedia | 用于支持音视频、摄像头功能的类。 |
Qt Multimedia Widgets | 用于支持多媒体的图形类。 |
Qt Network | 用于简化网络编程的类。 |
Qt QML | QML and JavaScript 相关的类。 |
Qt Quick | 用于构建高动态和易交互的用户界面的声明式框架。 |
Qt Quick Controls | 提供轻量级的 QML 类型,用于为桌面、嵌入式和移动设备创建高性能的用户界面。 |
Qt Quick Dialogs | 用于创建对话框的 QML types。 |
Qt Quick Layouts | 用于布局的 QML types |
Qt Quick Test | 用于对 QML 应用的进行单元测试 |
Qt SQL | 用于 SQL 相关的操作 |
Qt Test | 用于对 Qt 应用和库进行单元测试 |
Qt Widgets | 提供了一组 UI 元素来创建经典用户界面 |
Qt附加模块
模块 描述
Active Qt 用于开发使用 ActiveX 和 COM 的 Windows 应用程序
Qt 3D 支持 2D 和 3D 渲染,提供用于开发近实时仿真系统的功能
Qt Android Extras 提供 Android 平台相关的 API
Qt Bluetooth 提供访问蓝牙硬件的功能
Qt Concurrent 提供一些类,无需使用底层的线程控制就可以编写多线程程序
Qt D-Bus 使进程间通过 D-Bus 协议通信的一些类
Qt Gamepad 使 Qt 应用程序支持游戏手柄硬件的使用
Qt Image Formats 支持附加图片格式的插件,包括 TIFF、MNG、TGA、WBMP
Qt Mac Extras 提供 macOS 平台相关的 API
Qt NFC 提供访问 NFC (近场通信)硬件的功能
Qt Positioning 提供一些类,用于通过 GPS 卫星、WiFi 等定位
Qt Print Support 提供一些用于打印控制的类
Qt Purchasing 提供一些类,在 Qt 应用程序内实现应用内购买的功能
Qt Sensors 提供访问传感器硬件的功能,以识别运动和手势
Qt Serial Bus 访问串行工业总线的功能,目前只支持 CAN 和 Modbus 协议
Qt SVG 提供显示 SVG 图片文件的类
Qt WebChannd 用于实现服务器端(QML 或 C++ 应用程序)与客户端(HTML/JavaScript 或 QML 应用程序)之间的 P2P 通信
Qt WebEngine 提供类和函数,实现在应用程序中嵌入网页内容
Qt WebSocket 提供兼容于 RFC 6455 的 WebSocket 通信,是实现客户端程序与远端主机进行双向通信的基于 Web 的协议
Qt Windows Extras 提供 Windows 平台相关的 API
Qt XML 该模块不再维护了,应使用 QtCore 中的 QXmlStreamReader 和 QXmlStream Writer Qt XML Patterns 提供对 XPath、XQuery、XSLT 和 XML 等的支持
Qt Charts 用于数据显示的二维图表组件
Qt Data Visualization 用于 3D 数据可视化显示的界面组件
Qt Virtual Keyboard 实现不同输入法的虚拟键盘框架
增值模块
除了随 Qt 5 发布的上述这些模块,还有一些模块(见表 3)是单独发布的,这些模块只在商业版许可的 Qt 里才有。
表 3 Qt的增值模块
特性 描述
Qt for Device Creation 高效、易用、全集成的嵌入式设备应用程序开发工具,包括很多其他增值特性
Qt Quick Compiler 编译.qml 源文件生成二进制应用程序的编译器,提高载入时间和代码的安全性
技术预览模块
技术预览模块就是一些还处于开发和测试阶段的模块,一般技术预览模块经过几个版本的发布后会变成正式的模块。表 4 是 Qt 5.9 中的技术预览模块。
模块 描述
Qt Network Authorization 基于 OAuth 协议,为应用程序提供网络账号验证的功能
Qt Speech 提供文字转语音(text-to-speech)功能支持
Qt Remote Objects 进程间或设备间通信,共享 QObject 的 API
Qt 工具
Qt 工具(见表 5)在所有支持的平台上都可以使用,用于帮助应用程序的开发和设计。
工具 描述
Qt Designer 用于扩展 Qt Designer 的类
Qt Help 在应用程序中集成在线文档的类,实现类似于 Qt Assistant 的功能
QtUI Tools 操作 Qt Designer 生成的窗体的类
QSpinBox 旋转框
二进制显示这些都是这个组件的特有的
例子:
#include "widget.h"
#include "./ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::setTotal()
{
int num = ui->doubleSpinBox1->value();
float price = ui->doubleSpinBox2->value();
float total = num*price;
ui->doubleSpinBox3->setValue(total);
}
void Widget::on_pushButton_5_clicked()
{
this->setTotal();
}
void Widget::on_doubleSpinBox1_valueChanged(double arg1)
{
this->setTotal();
}
void Widget::setToBase(int number)
{
ui->spinBox_4->setValue(number);
ui->spinBox_5->setValue(number);
ui->spinBox_6->setValue(number);
ui->spinBox_7->setValue(number);
}
void Widget::on_pushButton_clicked()
{
int bin = ui->spinBox_4->value();
setToBase(bin);
}
void Widget::on_pushButton_2_clicked()
{
int dec = ui->spinBox_5->value();
setToBase(dec);
}
void Widget::on_pushButton_3_clicked()
{
int otc = ui->spinBox_6->value();
setToBase(otc);
}
void Widget::on_pushButton_4_clicked()
{
int hex = ui->spinBox_7->value();
setToBase(hex);
}
void Widget::on_spinBox_4_valueChanged(int arg1)
{
int bin = ui->spinBox_4->value();
setToBase(bin);
}
QSlider 其他数字输入和显示组件
QTime.....日期和其他控件
例子:
#include "widget.h"
#include "./ui_widget.h"
#include <QDebug>
#include <QTimer>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//定时器,没时隔1s 调用SLOT函数
this->fTimer = new QTimer(this);
//设置间隔
this->fTimer->setInterval(1000);
this->fTimer->stop();
connect(fTimer,SIGNAL(timeout()),this,SLOT(on_timer_timeout()));
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_readCourrentTime_btn_clicked()
{
//获取当前时间
QDateTime curDateTime = QDateTime::currentDateTime();
//时间
ui->timeEdit->setTime(curDateTime.time());
//日期
ui->dateEdit->setDate(curDateTime.date());
//时间日期
ui->dateTimeEdit->setDateTime(curDateTime);
//时间转字符
ui->time_lineEdit->setText(curDateTime.toString("HH:mm:ss"));
ui->date_lineEdit->setText(curDateTime.toString("yyyy-MM-dd"));
ui->dateTime_lineEdit->setText(curDateTime.toString("yyyy-MM-dd HH:mm:ss"));
}
void Widget::on_setTime_btn_clicked()
{
QString str= ui->time_lineEdit->text();
//字符转时间
QTime time = QTime::fromString(str,"HH:mm:ss");
ui->timeEdit->setTime(time);
}
void Widget::on_setDate_btn_clicked()
{
QString str = ui->date_lineEdit->text();
QDate date = QDate::fromString(str,"yyyy-MM-dd");
ui->dateEdit->setDate(date);
}
void Widget::on_setDateTime_btn_clicked()
{
QString str = ui->dateTime_lineEdit->text();
QDateTime dateTime = QDateTime::fromString(str,"yyyy-MM-dd HH:mm:ss");
ui->dateTimeEdit->setDateTime(dateTime);
}
void Widget::on_calendarWidget_selectionChanged()
{
QDate date = ui->calendarWidget->selectedDate();
ui->selectDate_lineEdit->setText(date.toString("yyyy-MM-dd"));
qDebug()<< "日历选择时间:" <<date;
}
void Widget::on_timer_timeout()
{
//获取当前时间,设置LCD
ui->lcdNumber->display(QTime::currentTime().hour());
ui->lcdNumber_2->display(QTime::currentTime().minute());
ui->lcdNumber_3->display(QTime::currentTime().second());
//设置进度条
int value = ui->progressBar->value();
value++;
if(value>=100){
value=0;
}
ui->progressBar->setValue(value);
}
void Widget::on_setDateRole_btn_clicked()
{
fTimer->setInterval(ui->spinBox->value());
}
// 开始
void Widget::on_start_btn_clicked()
{
this->fTimer->start();
ui->start_btn->setEnabled(false);
ui->setDateRole_btn->setEnabled(false);
ui->end_btn->setEnabled(true);
//开始记时
this->fTimerCounter.start();
}
void Widget::on_end_btn_clicked()
{
this->fTimer->stop();
ui->start_btn->setEnabled(true);
ui->setDateRole_btn->setEnabled(true);
//返回自上次调用start()或restart()以来经过的毫秒数。
int tmMsecl= this->fTimerCounter.elapsed();
int sec = tmMsecl/1000; // 获取秒数
int mSec = tmMsecl%1000; //获取毫秒数
ui->label_5->setText(QString::asprintf("时间流逝: %1 秒 %2 毫秒").arg(sec).arg(mSec));
}
QApplication
qApp->processEvents(); //作用是处理密集型耗时的事情。
有时候需要处理一些跟界面无关的但非常耗时的事情,这些事情跟界面在同一个线程中,由于时间太长,导致界面无法响应,处于“假死”状态。例如:在应用程序中保存文件到硬盘上,从开始保存直到文件保存完毕,程序不响应用户的任何操作,窗口也不会重新绘制,从而处于“无法响应”状态,这是一个非常糟糕的体验 。
在这种情况下,有一种方法是使用多线程,即在子线程中处理文件保存,主线程负责界面相关。
而如果不想使用多线程,最简单的办法就是在文件保存过程中频繁调用QApplication::processEvents()。该函数的作用是让程序处理那些还没有处理的事件,然后再把使用权返回给调用者。
QFile文件
QFile
打开一个文件
#include <QFile>
#include <QByteArray>
QFile有三个open()
函数重载
bool open(FILE *fh, QIODevice::OpenMode mode, QFileDevice::FileHandleFlags handleFlags = DontCloseHandle)
bool open(int fd, QIODevice::OpenMode mode, QFileDevice::FileHandleFlags handleFlags = DontCloseHandle)
virtual bool open(QIODevice::OpenMode mode) override //常用
开始使用:
QFile file("G:\\C++Project\\Demo\\Duplicate_file_detection\\CMakeLists.txt");
if(file.open( QIODevice::ReadOnly)){
QByteArray content = file.readAll();
qDebug() << content.toStdString().c_str() << "\n文件读取完毕";
file.close();
}
QDir & QFileInfo
递归遍历文件
QStringList Duplicate::getFiles(const QString filesPath)
{
QStringList ret;
QDir dir(filesPath); // 查看G:/xx/xxxx 下的文件
//QStringList dirList = dir.entryList();//返回目录下的所有文件
QFileInfoList infoList = dir.entryInfoList(QDir::Files | QDir::Dirs |QDir::NoDotAndDotDot);//返回文件信息,过滤文件信息: .表示当前目录,..表示上一级目录
//for(int i =0;i< infoList.count();i++){ infoList.at(i)}
for(auto info : infoList){
//是不是一个目录
if(info.isDir()){
QString fileinfo = info.absoluteFilePath();
//递归遍历目录结构
QStringList files = getFiles(fileinfo);
ret.append(files);
}else{
//是一个文件
QString fileName= info.absoluteFilePath();
ret.append(fileName);
}
}
return ret;
}
QMessageBox消息弹窗:
#include <QMessageBox>
对应方法
static StandardButton information(QWidget *parent, const QString &title,const QString &text, StandardButtons buttons = Ok,StandardButton defaultButton = NoButton);
static StandardButton question(QWidget *parent, const QString &title,const QString &text, StandardButtons buttons = StandardButtons(Yes | No),StandardButton defaultButton = NoButton);
static StandardButton warning(QWidget *parent, const QString &title,const QString &text, StandardButtons buttons = Ok,StandardButton defaultButton = NoButton);
static StandardButton critical(QWidget *parent, const QString &title,const QString &text, StandardButtons buttons = Ok,StandardButton defaultButton = NoButton);
开始使用:
#include <QMessageBox>
......
//QMessageBox::information(this,"读取文件内容",str);
//QMessageBox::critical(this,"错误警告","文件读取失败");
//QMessageBox::question(this,"询问操作","确定要关闭软件吗");
QMessageBox::warning(this,"警告","文件未保存");
QCryptographicHash加密:
例子:
QListWidget和QToolButton
这个组件非常实用
组件 TooBox
,常用于侧边栏,配合 tab Widget
使用,就类似和web前端类似了
组件tab Widget
例子:
QTreeWidget和QDockWidget
使用案例:
QPixmap 图片
QPixmap类是一种可以用作绘制设备的屏幕外图像表示
QTableWidget
表格的第 1 行称为行表头,用于设置每一列的标题,第 1 列称为列表头,可以设置其标题,但一般使用缺省的标题,即为行号。行表头和列表头一般是不可编辑的。
除了行表头和列表头之外的表格区域是内容区,内容区是规则的网格状,如同一个[二维数组](http://c.biancheng.net/c/array/),每个网格单元称为一个单元格。每个单元格有一个行号、列号,图 1 表示了行号、列号的变化规律。
在 QTableWidget 表格中,每一个单元格是一个 QTable Widgetltem 对象,可以设置文字内容、字体、前景色、背景色、图标,也可以设置编辑和显示标记。每个单元格还可以存储一个 QVariant 数据,用于设置用户自定义数据。
例子:
Model/View结构
数据模型
QFileSystemModel
适配TableView TreeView ListView
如同Widnows的资源管理器一样。使用QFileSystemModel提供的接口函数,可
以创建目录、删除目录、重命名目录,可以获得文件名称、目录名称、文件大小等
参数,还可以获得文件的详细信息。
QFileSysetmModel *model = new QFileSystemMOdel;
mode->setRootPath(Qt::Dir::currentPath());
效果

例子:
QStringListModel
QStandardItemModel
配合QTableView组合成Model/View , 每一行的每一项 都有自己的信息内容
例子:
视图组件

代理

代理是在Model和View之间的动作,
表格内的每一个单元可以能是:下拉框,单选框,多选框等等, 代理就是适配表格单元动作
例子:
标准对话框
- 用户选择一个文件
- 用户另存为文件
- 标准消息框
- 标准输入框
例子:
自定义对话框
创建新文件,Dialog文件即可完成
多窗体应用
多个TabWidget
案例
MDI应用程序
QMdiArea(Multiple Document Interface Area)提供了一个可以同时显示多个文档窗口的区域。每一个窗口都是一个 QMdiSubWindow
对象
MDI 有两种模式 MdiArea:SubWindowView是传统的子窗口模式
QMdiArea:TabbedView定多页的显示模式
, 设置模式使用setViewMode()
函数
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本