深入理解Qt属性系统[Q_PROPERTY]
Qt 属性系统是 Qt 框架中一个非常核心和强大的部分,它提供了一种标准化的方法来访问对象的属性。这一系统不仅使得开发者能够以一致的方式处理各种数据类型,还为动态属性的管理提供了支持,并与 Qt 的元对象系统紧密集成。在这篇文章中,我们将详细介绍 Qt 属性系统的概念、实现机制和使用方式,以及它如何帮助开发者提升应用程序的灵活性和可维护性。
Q_PROPERTY
声明属性的类必须要继承QObject
,并且使用Q_OBJECT
宏,然后通过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][CONSTANT][FINAL][REQUIRED])
基本结构
- type:属性的数据类型,支持以下类型:
- Qt内建类型:常见的Qt数据类型如
QString
、QDate
、QTime
、QDateTime
、QUrl
、QList
、QMap
、QStringList
、QChar
、QByteArray
等。 - 标准C++类型:
int
、bool
、double
、float
、char*
等。 - 枚举类型
- 自定义类型:自定义的类也可以作为属性的类型,但是要满足以下条件:
- 类必须被Qt的元对象系统所知晓,这通常意味着类需要使用
Q_DECLARE_METATYPE
宏声明。 - 类需要提供公共的构造函数、拷贝构造函数和赋值运算符,以便Qt能够在内部处理属性的复制和赋值。
- 如果要通过
QVariant
进行存储或传递,类必须注册到Qt的类型系统中,使用qRegisterMetaType<ClassName>()
。
- 类必须被Qt的元对象系统所知晓,这通常意味着类需要使用
- Qt内建类型:常见的Qt数据类型如
- name:属性的名称
注意:这里的
name
和具体的成员变量名可以不同,因为Q_PROPERTY
声明的属性不是具体的成员,属性是用来关联对应某个成员对象的。
访问函数
- READ:READ访问器函数,即读取属性值的函数。函数的返回值必须是属性对应类型或对该类型的
const
引用,函数体必须带const
修饰。函数示例如下:
type getNameFunction() const;
const type& getNameFunction() const;
- WRITE:WRITE访问器函数,即修改属性值的函数。函数的返回值必须是
void
类型,并且只接受一个参数,可以是属性对应的类型,也可以是指向该类型的指针或引用。函数示例如下:
void setNameFunction(type val);
void setNameFunction(const type& val);
- MEMBER:直接指定类中的成员变量作为属性的存储,代替使用单独的读写方法。可以与
READ
或WRITE
搭配使用来覆盖默认的成员访问。
可选修饰符
- RESET:指定一个函数用于重置属性到默认的初始状态。通常不带参数,没有返回值。
- NOTIFY:指定当属性值改变时要发出的信号。这在绑定和数据驱动的界面更新中非常有用。
- REVISION:用于版本控制,指明属性从哪个Qt版本开始可用。这主要用于与QML集成时的版本控制。
- DESIGNABLE:指定属性是否应该在Qt设计器中显示,默认为
true
。 - SCRIPTABLE:指定属性是否可以通过脚本访问,默认为
true
。 - STORED:指明属性是否应该被序列号,默认为
true
。如果属性是计算出来的,并不需要存储,可以设置为false
。 - USER:指明这个属性是否被指定为该类的面向用户的属性或用户可编辑的属性。通常,每个类只有一个USER属性,默认为
false
。 - CONSTANT:标记属性为常量,一旦在构造函数中设置后不能改变。常用于只读的配置值。
- FINAL:指明这个属性在派生类中不能被重写。
- REQUIRED:属性的存在表明该属性应由该类的用户设置。这不是由 moc 强制执行的,并且对于暴露于 QML 的类来说最有用。在 QML 中,除非设置了所有必需属性,否则无法实例化具有必需属性的类。
使用示例
注意:
- 使用
READ
和WRITE
两个访问器函数在未指定MEMBER
时需要显式定义- 在指定
MEMBER
时可以不用显式定义READ
和WRITE
两个访问器函数READ
和WRITE
两个访问器函数主要的作用不是显式调用相应的函数,而是通过对象的property()
和setProperty()
来调用
只读属性
class Data : public QObject {Q_OBJECTQ_PROPERTY(QString name READ name FINAL)
public:explicit Data(QObject* parent = nullptr): QObject(parent){}QString name() const{return m_name;}private:QString m_name { "Data" };
};int main(int argc, char* argv[])
{QCoreApplication a(argc, argv);Data data;// 使用 property() 来获取属性值qDebug() << "Initial name:" << data.property("name").toString();// 使用 setProperty() 来修改属性值data.setProperty("name", "Updated Name");qDebug() << "Updated name:" << data.property("name").toString();return a.exec();
}
输出结果:
Initial name: "Data"
Updated name: "Data"
可读写属性
class Data : public QObject {Q_OBJECTQ_PROPERTY(QString name READ name WRITE setName FINAL)
public:explicit Data(QObject* parent = nullptr): QObject(parent){}QString name() const{return m_name;}void setName(const QString& name){if (name != m_name)m_name = name;}private:QString m_name { "Data" };
};int main(int argc, char* argv[])
{QCoreApplication a(argc, argv);Data data;// 使用 property() 来获取属性值qDebug() << "Initial name:" << data.property("name").toString();// 使用 setProperty() 来修改属性值data.setProperty("name", "Updated Name");qDebug() << "Updated name:" << data.property("name").toString();return a.exec();
}
输出结果:
Initial name: "Data"
Updated name: "Updated Name"
MEMBER关键字
class Data : public QObject {Q_OBJECTQ_PROPERTY(QString name MEMBER m_name FINAL)
public:explicit Data(QObject* parent = nullptr): QObject(parent){}private:QString m_name { "Data" };
};int main(int argc, char* argv[])
{QCoreApplication a(argc, argv);Data data;// 使用 property() 来获取属性值qDebug() << "Initial name:" << data.property("name").toString();// 使用 setProperty() 来修改属性值data.setProperty("name", "Updated Name");qDebug() << "Updated name:" << data.property("name").toString();return a.exec();
}
输出结果:
Initial name: "Data"
Updated name: "Updated Name"
使用
MEMBER
关键字时,用户无法通过实例对象显式调用getter
和setter
函数,只能通过property()
和setProperty()
来获取和设置。
NOTIFY关键字的使用
class Data : public QObject {Q_OBJECTQ_PROPERTY(QString name MEMBER m_name NOTIFY nameChanged FINAL)
public:explicit Data(QObject* parent = nullptr): QObject(parent){}signals:void nameChanged(const QString& name);private:QString m_name { "Data" };
};int main(int argc, char* argv[])
{QCoreApplication a(argc, argv);Data data;QObject::connect(&data, &Data::nameChanged, [&data](const QString& name) {qDebug() << "nameChanged: " << data.property("name");});// 使用 property() 来获取属性值qDebug() << "Initial name:" << data.property("name").toString();// 使用 setProperty() 来修改属性值data.setProperty("name", "Updated Name");qDebug() << "Updated name:" << data.property("name").toString();return a.exec();
}
输出结果:
Initial name: "Data"
nameChanged: QVariant(QString, "Updated Name")
Updated name: "Updated Name"
属性系统的主要应用场景
C++和QML交互
首先,我们需要定义一个包含可观察属性的C++类。此类将发出属性变化的信号,QML界面将响应这些信号更新显示。
CppBackend.h
#ifndef CPPBACKEND_H
#define CPPBACKEND_H#include <QObject>
#include <QString>class CppBackend : public QObject {Q_OBJECTQ_PROPERTY(QString message READ message WRITE setMessage NOTIFY messageChanged)public:explicit CppBackend(QObject *parent = nullptr) : QObject(parent), m_message("Hello from C++!") {}QString message() const { return m_message; }public slots:void setMessage(const QString &message) {if (m_message != message) {m_message = message;emit messageChanged();}}signals:void messageChanged();private:QString m_message;
};#endif // CPPBACKEND_H
接下来,我们创建一个QML文件来显示message
属性,并提供一个按钮来改变它。
main.qml
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15Window {width: 640height: 480visible: truetitle: qsTr("Hello World")Column {anchors.centerIn: parentspacing: 20Text {id: displayTexttext: backend.messagefont.pointSize: 20}Button {text: "Change Message"onClicked: {backend.message = "Updated by QML!"}}}
}
在主程序中,我们将注册后端对象为QML可访问,并加载QML文件。
main.cpp
#include "CppBackend.h"
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>int main(int argc, char* argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endifQGuiApplication app(argc, argv);QQmlApplicationEngine engine;CppBackend backend;engine.rootContext()->setContextProperty("backend", &backend);const QUrl url(QStringLiteral("qrc:/main.qml"));QObject::connect(&engine,&QQmlApplicationEngine::objectCreated,&app,[url](QObject* obj, const QUrl& objUrl) {if (!obj && url == objUrl)QCoreApplication::exit(-1);},Qt::QueuedConnection);engine.load(url);return app.exec();
}
属性树形控件
属性树形控件的类虽然已经被移除,但QtCreator还是在使用的,可以通过Qt的源码查找qtpropertybrowser
。
展示使用的代码:
TestData1.h
#ifndef TESTDATA1_H
#define TESTDATA1_H#include <QColor>
#include <QObject>class TestData1 : public QObject {Q_OBJECTQ_PROPERTY(int count READ count WRITE setCount NOTIFY countChanged FINAL)Q_PROPERTY(QColor bkColor READ bkColor WRITE setBkColor NOTIFY bkColorChanged FINAL)Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged FINAL)
public:explicit TestData1(QObject* parent = nullptr): QObject(parent){}int count() const { return m_count; }void setCount(int value){if (value != m_count) {m_count = value;emit countChanged(m_count);}}QColor bkColor() const { return m_bkColor; }void setBkColor(const QColor& color){if (color != m_bkColor) {m_bkColor = color;emit bkColorChanged(m_bkColor);}}QString name() const { return m_name; }void setName(const QString& value){if (value != m_name) {m_name = value;emit nameChanged(m_name);}}signals:void countChanged(int value);void bkColorChanged(const QColor& color);void nameChanged(const QString& name);private:int m_count { 1 };QColor m_bkColor { Qt::black };QString m_name { "TestData" };
};#endif // TESTDATA1_H
TestData2.h
#ifndef TESTDATA2_H
#define TESTDATA2_H#include <QColor>
#include <QObject>class TestData2 : public QObject {Q_OBJECTQ_PROPERTY(int number READ count WRITE setCount NOTIFY countChanged FINAL)Q_PROPERTY(QColor bkColor READ bkColor WRITE setBkColor NOTIFY bkColorChanged FINAL)Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged FINAL)
public:explicit TestData2(QObject* parent = nullptr): QObject(parent){}int count() const { return m_number; }void setCount(int value){if (value != m_number) {m_number = value;emit countChanged(m_number);}}QColor bkColor() const { return m_bkColor; }void setBkColor(const QColor& color){if (color != m_bkColor) {m_bkColor = color;emit bkColorChanged(m_bkColor);}}QString name() const { return m_name; }void setName(const QString& value){if (value != m_name) {m_name = value;emit nameChanged(m_name);}}signals:void countChanged(int value);void bkColorChanged(const QColor& color);void nameChanged(const QString& name);private:int m_number { 99 };QColor m_bkColor { Qt::blue };QString m_name { "TestData2" };
};
#endif // TESTDATA2_H
widget.h
#ifndef WIDGET_H
#define WIDGET_H#include "qtpropertybrowser/qteditorfactory.h"
#include "qtpropertybrowser/qttreepropertybrowser.h"
#include "qtpropertybrowser/qtvariantproperty.h"
#include "testdata1.h"
#include "testdata2.h"
#include <QListWidgetItem>
#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_listWidget_currentItemChanged(QListWidgetItem* current, QListWidgetItem* previous);protected:void updateProperties(QObject* selectedObject);private:Ui::Widget* ui;QtVariantPropertyManager* m_variantManager;QtVariantEditorFactory* m_variantFactory;TestData1 m_data1;TestData1 m_data1_2;TestData2 m_data2;TestData2 m_data2_2;
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "./ui_widget.h"
#include <QMetaProperty>Widget::Widget(QWidget* parent): QWidget(parent), ui(new Ui::Widget), m_variantManager(new QtVariantPropertyManager(this)), m_variantFactory(new QtVariantEditorFactory(this))
{ui->setupUi(this);m_data1.setName("TestData1");m_data1.setBkColor(Qt::red);m_data1_2.setName("TestData1_2");m_data1_2.setBkColor(Qt::yellow);m_data2.setCount(60);ui->widget->setFactoryForManager(m_variantManager, m_variantFactory);QtVariantProperty* item = m_variantManager->addProperty(QVariant::Double, "透明度");item->setValue(0.5);ui->widget->addProperty(item);
}Widget::~Widget()
{delete ui;
}void Widget::on_listWidget_currentItemChanged(QListWidgetItem* current, QListWidgetItem* previous)
{static int index = 0;int i = index % 4;if (i == 0)updateProperties(&m_data1);else if (i == 1)updateProperties(&m_data1_2);else if (i == 2)updateProperties(&m_data2);elseupdateProperties(&m_data2_2);index++;
}void Widget::updateProperties(QObject* selectedObject)
{ui->widget->clear();auto metaObj = selectedObject->metaObject();for (int i = 0; i < metaObj->propertyCount(); ++i) {auto propertyObj = metaObj->property(i);auto propertyName = propertyObj.name();auto propertyValue = selectedObject->property(propertyName);auto item = m_variantManager->addProperty(propertyValue.type(), propertyName);item->setValue(propertyValue);ui->widget->addProperty(item);connect(m_variantManager, &QtVariantPropertyManager::valueChanged,this, [selectedObject](QtProperty* prop, const QVariant& value) {selectedObject->setProperty(prop->propertyName().toStdString().c_str(), value);});}
}
注意:
ui->widget
就是QtTreePropertyBrowser
类。
效果:
总结
属性系统说到底就是建立在类的成员变量之上,并通过标准化的接口(getter和setter)以及元对象系统来增强这些成员的功能。
在很多UI和数据进行交互的时候,我们通常可以通过属性系统提供的标准化接口来实现,因为其带来以下好处:
- 一致性和易用性
提供一个统一的方法来访问和修改对象的属性,这意味着无论你在哪里或如何使用这些对象,访问和修改属性的方式总是相同的。这种一致性减少了学习和使用不同对象时的认知负担,使得开发更直观,同时也减少了代码中可能出现的错误。
- 封装和数据保护
通过标准化的 getter 和 setter 方法来访问和修改数据,这增强了封装性,保护了数据不被非法访问和修改。封装是面向对象编程中的一个核心概念,它隐藏了对象的内部状态和实现细节,只暴露有限的接口与外界交互。
- 解耦和模块化
标准化接口促进了解耦和模块化设计。开发者可以更轻松地替换或修改内部实现而不影响使用这些对象的代码。这是因为外部代码依赖于接口而非具体实现,从而使得整个系统更加灵活和可维护。
- 自动化工具和库的集成
标准化的属性接口使得自动化工具(如 GUI 设计器)和其他库(如序列化库、数据库映射工具)能够更容易地与你的代码集成。例如,一个自动化工具可以通过反射机制查找所有属性并允许用户在图形界面中配置它们。
- 动态性和反射能力
Qt 的元对象系统依赖于标准化的属性接口来支持反射,即在运行时查询和操作对象的能力。这不仅有助于开发各种动态特性(如动态数据绑定和脚本集成),也使得开发者能够编写通用代码来处理不同类型的对象。
- 跨语言和平台兼容性
标准化接口简化了在不同编程语言和平台间的对象交互。由于接口提供了清晰定义的交互点,因此可以更容易地将 Qt 应用与其他系统集成,或者在不同平台间迁移应用。
相关文章:

深入理解Qt属性系统[Q_PROPERTY]
Qt 属性系统是 Qt 框架中一个非常核心和强大的部分,它提供了一种标准化的方法来访问对象的属性。这一系统不仅使得开发者能够以一致的方式处理各种数据类型,还为动态属性的管理提供了支持,并与 Qt 的元对象系统紧密集成。在这篇文章中&#x…...
【C语言课程设计】员工信息管理系统
员工信息管理系统 在日常的企业管理中,员工信息的管理显得尤为重要。为了提高员工信息管理的效率,我们设计并实现了一个简单的员工信息管理系统。该系统主要使用C语言编写,具备输入、显示、查询、更新(增加、删除、修改ÿ…...

「动态规划」如何求最长递增子序列的长度?
300. 最长递增子序列https://leetcode.cn/problems/longest-increasing-subsequence/description/ 给你一个整数数组nums,找到其中最长严格递增子序列的长度。子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其…...
深度神经网络DNN概念科普
深度神经网络DNN概念科普 深度神经网络(Deep Neural Network, DNN)是机器学习领域中一类具有多层结构的神经网络模型,它能够通过学习数据中的复杂模式来解决非线性问题。下面是对深度神经网络的详细解析: 基本组成部分 输入层&…...

Tomcat WEB站点部署
目录 1、使用war包部署web站点 2、自定义默认网站目录 3、部署开源站点(jspgou商城) 对主机192.168.226.22操作 对主机192.168.226.20操作 上线的代码有两种方式: 第一种方式是直接将程序目录放在webapps目录下面,这种方式…...
IPv6 中 MAC 33:33 的由来
一、33:33 由来 1. RFC9542 - 2024-05-02 Note IANA allocates addresses under the IANA OUI (00-00-5E) as explained in [RFC9542]. Unicast addresses under the IANA OUI start with 00-00-5E, while multicast addresses under the IANA OUI start with 01-00-5E. In t…...

告别手动邮件处理:使用imbox库轻松管理你的收件箱
imbox库简介: imbox是一个强大的Python库,专为与IMAP服务器交互而设计.IMAP(Internet Message Access Protocol)是一种用于电子邮件的标准协议,允许用户在远程服务器上管理邮件.imbox库通过IMAP协议与邮件服务器通信,帮助用户轻松地读取、搜索…...

Ubuntu 18.04 安装 PCL 1.14.1
在进行科研项目时,我们常常需要将 C 和 Python 结合起来编程。然而,每次将 PCL(Point Cloud Library)的内容添加到 CMakeLists.txt 文件中时都会报错。在深入分析后,我们推测可能是当前使用的 PCL 1.8 版本与现有程序不…...

公司logo设计大全怎么找?直接帮你设计logo
公司logo设计大全怎么找?在品牌塑造的过程中,Logo无疑是至关重要的一环。一个优秀的Logo不仅能够有效传达公司的核心理念和品牌形象,还能在消费者心中留下深刻的印象。然而,对于许多初创公司或小型企业来说,制作出适合…...
如何调整C#中数组的大小
前言 数组存储多个相同类型的一种非常常用的数据结构。它长度是固定,也就是数组一旦创建大小就固定了。C# 数组不支持动态长度。那在C#中是否有方法可以调整数组大小呢?本文将通过示例介绍一种调整一维数组大小的方法。 方法 数组实例是从 System.Arr…...

通过言语和非言语检索线索描绘睡眠中的记忆再激活茗创科技茗创科技
摘要 睡眠通过重新激活新形成的记忆痕迹来巩固记忆。研究睡眠中记忆再激活的一种方法是让睡眠中的大脑再次暴露于听觉检索线索(定向记忆再激活范式)。然而,记忆线索的声学特性在多大程度上影响定向记忆再激活的有效性,目前还没有得到充分探索。本研究通…...
MDPI旗下SSCI最新影响因子目录出炉!“水刊“Sustainability表现如何?
本周投稿推荐 SSCI • 1区,4.0-5.0(无需返修,提交可录) EI • 各领域沾边均可(2天录用) CNKI • 7天录用-检索(急录友好) SCI&EI • 4区生物医学类,0.1-0.5&…...

Matlab基础篇:数据输入输出
前言 数据输入和输出是 Matlab 数据分析和处理的核心部分。良好的数据输入输出能够提高工作效率,并确保数据处理的准确性。本文将详细介绍 Matlab 数据输入输出的各种方法,包括导入和导出数据、数据处理和数据可视化。 一、导入数据 Matlab 提供了多种方…...

MySQL字典数据库设计与实现 ---项目实战
软件准备✍:Mysql与Navicat可视化命令大全 ----项目实战 文章前言部分 目录 一.摘要 二.设计内容 三.项目实现 一.摘要 本项目关注于字典数据库表结构的设计和数据管理。通过现有的sql文件,实现system_dict_type和system_dict_data两个数据表。随后…...

python数据分析——数据预处理
数据预处理 前言一、查看数据数据表的基本信息查看info()示例 查看数据表的大小shape()示例 数据格式的查看type()dtype()dtypes()示例一示例二 查看具体的数据分布describe()示例 二…...

【Python】使用matplotlib绘制图形(曲线图、条形图、饼图等)
文章目录 一、什么是matplotlib二、matplotlib 支持的图形三、如何使用matplotlib1. 安装matplotlib2. 导入matplotlib.pyplot3. 准备数据4. 绘制图形5. 定制图形6. 显示或保存图形7. (可选)使用subplots创建多个子图注意事项: 四、常见图形使…...
vue下载本地xls模版静态文件
需求导入的下载模版不想放在服务器放在前端本地下载静态资源最简单的方式直接访问 public 文件夹下的文件 方法一:使用静态文件路径 将文件放在 public 文件夹中: 把你的文件从 src/assets 移动到 public 文件夹。例如:public/template.xls。…...
手机开热点,里面的WPA2-Personal和WPA3-Personal的区别
WPA2-Personal和WPA3-Personal这两种协议都是用来保护无线网络安全的,但它们在加密强度和安全性方面有所不同。 WPA2-Personal (Wi-Fi Protected Access 2) WPA2是目前最广泛使用的Wi-Fi安全标准之一。它使用AES(Advanced Encryption Standard…...

算法课程笔记——点积叉积
算法课程笔记——点积叉积...

详解 | DigiCert EV代码签名证书
简介 DigiCert EV 代码签名证书是一种高级别的代码签名证书,它不仅提供了标准代码签名证书的所有安全特性,还增加了额外的身份验证流程,以确保软件开发者或发布者的身份得到最严格验证。这对于提升软件的信任度、防止恶意篡改和确保下载安全…...

地震勘探——干扰波识别、井中地震时距曲线特点
目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波:可以用来解决所提出的地质任务的波;干扰波:所有妨碍辨认、追踪有效波的其他波。 地震勘探中,有效波和干扰波是相对的。例如,在反射波…...

高等数学(下)题型笔记(八)空间解析几何与向量代数
目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...

Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级
在互联网的快速发展中,高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司,近期做出了一个重大技术决策:弃用长期使用的 Nginx,转而采用其内部开发…...

MySQL 8.0 OCP 英文题库解析(十三)
Oracle 为庆祝 MySQL 30 周年,截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始,将英文题库免费公布出来,并进行解析,帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...

Golang——9、反射和文件操作
反射和文件操作 1、反射1.1、reflect.TypeOf()获取任意值的类型对象1.2、reflect.ValueOf()1.3、结构体反射 2、文件操作2.1、os.Open()打开文件2.2、方式一:使用Read()读取文件2.3、方式二:bufio读取文件2.4、方式三:os.ReadFile读取2.5、写…...

从“安全密码”到测试体系:Gitee Test 赋能关键领域软件质量保障
关键领域软件测试的"安全密码":Gitee Test如何破解行业痛点 在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的"神经中枢"。从国防军工到能源电力,从金融交易到交通管控,这些关乎国计民生的关键领域…...

论文阅读笔记——Muffin: Testing Deep Learning Libraries via Neural Architecture Fuzzing
Muffin 论文 现有方法 CRADLE 和 LEMON,依赖模型推理阶段输出进行差分测试,但在训练阶段是不可行的,因为训练阶段直到最后才有固定输出,中间过程是不断变化的。API 库覆盖低,因为各个 API 都是在各种具体场景下使用。…...
用神经网络读懂你的“心情”:揭秘情绪识别系统背后的AI魔法
用神经网络读懂你的“心情”:揭秘情绪识别系统背后的AI魔法 大家好,我是Echo_Wish。最近刷短视频、看直播,有没有发现,越来越多的应用都开始“懂你”了——它们能感知你的情绪,推荐更合适的内容,甚至帮客服识别用户情绪,提升服务体验。这背后,神经网络在悄悄发力,撑起…...

qt+vs Generated File下的moc_和ui_文件丢失导致 error LNK2001
qt 5.9.7 vs2013 qt add-in 2.3.2 起因是添加一个新的控件类,直接把源文件拖进VS的项目里,然后VS卡住十秒,然后编译就报一堆 error LNK2001 一看项目的Generated Files下的moc_和ui_文件丢失了一部分,导致编译的时候找不到了。因…...
Yii2项目自动向GitLab上报Bug
Yii2 项目自动上报Bug 原理 yii2在程序报错时, 会执行指定action, 通过重写ErrorAction, 实现Bug自动提交至GitLab的issue 步骤 配置SiteController中的actions方法 public function actions(){return [error > [class > app\helpers\web\ErrorAction,],];}重写Error…...