C++ Qt 学习(一):Qt 入门
- Qt6 安装教程
0. 基础知识
0.1 qmake 和 cmake 对比
- qmake:qt 独有的代码构建工具
- cmake:C++ 通用的代码构建工具,绝大部分 C++ 开源项目都使用 cmake 管理代码
- qt 项目,没有特殊要求,使用 qmake 即可
0.2 Qt 3 个窗口类的区别
- QMainWindow
- 包含菜单栏、工具栏、状态栏
- QMainWindow 使用的场景不多
- QWidget
- 一个普通的窗口,不包含菜单栏、状态栏,除了登录界面
- 新建项目时建议使用 Qwidget,因为大部分的窗口可能都要做成无边框窗口,需要自定义标题栏,实现拉伸等
- QDialog
- 对话框,常用来做登录窗口、弹出窗口 (例如设置界面)
1. 图片查看软件
1.1 main.cpp
#include "widget.h"#include <QApplication>int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec();
}
1.2 widget.h
#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget {Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();private:void open1(); // 基础版本void open2(); // 记住上次打开的路径,并指定默认的路径为 文档/图片void open3(); // 图片自适应显示private slots:void on_btnOpen_clicked();private:Ui::Widget *ui;
};
#endif // WIDGET_H
1.3 widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QFileDialog>
#include <QSettings> // 用于读取和写入应用程序的设置和配置信息
#include <QDebug>
#include <QStandardPaths>
#include <memory> // 智能指针Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);ui->label_image->clear();
}Widget::~Widget() {delete ui;
}void Widget::open1() {// QFileDialog::getOpenFileName() 用于显示一个打开文件对话框,并返回用户选择的文件路径// 参数:指向当前窗口的指针 this,打开的窗口标题,默认打开路径,文件类型过滤器QString filename = QFileDialog::getOpenFileName(this, "请选择图片", "D:/", "图片(*.png *jpg);");if(filename.isEmpty()) {return;}ui->lineEdit_path->setText(filename); // 显示图片路径ui->label_image->setPixmap((QPixmap(filename))); // 显示图片
}// 记住上次打开的路径,并指定默认的路径为 文档/图片
void Widget::open2() {// qApp->applicationDirPath() 用于获取当前应用程序所在的目录路径// "/config/Setting.ini" 是一个固定的字符串表示配置文件的路径QString config_path = qApp->applicationDirPath() + "/config/Setting.ini";qDebug() << config_path;// 使用智能指针创建了一个 QSettings 对象,并使用指定的配置文件路径和格式进行初始化// QSettings::IniFormat 是一个枚举值,用于指定配置文件的格式,此处采用的是 ini 格式的配置文件// 通过 pIniSet 指针调用 value() 函数来获取指定键的值,并将其转换为 QString 类型std::unique_ptr<QSettings> pIniSet(new QSettings(config_path, QSettings::IniFormat));QString lastPath = pIniSet->value("/LastPath/path").toString();// 设置默认读取路径为 windows 下 图片 目录if(lastPath.isEmpty()) {lastPath = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation);}QString filename = QFileDialog::getOpenFileName(this, "请选择图片", lastPath, "图片(*.png *jpg);");if(filename.isEmpty()) {return;}ui->lineEdit_path->setText(filename);ui->label_image->setPixmap((QPixmap(filename)));// 找到给定文件名(filename)中最后一个斜杠"/"的位置int end = filename.lastIndexOf("/");// 提取文件名中最后一个斜杠"/"之前的部分,即路径部分QString _path = filename.left(end);// 将键 "/LastPath/path" 的值设置为 _pathpIniSet->setValue("/LastPath/path", _path);qDebug() << _path;
}// 图片自适应显示
void Widget::open3() {QString config_path = qApp->applicationDirPath() + "/config/Setting.ini";qDebug() << config_path;std::unique_ptr<QSettings> pIniSet(new QSettings(config_path, QSettings::IniFormat));QString lastPath = pIniSet->value("/LastPath/path").toString();if(lastPath.isEmpty()) {lastPath = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation);}QString filename = QFileDialog::getOpenFileName(this, "请选择图片", lastPath, "图片(*.png *jpg);");if(filename.isEmpty()) {return;}ui->lineEdit_path->setText(filename);// 图片自适应显示std::unique_ptr<QPixmap> pix(new QPixmap(filename)); // 通过给定的文件名(filename)加载图像数据// 将加载的图像按照 ui->label_image 控件的大小进行缩放// Qt::KeepAspectRatio:保持图像的纵横比例不变pix->scaled(ui->label_image->size(), Qt::KeepAspectRatio);ui->label_image->setScaledContents(true); // 当图像大于控件大小时,将自动缩放以适应控件的大小ui->label_image->setPixmap(*pix); // 设置 ui->label_image 控件的图像为加载并缩放后的图像int end = filename.lastIndexOf("/");QString _path = filename.left(end);pIniSet->setValue("/LastPath/path", _path);qDebug() << _path;
}void Widget::on_btnOpen_clicked() {//open1();//open2();open3();
}
1.4 widget.ui
2. C++ lambda 函数详解
- C++ lambda 表达式的本质就是重载了 operator(),lambda 是一个类,在调用时会进行编译展开,因此 lambda 表达式对象其实就是一个匿名的 functor,所以 lambda 表达式也叫匿名函数对象
- Qt 槽函数可以使用 lambda 函数来写
- C++ 中 lambda 表达式的构成
[捕获列表](形参列表) mutable 异常列表->返回类型 {函数体 }
- 捕获列表:捕获外部变量,捕获的变量可以在函数体中使用,可以省略,即不捕获外部变量
- 形参列表:和普通函数的形参列表一样。可省略,即无参数列表
- mutable:如果有,则表示在函数体中可以修改捕获变量,根据具体需求决定是否需要省略
- 异常列表:noexcept /throw(…),和普通函数的异常列表一样,可省略,即代表可能抛出任何类型的异常
- 返回类型:和函数的返回类型一样。可省略,如省略,编译器将自动推导返回类型
- 函数体:代码实现,可省略,但是没意义
- 捕获方式
- 值捕获:不能在 lambda 表达式中修改捕获变量的值
- 引用捕获:使用引用捕获一个外部变量,需在捕获列表变量前面加上一个引用说明符 &
- 隐式捕获
#include <iostream>using namespace std;int main() {// 1、值捕获int value = 100;auto f = [value](int a, int b)->int {//value++; // 不能在 lambda 表达式中修改捕获变量的值return a + b + value;};cout << f(1, 2) << endl;// 2、引用捕获auto f2 = [&value](int a, int b)->int {value++;return a + b;};cout << f2(1, 3) << endl;cout << "value = " << value << endl;// 3、隐式捕获// = 值捕获// & 引用捕获int age = 123;auto f3 = [&](int a, int b)->int {value++;age++;return a + b;};return 0; }
3. 槽函数的常见写法
-
Qt 4 写法
connect(ui->btnOpen, SIGNAL(clicked), this, SLOT(open()));
- 不推荐这种写法,如果 SIGNAL写错了,或者信号名字、槽函数名字写错了编译器检查不出来,导致程序无响应,引起不必要的误解
-
Qt 5 写法
connect(ui.btnOpen, QPushButton::clicked, this, &Widget::open);
- 推荐使用这种写法
-
lambda 函数表达式写法
connect(ui.btnOpen, &QPushButton::clicked, [=](){// 具体代码实现 });
- 适用于 slot 代码比较少的逻辑
-
直接法
void on_控件名_信号名();
- 这种不用 connect,Qt 自动连接
4. 自定义信号及参数注册
4.1 跨 UI 发送自定义信号
-
如何自定义信号
- 使用signals声明
- 返回值是void
- 在需要发送的地方使用下述方法进行发送
- emit 信号名字(参数);
- 在需要链接的地方使用connect进行链
-
widget.h
#ifndef WIDGET_H #define WIDGET_H#include <QWidget>QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACEclass Widget : public QWidget {Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();private slots:void on_btnOpen_clicked();private:Ui::Widget *ui; }; #endif // WIDGET_H
-
dialog.h
- 跨 UI 发送:New File --> Qt Designer Form Class --> Dialog without Buttons
#ifndef DIALOG_H #define DIALOG_H#include <QDialog>namespace Ui { class Dialog; }class Dialog : public QDialog {Q_OBJECTpublic:explicit Dialog(QWidget *parent = nullptr);~Dialog();private slots:void on_btnAdd_clicked();signals:void sig_addOne(int value);private:Ui::Dialog *ui; };#endif // DIALOG_H
-
widget.cpp
#include "widget.h" #include "ui_widget.h" #include "dialog.h" // 跨 UI 头文件Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }void Widget::on_btnOpen_clicked() {Dialog dlg;// 使用 lambda 函数编写槽函数connect(&dlg, &Dialog::sig_addOne, [=](int value) {ui->lineEdit->setText(QString::number(value));});dlg.exec(); // 需放在 connect 后,因为此行为事件循环会阻塞 UI }
-
dialog.cpp
#include "dialog.h" #include "ui_dialog.h"Dialog::Dialog(QWidget *parent) : QDialog(parent), ui(new Ui::Dialog) {ui->setupUi(this); }Dialog::~Dialog() {delete ui; }void Dialog::on_btnAdd_clicked() {static int value = 100;emit sig_addOne(value++); // 实现跨 UI 自加操作 }
4.2 跨线程发送自定义信号
Qt 的子线程无法直接修改 ui,需要发送信号到 ui 线程进行修改
-
widget.h
#ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include "childthread.h"QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACEclass Widget : public QWidget {Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();private slots:void on_btnUpdate_clicked();void showInfo(Score s);private:Ui::Widget *ui; }; #endif // WIDGET_H
-
widget.cpp
#include "widget.h" #include "ui_widget.h" #include <QDebug>Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);qDebug() << "ui thread id = " << QThread::currentThreadId(); }Widget::~Widget() {delete ui; }void Widget::on_btnUpdate_clicked() {ChildThread *ch = new ChildThread();// 以下实现还是在子线程中(不在 ui 线程中),无法直接修改 ui // connect(ch, &ChildThread::sig_SendToUI, [=](Score s) { // string info = s.name + "id = " + to_string(s.id) + " age = " + to_string(s.age); // ui->lineEdit->setText(QString::fromStdString(info)); // // 用于验证 slot 与 ui 下是否同属一个线程(id) // // 结果表明此处的 slot 与子线程的 run() 同属一个线程(id) // qDebug() << "slot thread id = " << QThread::currentThreadId(); // });// 以下实现在 ui 线程 (主线程) 中,可以直接修改 uiconnect(ch, &ChildThread::sig_SendToUI, this, &Widget::showInfo);ch->start(); }void Widget::showInfo(Score s) {qDebug() << "ui thread id2 = " << QThread::currentThreadId();string info = s.name + "id = " + to_string(s.id) + " age = " + to_string(s.age);ui->lineEdit->setText(QString::fromStdString(info)); }
-
childthread.h
#ifndef CHILDTHREAD_H #define CHILDTHREAD_H#include <QThread> #include <string>using namespace std;struct Score {string name;int id;int age; };class ChildThread : public QThread {Q_OBJECTpublic:ChildThread();protected:void run() override;signals:void sig_SendToUI(Score score); };#endif // CHILDTHREAD_H
-
childthread.cpp
#include "childthread.h" #include <QDebug>ChildThread::ChildThread() {// 非基础类型参数需要注册qRegisterMetaType<Score>("Score");qRegisterMetaType<string>("string"); }void ChildThread::run() {qDebug() << "run thread id = " << QThread::currentThreadId();Score s;s.name = "jack";s.id = 1001;s.age = 26;emit sig_SendToUI(s); // 发送信号 }
4.3 处理信号重名问题
-
例如 QComboBox 的信号
Q_SIGNALS:void currentIndexChanged(int index);void currentIndexChanged(const QString &);
-
解决方案
// 错误写法 connect(ui->comboBox, &QComboBox::currentIndexChanged, this, &Widget::onIndex);// 解决方案一 connect(ui->comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onIndex(int)));// 解决方案二 connect(ui->comboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &Widget::onIndex);
5. connect 函数详解
template <typename Func1, typename Func2>
static inline QMetaObject::Connection connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender,Func1 signal,const typename QtPrivate::FunctionPointer<Func2>::Object *receiver,Func2 slot,Qt::ConnectionType type = Qt::AutoConnection)
enum ConnectionType {AutoConnection,DirectConnection,QueuedConnection,BlockingQueuedConnection,UniqueConnection = 0x80,SingleShotConnection = 0x100,
};
-
AutoConnection
- 默认连接方式,如果接收方在发出信号的线程中,使用 Qt::DirectConnection
- 否则使用 Qt::QueuedConnection,在发出信号时确定连接类型
- Qt 中默认使用 AutoConnection,所以平时写信号槽时都是 4 个参数
-
DirectConnection
- 当发出信号时,插槽立即被调用,槽在发送信号的线程中执行
-
QueuedConnection
- 当控制返回到接收方线程的事件循环时调用槽,槽在接收方的线程中执行
-
BlockingQueuedConnection
- 与 Qt::QueuedConnection 相同,只是发送信号的线程会阻塞,直到槽返回
- 如果接收方存在于发送信号的线程中,则不能使用此连接,否则应用程序将产生死锁
-
UniqueConnection
- 这是一个可以使用按位 OR 与上述任何一种连接类型组合的标志,当 Qt::UniqueConnection 被设置时,如果连接已经存在,QObject::connect() 将失败 (例如,如果相同的信号已经连接到相同的对象对的插槽)
6. Qt 信号槽与 MOC
- moc 全称是 Meta-Object Compiler,也就是 “元对象编译器”
- Qt 程序在交由标准编译器编译之前,先要使用 moc 分析 C++ 源文件
- 如果发现在一个头文件中包含了宏 Q_OBJECT,则会生成另外一个 C++ 源文件,这个源文件中包含了 Q_OBJECT 宏的实现代码,这个新的文件名是原文件名前面加上 moc_ 构成,这个新的文件同样将进入编译系统,最终被链接到二进制代码中去。因此,这个新的文件不是 “替换” 掉旧的文件,而是与原文件一起参与编译
- 另外,还可看出:moc 的执行是在预处理器之前,因为预处理器执行之后,Q_OBJECT 宏就不存在了
可以这么理解,moc 把 Qt 中一些不是 C++ 的关键字做了解析,让 C++ 编译器认识,例如:slots, signals,emit 等,moc 会把这些重新编译解析
7. Qt 内存管理机制
-
C++ 派生类
- 构造顺序:先执行基类的构造函数,再执行派生类的构造函数
- 析构顺序:先执行派生类的析构函数,再执行基类的析构函数
-
Qt 半内存管理机制
- QObject 及其派生类的对象,如果其 parent 非 0,那么其 parent 析构时会析构该对象
- QWidget 及其派生类的对象,可以设置 Qt::WA_DeleteOnClose 标志位,当 close 时会调用 QWidgetPrivate::close_helper,进而调用 deleteLater 析构该对象
8. 解决 Qt 中文乱码问题
-
粘贴别人的代码时,首先在记事本里复制一遍,再粘贴到 QtCreator
-
使用 u8
- ui.pushButton->setText (u8"你好")
-
不使用 QtCreator开发,直接使用 vs2019
-
其他设置
- QtCreator — 选项 — 文本编辑器 — UTF8 BOM 总是删除
- #pragma execution_character_set(“utf-8”)
相关文章:

C++ Qt 学习(一):Qt 入门
Qt6 安装教程 0. 基础知识 0.1 qmake 和 cmake 对比 qmake:qt 独有的代码构建工具cmake:C 通用的代码构建工具,绝大部分 C 开源项目都使用 cmake 管理代码qt 项目,没有特殊要求,使用 qmake 即可 0.2 Qt 3 个窗口类的…...

高性能消息中间件 - Kafka3.x(三)
文章目录 高性能消息中间件 - Kafka3.x(三)Kafka Broker ⭐Kafka Broker概念Zookeeper(新版本可以不使用zk了)⭐Zookeeper的作用 Kafka的选举1:Broker选举Leader⭐Broker核心参数⭐案例:服役新节点和退役旧…...

【八】Linux成神之路
Linux成神之路 简介:最近梳理了一下自己linux系统的学习历程,感觉整个成长过程就很顺利,并没有走弯路,于是想着可以不可以把自己linux系统学习的路线记录下来,能够在大家成长的路上有一点帮助,就在这样的一…...

功能测试用例,需要详细到什么程度?
📢专注于分享软件测试干货内容,欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!📢交流讨论:欢迎加入我们一起学习!📢资源分享:耗时200小时精选的「软件测试」资…...

VScode远程连接错误:进程试图写入不存在的管道
使用VScode连接树莓派时,出现远程连接错误:进程试图写入不存在的管道 解决方案: (1)可以进入config所在文件夹,删除文件 (2)无法解决的化尝试下述方法 输入 Remotting-SSH:Settin…...

Python测试之Pytest详解
概要 当涉及到python的测试框架时,pytest是一个功能强大且广泛应用的第三方库。它提供简洁而灵活的方式来编写和执行测试用例,并具有广泛的应用场景。下面是pytest的介绍和详细使用说明: pytest是一个用于python单元测试的框架,它…...

uni-app微信小程序打开第三方地图
需求 小程序中有个按钮点击以后会调用手机中第三方地图进行导航。参数 位置信息 经度 与纬度。 实现方法 uni.openLocation({latitude: Number(地址纬度),longitude: Number(地址经度),name: 地址名称,address: 地址详情,success: function (res) {console.log(打开系统位置地…...
Android NDK开发详解之NDK 使用入门
Android NDK开发详解之NDK 使用入门 下载 NDK 和工具创建或导入原生项目 原生开发套件 (NDK) 是一套工具,使您能够在 Android 应用中使用 C 和 C 代码,并提供众多平台库,您可使用这些平台库管理原生 activity 和访问实体设备组件,…...
nmap指纹识别要点以及又快又准之方法
nmap指纹识别要点以及又快又准之方法 一. 前言:二. nmap识别实验:一. 实验一:IP配置:空间配置:扫描结果:详细输出二. 实验二:IP配置:空间配置:扫描结果:详细输出三. 实验三:IP配置:空间配置:扫描结果:详细输出四. 实验四:IP...

Rust编程基础之6大数据类型
1.Rust数据类型 在 Rust 中, 每一个值都属于某一个 数据类型(data type), 这告诉 Rust 它被指定为何种数据,以便明确数据处理方式。我们将看到两类数据类型子集:标量(scalar)和复合(compound&a…...

06 MIT线性代数-线性无关,基和维数Independence, basis, and dimension
1. 线性无关 Independence Suppose A is m by n with m<n (more unknowns than equations) Then there are nonzero solutions to Ax0 Reason: there will be free variables! A中具有至少一个自由变量,那么Ax0一定具有非零解。A的列向量可以线性组合得到零向…...

Kubernetes 概述以及Kubernetes 集群架构与组件
目录 Kubernetes概述 K8S 是什么 为什么要用 K8S K8S 的特性 Kubernetes 集群架构与组件 核心组件 Master 组件 Node 组件 编辑 Kubernetes 核心概念 常见的K8S按照部署方式 Kubernetes概述 K8S 是什么 K8S 的全称为 Kubernetes,Kubernetes 是一个可移植、可扩…...

GZ035 5G组网与运维赛题第9套
2023年全国职业院校技能大赛 GZ035 5G组网与运维赛项(高职组) 赛题第9套 一、竞赛须知 1.竞赛内容分布 竞赛模块1--5G公共网络规划部署与开通(35分) 子任务1:5G公共网络部署与调试(15分) 子…...
使用Jasypt3.0.3版本对SpringBoot配置文件加密
时间 2023-11-01 使用Jasypt3.0.3版本对SpringBoot配置文件加密 目录 引入依赖使用密钥生成密文配置yml验证是否自动解密 引入依赖 <!--yml 文件加解密--><dependency><groupId>com.github.ulisesbocchio</groupId><artifactId>jasypt-spring-b…...
生成一篇博客,详细讲解springboot的单点登录功能,有流程图,有源码demo
SpringBoot是目前非常流行的一个Java开发框架,它以简洁的配置和快速的开发效率著称。在实际应用中,单点登录是一个非常重要的功能,它可以让用户在多个应用系统中使用同一个账号登录,提高用户体验和安全性。本文将详细讲解如何在Sp…...

Hadoop、Hive安装
一、 工具 Linux系统:Centos,版本7.0及以上 JDK:jdk1.8 Hadoop:3.1.3 Hive:3.1.2 虚拟机:VMware mysql:5.7.11 工具下载地址: https://pan.baidu.com/s/1JYtUVf2aYl5–i7xO6LOAQ 提取码: xavd…...
PHP自定义函数--输入起始日期和解算日期返回日期差几天和 上一个周期的起始结束日期
/** 日期差几天* param beginDate:2018-01-26 endDatee:2018-01-26* return int days* */ function dateDiff($beginDate, $endDate) {$diff date_diff(date_create($beginDate), date_create($endDate))->format(%R%a);return (int)$diff; }/** 返回上一周期的起始和结束日…...
.net 7 上传文件踩坑
(Name “file”) 没加上这个传不进文件 /// <summary>/// 上传单个文件/// </summary>/// <param name"formFile"></param>/// <returns></returns>[HttpPost("UploadFiles")][FunctionAttribute(MuType.Btn, "…...

C++基础算法④——排序算法(快速、归并附完整代码)
快速排序 快速排序是对冒泡排序的一种改进。 它的基本思想是:通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行快速排序,以达到整个序列有序。 假设我们现在对 …...

高防CDN如何在防护cc上大显神通
高级防御CDN(Content Delivery Network)在对抗CC(HTTP Flood)攻击方面扮演着关键的角色,具备以下重要职能和作用: 流量分散:CC攻击的目标是通过大规模的HTTP请求使服务器过载,从而导…...
谷歌浏览器插件
项目中有时候会用到插件 sync-cookie-extension1.0.0:开发环境同步测试 cookie 至 localhost,便于本地请求服务携带 cookie 参考地址:https://juejin.cn/post/7139354571712757767 里面有源码下载下来,加在到扩展即可使用FeHelp…...
Cursor实现用excel数据填充word模版的方法
cursor主页:https://www.cursor.com/ 任务目标:把excel格式的数据里的单元格,按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例,…...

Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误
HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误,它们的含义、原因和解决方法都有显著区别。以下是详细对比: 1. HTTP 406 (Not Acceptable) 含义: 客户端请求的内容类型与服务器支持的内容类型不匹…...

PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建
制造业采购供应链管理是企业运营的核心环节,供应链协同管理在供应链上下游企业之间建立紧密的合作关系,通过信息共享、资源整合、业务协同等方式,实现供应链的全面管理和优化,提高供应链的效率和透明度,降低供应链的成…...

《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- CSI-2 协议详细解析 (一)
CSI-2 协议详细解析 (一) 1. CSI-2层定义(CSI-2 Layer Definitions) 分层结构 :CSI-2协议分为6层: 物理层(PHY Layer) : 定义电气特性、时钟机制和传输介质(导线&#…...

Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)
概述 在 Swift 开发语言中,各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过,在涉及到多个子类派生于基类进行多态模拟的场景下,…...
深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法
深入浅出:JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中,随机数的生成看似简单,却隐藏着许多玄机。无论是生成密码、加密密钥,还是创建安全令牌,随机数的质量直接关系到系统的安全性。Jav…...
JVM垃圾回收机制全解析
Java虚拟机(JVM)中的垃圾收集器(Garbage Collector,简称GC)是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象,从而释放内存空间,避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...
基于Uniapp开发HarmonyOS 5.0旅游应用技术实践
一、技术选型背景 1.跨平台优势 Uniapp采用Vue.js框架,支持"一次开发,多端部署",可同步生成HarmonyOS、iOS、Android等多平台应用。 2.鸿蒙特性融合 HarmonyOS 5.0的分布式能力与原子化服务,为旅游应用带来…...

CMake 从 GitHub 下载第三方库并使用
有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...