Qt源码解析之QObject
省去大部分virtual和public方法后,Qobject主要剩下以下成员:
//qobject.h
class Q_CORE_EXPORT Qobject{Q_OBJECTQ_PROPERTY(QString objectName READ objectName WRITE setObjectName NOTIFY objectNameChanged)Q_DECLARE_PRIVATE(QObject)
public:Q_INVOKABLE explicit QObject(QObject *parent=nullptr);virtual ~QObject();//...
protected:QObject(QObjectPrivate &dd, QObject *parent = nullptr);//...
protected:QScopedPointer<QObjectData> d_ptr;static const QMetaObject staticQtMetaObject;//...
private:Q_DISABLE_COPY(QObject)//...
}
一、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 \
private: \Q_OBJECT_NO_ATTRIBUTES_WARNING \Q_DECL_HIDDEN_STATIC_METACALL static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **); \QT_WARNING_POP \struct QPrivateSignal {}; \QT_ANNOTATE_CLASS(qt_qobject, "")1、宏 QT_WARNING_PUSH 和 QT_WARNING_POP用于保存和恢复编译器的警告状态,以便在宏定义内部做一些修改或设置,而不影响用户定义的警告状态。
2、Q_OBJECT_NO_OVERRIDE_WARNING和Q_OBJECT_NO_ATTRIBUTES_WARNING这两个宏用于控制是否发出关于未覆盖(override)的警告或者关于某些属性的警告。
3、QT_TR_FUNCTIONS这个宏用于启用Qt的国际化(internationalization)功能,使得文本可以被翻译为不同的语言。
4、Q_DECL_HIDDEN_STATIC_METACALL在qobjectdefs.h有定义:
# define Q_DECL_HIDDEN_STATIC_METACALL Q_DECL_HIDDEN
使用 Q_DECL_HIDDEN 可以将类或函数标记为在外部接口中隐藏的,从而使它们对库的用户不可见。这对于避免一些链接时的符号冲突和提高库的封装性很有帮助。这个宏可能会被翻译成 __attribute__((visibility("hidden")))。也就是说qt_static_metacall这个函数没用到,我们忽略。
去除和编译器相关的宏,Q_OBJECT剩下的关键部分:
//qobjectdefs.h
#define Q_OBJECT \
public: \static const QMetaObject staticMetaObject; \virtual const QMetaObject *metaObject() const; \virtual void *qt_metacast(const char *); \virtual int qt_metacall(QMetaObject::Call, int, void **); \
private: \Q_DECL_HIDDEN_STATIC_METACALL static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **); Q_OBJECT宏声明了1个QMetaObject变量和3个QMetaObject相关的虚函数。QMetaObject类非常重要,和元对象系统相关。
二、Q_PROPERTY
//qobjectdefs.h
#define Q_PROPERTY(...) QT_ANNOTATE_CLASS(qt_property, __VA_ARGS__)
#define QT_ANNOTATE_CLASS(type, ...)在 qobjectdefs.h 中我们并没有看到 Q_PROPERTY 的准确定义。很多Qt的宏和特殊功能是通过moc生成的代码而不是在头文件中显式定义的。C++编译器能够识别 Q_PROPERTY 宏,是因为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])
三、Q_DECLARE_PRIVATE
//qglobal.h
#define Q_DECLARE_PRIVATE(Class) \inline Class##Private* d_func() \{ Q_CAST_IGNORE_ALIGN(return reinterpret_cast<Class##Private *>(qGetPtrHelper(d_ptr));) } \inline const Class##Private* d_func() const \{ Q_CAST_IGNORE_ALIGN(return reinterpret_cast<const Class##Private *>(qGetPtrHelper(d_ptr));) } \friend class Class##Private;加入参数并翻译过后:
inline QObjectPrivate* d_func()
{ Q_CAST_IGNORE_ALIGN(return reinterpret_cast<QObjectPrivate *>(qGetPtrHelper(d_ptr));) }
inline const QObjectPrivate* d_func() const
{ Q_CAST_IGNORE_ALIGN(return reinterpret_cast<const QObjectPrivate *>(qGetPtrHelper(d_ptr));) }
friend class QObjectPrivate;qGetPtrHelper()方法的定义:
//qglobal.h
template <typename T> static inline T *qGetPtrHelper(T *ptr) { return ptr; }
template <typename Wrapper> static inline typename Wrapper::pointer qGetPtrHelper(const Wrapper &p) { return p.data(); }qGetPtrHelper是一个模板函数,其目的是为了获取指针或类似指针的数据。
Q_CAST_IGNORE_ALIGN用于禁用GCC编译器的 -Wcast-align 警告。Q_DECLARE_PRIVATE宏定义了2个函数和1个友元类。2个d_func只是签名不同,传入参数d_ptr,都返回一个QObjectPrivate*类型的指针,而且友元类的名称也是QObjectPrivate。
四、QObjectData和QObjectPrivate
关于变量QScopedPointer<QObjectData> d_ptr:QScopedPointer类是用于存储指向动态分配对象的指针,并在其销毁时删除它,确保指向的对象在当前作用域消失时将被删除。
所以QScopedPointer<QObjectData>是一个QObjectData的指针。QObjectData定义:
//qobject.h
class Q_CORE_EXPORT QObjectData {//防止对象拷贝Q_DISABLE_COPY(QObjectData)
public:QObjectData() = default;virtual ~QObjectData() = 0;QObject *q_ptr;QObject *parent;QObjectList children;uint isWidget : 1;uint blockSig : 1;uint wasDeleted : 1;uint isDeletingChildren : 1;uint sendChildEvents : 1;uint receiveChildEvents : 1;uint isWindow : 1; //for QWindowuint deleteLaterCalled : 1;uint unused : 24;int postedEvents;QDynamicMetaObjectData *metaObject;QMetaObject *dynamicMetaObject() const;
#ifdef QT_DEBUGenum { CheckForParentChildLoopsWarnDepth = 4096 };
#endif
};上面说到d_func函数传入参数d_ptr,返回的QObjectPrivate*类型的指针,而d_ptr是QObjectData,那也就是说QObjectPrivate是QObjectData的子类。我们且看QObjectPrivate的定义:
//qobject_p.h
class Q_CORE_EXPORT QObjectPrivate : public QObjectData
{Q_DECLARE_PUBLIC(QObject)
public:struct ExtraData{//...};//和信号&槽相关struct ConnectionOrSignalVector{//...};//和信号&槽相关struct Connection : public ConnectionOrSignalVector{//...};//和信号&槽相关struct Sender{//...};//和信号&槽相关struct ConnectionData{//...};QObjectPrivate(int version = QObjectPrivateVersion);virtual ~QObjectPrivate();
public:ExtraData *extraData;QAtomicPointer<QThreadData> threadData;using ConnectionDataPointer = QExplicitlySharedDataPointer<ConnectionData>;QAtomicPointer<ConnectionData> connections;union {QObject *currentChildBeingDeleted;QAbstractDeclarativeData *declarativeData;};QAtomicPointer<QtSharedPointer::ExternalRefCountData> sharedRefcount;
}Q_DECLARE_PUBLIC(QObject)定义:
//qglobal.h
#define Q_DECLARE_PUBLIC(Class) \inline Class* q_func() { return static_cast<Class *>(q_ptr); } \inline const Class* q_func() const { return static_cast<const Class *>(q_ptr); } \friend class Class;翻译过后:
inline QObject* q_func() { return static_cast<QObject *>(q_ptr); }
inline const QObject* q_func() const { return static_cast<const QObject *>(q_ptr); } \
friend class QObject;
这个宏实际上定义了2个签名不一样的函数q_func(),返回q_ptr指针,声明了QObject是友元类。QObjectPrivate的构造器定义如下:
//qobject.cpp
QObjectPrivate::QObjectPrivate(int version): threadData(nullptr), currentChildBeingDeleted(nullptr)
{checkForIncompatibleLibraryVersion(version);// QObjectData initializationq_ptr = nullptr;parent = nullptr; // no parent yet. It is set by setParent()isWidget = false; // assume not a widget objectblockSig = false; // not blocking signalswasDeleted = false; // double-delete catcherisDeletingChildren = false; // set by deleteChildren()sendChildEvents = true; // if we should send ChildAdded and ChildRemoved events to parentreceiveChildEvents = true;postedEvents = 0;extraData = nullptr;metaObject = nullptr;isWindow = false;deleteLaterCalled = false;
}
基本上是对继承下来的变量和自身变量进行初始化。
五、QObject()
当实例化一个继承自QObject的对象时,首先会调用QObject的构造器,构造器开始构造对象模型的世界,我们且看QObject构造函数QObject()的定义:
//qobject.cpp
QObject::QObject(QObject *parent): QObject(*new QObjectPrivate, parent)
{
}//qobject.cpp
QObject::QObject(QObjectPrivate &dd, QObject *parent): d_ptr(&dd)
{Q_ASSERT_X(this != parent, Q_FUNC_INFO, "Cannot parent a QObject to itself");Q_D(QObject);d_ptr->q_ptr = this;auto threadData = (parent && !parent->thread()) ? parent->d_func()->threadData.loadRelaxed() : QThreadData::current();threadData->ref();d->threadData.storeRelaxed(
threadData);if (parent) {QT_TRY {if (!check_parent_thread(parent, parent ? parent->d_func()->threadData.loadRelaxed() : nullptr,
threadData))parent = nullptr;if (d->isWidget) {if (parent) {d->parent = parent;d->parent->d_func()->children.append(
this);}// no events sent here, this is done at the end of the QWidget constructor} else {setParent(parent);}} QT_CATCH(...) {threadData->deref();QT_RETHROW;}}
#if QT_VERSION < 0x60000qt_addObject(this);
#endifif (Q_UNLIKELY(qtHookData[QHooks::AddQObject]))reinterpret_cast<QHooks::AddQObjectCallback>(qtHookData[QHooks::AddQObject])(this);Q_TRACE(QObject_ctor, this);
}public的构造函数实际上是调用了protected的构造函数。
默认新建了一个QObjectPrivate并作为构造函数参数传入,赋值给了d_ptr。变量QScopedPointer<QObjectData> d_ptr在构造函数里实际被赋值为其新建的子实例QObjectPrivate。Q_D(QObject)定义:
//qglobal.h
#define Q_D(Class) Class##Private * const d = d_func()
调用d_func()得到QObjectPrivate* 并赋值给d,此时d和d_ptr都指向前面实例化的QObjectPrivate。d_ptr->q_ptr = this;
将QObjectPrivate->q_ptr设置为自身。//qobject.cpp
auto threadData = (parent && !parent->thread()) ? parent->d_func()->threadData.loadRelaxed() : QThreadData::current();
threadData->ref();
d->threadData.storeRelaxed(
threadData);
检查 parent 是否非空且它所属的线程是否为空,如果都不空的话,获取parent的线程数据;否则获取当前的线程数据。将线程数据存储到对象内部的数据结构中。//qobject.cpp
if (!check_parent_thread(parent, parent ? parent->d_func()->threadData.loadRelaxed() : nullptr, threadData))parent = nullptr;
检查parent和当前对象是否在相同的线程中,如果不在相同线程中,将 parent 设置为 nullptr。//qobject.cpp
if (d->isWidget) {if (parent) {d->parent = parent;d->parent->d_func()->children.append(this);}
}else{//...
}
如果对象是一个QWidget,parent不空,则建立起对象和parent的联系,对象的父对象就是parent,parent的children添加该对象。//qobject.cpp
if (d->isWidget) {//...
} else {setParent(parent);
}
如果对象不是QWidget,通过setParent(parent)设置父对象。setParent()的定义:
//qobject.cpp
void QObject::setParent(QObject *parent)
{Q_D(QObject);Q_ASSERT(!d->isWidget);d->setParent_helper(parent);
}
继续调用d->setParent_helper(parent)。setParent_helper()的定义:
void QObjectPrivate::setParent_helper(QObject *o)
{Q_Q(QObject);Q_ASSERT_X(q != o, Q_FUNC_INFO, "Cannot parent a QObject to itself");
#ifdef QT_DEBUGconst auto checkForParentChildLoops = qScopeGuard(
[&](){int depth = 0;auto p = parent;while (p) {if (++depth == CheckForParentChildLoopsWarnDepth) {qWarning(
"QObject %p (class: '%s', object name: '%s') may have a loop in its parent-child chain; ""this is undefined behavior",q, q->metaObject()->className(), qPrintable(q->objectName()));}p = p->parent();}});
#endifif (o == parent)return;if (parent) {QObjectPrivate *parentD = parent->d_func();if (parentD->isDeletingChildren && wasDeleted&& parentD->currentChildBeingDeleted == q) {// don't do anything since QObjectPrivate::deleteChildren() already// cleared our entry in parentD->children.} else {const int index = parentD->children.indexOf(q);if (index < 0) {// we're probably recursing into setParent() from a ChildRemoved event, don't do anything} else if (parentD->isDeletingChildren) {parentD->children[index] = 0;} else {parentD->children.removeAt(index);if (sendChildEvents && parentD->receiveChildEvents) {QChildEvent e(QEvent::ChildRemoved, q);QCoreApplication::sendEvent(parent, &e);}}}}parent = o;if (parent) {// object hierarchies are constrained to a single threadif (threadData != parent->d_func()->threadData) {qWarning(
"QObject::setParent: Cannot set parent, new parent is in a different thread");parent = nullptr;return;}parent->d_func()->children.append(q);if(sendChildEvents && parent->d_func()->receiveChildEvents) {if (!isWidget) {QChildEvent e(QEvent::ChildAdded, q);QCoreApplication::sendEvent(parent, &e);}}}if (!wasDeleted && !isDeletingChildren && declarativeData && QAbstractDeclarativeData::parentChanged)QAbstractDeclarativeData::parentChanged(declarativeData, q, o);
}Q_Q(QObject)的定义:
//qglobal.h
#define Q_Q(Class) Class * const q = q_func()
通过q_func()获取QObjectPrivate的q_ptr,在上面我们知道q_ptr指向了QObject,所以q和q_ptr都指向QObject。#ifdef QT_DEBUGconst auto checkForParentChildLoops = qScopeGuard(
[&](){int depth = 0;auto p = parent;while (p) {if (++depth == CheckForParentChildLoopsWarnDepth) {qWarning(
"QObject %p (class: '%s', object name: '%s') may have a loop in its parent-child chain; ""this is undefined behavior",q, q->metaObject()->className(), qPrintable(q->objectName()));}p = p->parent();}});
#endif
这一段通过warning可以推断出是在检测父子关系链中是否存在循环,如果循环链深度超过阈值,则警告。if (o == parent)return;
如果已经设置过parent且没变,直接返回。//如果已经有parent
if (parent) {//获取父对象的QObjectPrivateQObjectPrivate *parentD = parent->d_func();//检查父对象是否正在删除其子对象,当前对象是否已经被删除,前对象是否是父对象正在删除的子对象。//如果这些条件都成立,就跳过后续的处理,因为在删除子对象的过程中已经做了清理工作。if (parentD->isDeletingChildren && wasDeleted&& parentD->currentChildBeingDeleted == q) {// don't do anything since QObjectPrivate::deleteChildren() already// cleared our entry in parentD->children.} else {//获取当前对象在其父对象的子对象列表中的索引const int index = parentD->children.indexOf(q);//如果索引为负数,可能表示正在从 ChildRemoved 事件中递归到 setParent(),这时不执行任何操作。if (index < 0) {// we're probably recursing into setParent() from a ChildRemoved event, don't do anything} else if (parentD->isDeletingChildren) {//如果父对象正在删除其子对象,将相应的子对象指针更新为0。parentD->children[index] = 0;} else {//否则,从父对象的子对象列表中移除当前对象parentD->children.removeAt(index);//发送一个 ChildRemoved 事件给父对象。if (sendChildEvents && parentD->receiveChildEvents) {QChildEvent e(QEvent::ChildRemoved, q);QCoreApplication::sendEvent(parent, &e);}}}
}
上面这一段是在已有perent的情况下,断开parent和当前对象的联系,并确保在移除子对象时做了适当的清理和事件通知。实际上是为下面刷新parent做准备。parent = o;//更新parent//parent赋值后
if (parent) {// object hierarchies are constrained to a single thread// 对象层次结构受限于单个线程// 比较当前对象的线程数据和父对象的线程数据,如果它们不一致if (threadData != parent->d_func()->threadData) {qWarning("QObject::setParent: Cannot set parent, new parent is in a different thread");//父对象置空parent = nullptr;//直接返回return;}//将当前对象添加到父对象的子对象列表中。parent->d_func()->children.append(q);if(sendChildEvents && parent->d_func()->receiveChildEvents) {if (!isWidget) {//将这个事件发送给父对象QChildEvent e(QEvent::ChildAdded, q);QCoreApplication::sendEvent(parent, &e);}}
}
上面这一段是在设置对象的父对象后进行一些检查,确保父对象线程数据和该对象的一致,否则将parent设为nullptr,随后发送相应的ChildAdded事件给parent。
setParent_helper函数主要做了两件事:
1)确保旧parent安全撤离。
2)确保新parent正确设置。简单概括一下构造函数QObject()的内容:
1)新建QObjectPrivate并赋值给d_ptr。
2)赋值d_ptr->q_ptr为对象本身。
3)初始化threadData。
4)检查当前对象和parent是否在同一线程.
5)为当前对象和parent设置关联.
六、Q_DISABLE_COPY()
//qglobal.h
#define Q_DISABLE_COPY(Class) \Class(const Class &) = delete;\Class &operator=(const Class &) = delete;这里删除了拷贝构造函数和拷贝赋值操作符,确保QObject不能被拷贝构造或赋值。
觉得有帮助的话,打赏一下呗。。

相关文章:
Qt源码解析之QObject
省去大部分virtual和public方法后,Qobject主要剩下以下成员: //qobject.h class Q_CORE_EXPORT Qobject{Q_OBJECTQ_PROPERTY(QString objectName READ objectName WRITE setObjectName NOTIFY objectNameChanged)Q_DECLARE_PRIVATE(QObject) public:Q_I…...
【算法专题】模拟算法题
模拟算法题往往不涉及复杂的数据结构或算法,而是侧重于对特定情景的代码实现,关键在于理解题目所描述的情境,并能够将其转化为代码逻辑。所以我们在处理这种类型的题目时,最好要现在演草纸上把情况理清楚,再动手编写代…...
分库分表真的适合你的系统吗?
曾几何时,“并发高就分库,数据大就分表”已经成了处理 MySQL 数据增长问题的圣经。 面试官喜欢问,博主喜欢写,候选人也喜欢背,似乎已经形成了一个闭环。 但你有没有思考过,分库分表真的适合你的系统吗&am…...
9 redis,memcached,nginx网络组件
课程目标: 1.网络模块要处理哪些事情 2.reactor是怎么处理这些事情的 3.reactor怎么封装 4.网络模块与业务逻辑的关系 5.怎么优化reactor? io函数 函数调用 都有两个作用:io检测 是否就绪 io操作 1. int clientfd = accept(listenfd, &addr, &len); 检测 全连接队列…...
【MySQL】事务四大特性以及实现原理
事务四大特性 原子性(Atomicity) 事务中的所有操作要么全部完成,要么全部不执行。如果事务中的任何一步失败,整个事务都会被回滚,以保持数据的完整性。 一致性(Consistency) 事务应确保数据库…...
【控制Android.bp的编译】
1.首先Android.bp的语法是不支持if 条件语句的 2.查到可以用enabled来控制Android.bp中的模块是否参与编译,但是并不能实现动态的控制,比如你需要根据获取到的安卓版本来控制一个Android.bp是否编译,是无法做到的。enabled只能是固定的true或…...
【车载开发系列】J-Link/JFlash 简介与驱动安装方法
【车载开发系列】J-Link/JFlash 简介与驱动安装方法 【车载开发系列】J-Link/JFlash 简介与驱动安装方法 【车载开发系列】J-Link/JFlash 简介与驱动安装方法一. 软件介绍二. 下载安装包二. 开始安装三. 确认安装四. J-Flash的使用 一. 软件介绍 J-Link是SEGGER公司为支持仿真…...
207 课程表
题目 你这个学期必须选修 numCourses 门课程,记为 0 到 numCourses - 1 。 在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出,其中 prerequisites[i] [ai, bi] ,表示如果要学习课程 ai 则 必须 先学习课程 bi 。 …...
罗剑锋的C++实战笔记学习(一):const、智能指针、lambda表达式
1、const 1)、常量 const一般的用法就是修饰变量、引用、指针,修饰之后它们就变成了常量,需要注意的是const并未区分出编译期常量和运行期常量,并且const只保证了运行时不直接被修改 一般的情况,const放在左边&…...
宁德时代天行发布,商用车超充时代来临
近日,宁德时代正式推出商用动力电池品牌——“宁德时代天行”,同时发布“宁德时代天行轻型商用车(L)-超充版”和“宁德时代天行轻型商用车(L)-长续航版”两款产品,可实现4C超充能力和500km的实况…...
硅纪元应用评测 | 弱智吧大战GPT4o和Claude 3.5 Sonnet
"硅纪元AI应用测评"栏目,深入解析和评测最新的人工智能应用,提供专业见解和实用建议。不论您是AI专家还是科技爱好者,都能找到权威、详尽的测评,帮助您在快速发展的AI领域中做出最佳选择。一起探索AI的真实潜力…...
注意力机制 attention Transformer 笔记
动手学深度学习 这里写自定义目录标题 注意力加性注意力缩放点积注意力多头注意力自注意力Transformer 注意力 注意力汇聚的输出为值的加权和 查询的长度为q,键的长度为k,值的长度为v。 q ∈ 1 q , k ∈ 1 k , v ∈ R 1 v {\bf{q}} \in {^{1 \times…...
开始尝试从0写一个项目--后端(二)
实现学生管理 新增学生 接口设计 请求路径:/admin/student 请求方法:POST 请求参数:请求头:Headers:"Content-Type": "application/json" 请求体:Body: id 学生id …...
【图解大数据技术】Hive、HBase
【图解大数据技术】Hive、HBase Hive数据仓库Hive的执行流程Hive架构数据导入Hive HBaseHBase简介HBase架构HBase的列式存储HBase建表流程HBase数据写入流程HBase数据读取流程 Hive Hive是基于Hadoop的一个数据仓库工具,Hive的数据存储在HDFS上,底层基于…...
composables 目录下的文件(web前端)
composables 目录通常用于存放可组合的函数或逻辑,这些函数或逻辑可以在不同的组件中复用。具体来说,composables 目录下的文件通常包含以下内容: 组合式函数 (Composable Functions): 这些函数利用 Vue 3 的组合式 API࿰…...
使用Python绘制堆积柱形图
使用Python绘制堆积柱形图 堆积柱形图效果代码 堆积柱形图 堆积柱形图(Stacked Bar Chart)是一种数据可视化图表,用于显示不同类别的数值在某一变量上的累积情况。每一个柱状条显示多个子类别的数值,子类别的数值在柱状条上堆积在…...
DP:二维费用背包问题
文章目录 🎵二维费用背包问题🎶引言🎶问题定义🎶动态规划思想🎶状态定义和状态转移方程🎶初始条件和边界情况 🎵例题🎶1.一和零🎶2.盈利计划 🎵总结 …...
C语言标准库中的函数
由于C语言标准库中的函数非常多,我将按类别列出一些常见函数及其作用。请注意,这里不可能列出所有函数,但我会尽量覆盖主要的类别和函数。 ### 标准输入输出 - printf: 格式化输出到标准输出(通常是屏幕)。 - scanf: …...
Qt5.9.9 关于界面拖动导致QModbusRTU(QModbusTCP没有测试过)离线的问题
问题锁定 参考网友的思路: Qt5.9 Modbus request timeout 0x5异常解决 网友认为是Qt的bug, 我也认同;网友认为可以更新模块, 我也认同, 我也编译了Qt5.15.0的code并成功安装到Qt5.9.9中进行使用,界面拖…...
API的定义理解
前言 在程序员的日常工作中,“API”这个词在程序员的口中重复的次数,绝对是名列前茅的。 但是对刚开始工作的新人来说,API这个概念还是比较模糊。 确实,API这个概念是随着语义环境而不一样的,所以会让人迷惑。 下面…...
uniapp 对接腾讯云IM群组成员管理(增删改查)
UniApp 实战:腾讯云IM群组成员管理(增删改查) 一、前言 在社交类App开发中,群组成员管理是核心功能之一。本文将基于UniApp框架,结合腾讯云IM SDK,详细讲解如何实现群组成员的增删改查全流程。 权限校验…...
树莓派超全系列教程文档--(62)使用rpicam-app通过网络流式传输视频
使用rpicam-app通过网络流式传输视频 使用 rpicam-app 通过网络流式传输视频UDPTCPRTSPlibavGStreamerRTPlibcamerasrc GStreamer 元素 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 使用 rpicam-app 通过网络流式传输视频 本节介绍来自 rpica…...
大数据零基础学习day1之环境准备和大数据初步理解
学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 (1)设置网关 打开VMware虚拟机,点击编辑…...
2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面
代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口(适配服务端返回 Token) export const login async (code, avatar) > {const res await http…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
python报错No module named ‘tensorflow.keras‘
是由于不同版本的tensorflow下的keras所在的路径不同,结合所安装的tensorflow的目录结构修改from语句即可。 原语句: from tensorflow.keras.layers import Conv1D, MaxPooling1D, LSTM, Dense 修改后: from tensorflow.python.keras.lay…...
【VLNs篇】07:NavRL—在动态环境中学习安全飞行
项目内容论文标题NavRL: 在动态环境中学习安全飞行 (NavRL: Learning Safe Flight in Dynamic Environments)核心问题解决无人机在包含静态和动态障碍物的复杂环境中进行安全、高效自主导航的挑战,克服传统方法和现有强化学习方法的局限性。核心算法基于近端策略优化…...
AI+无人机如何守护濒危物种?YOLOv8实现95%精准识别
【导读】 野生动物监测在理解和保护生态系统中发挥着至关重要的作用。然而,传统的野生动物观察方法往往耗时耗力、成本高昂且范围有限。无人机的出现为野生动物监测提供了有前景的替代方案,能够实现大范围覆盖并远程采集数据。尽管具备这些优势…...
MFC 抛体运动模拟:常见问题解决与界面美化
在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...
C语言中提供的第三方库之哈希表实现
一. 简介 前面一篇文章简单学习了C语言中第三方库(uthash库)提供对哈希表的操作,文章如下: C语言中提供的第三方库uthash常用接口-CSDN博客 本文简单学习一下第三方库 uthash库对哈希表的操作。 二. uthash库哈希表操作示例 u…...
