【CXX-Qt】4.1 extern “RustQt“
-
QObjects
-
Properties
+Methods
- Signals
#[cxx_qt::bridge]
mod ffi {extern "RustQt" {}
}
extern “RustQt” 部分是 CXX-Qt 桥接的核心,用于声明 Rust 类型和签名,使其可用于 Qt 和 C++。
CXX-Qt 代码生成器使用你的 extern “RustQt” 部分生成一个包含相应 C++ 声明的 C++ 头文件。生成的头文件与输入的 Rust 文件同名,但扩展名为 .cxxqt.h。
一个桥接模块可以包含零个或多个 extern “RustQt” 块。
这补充了 extern “Rust” CXX 部分,但允许在 C++ 类型上声明 Qt 特定的功能。
可以通过块级属性自动转换为驼峰命名或蛇形命名。
QObjects
#[qobject] 属性可以放在类型别名上,以在 C++ 中生成一个 QObject 类型。
类型别名的左侧指定在 C++ 中生成的 QObject 类型。在引用 C++ 上下文时,应使用此类型。类型别名的右侧指定提供类型内部实现的 Rust 类型(例如字段)。
#[cxx_qt::bridge]
mod ffi {extern "RustQt" {#[qobject]type MyObject = super::MyObjectRust;}
}#[derive(Default)]
struct MyObjectRust;
📝 注意:目前,只有 `super::` 允许作为内部 Rust 类型的路径。因此,Rust 类型必须在桥接模块外部可用。如果你想重用现有类型,可以使用 `pub use` 指令将任何类型引入作用域。
QML 属性
通过使用 #[qml_element] 属性,可以在构建时直接将 QObject 注册为 QML 类型。
unsafe extern "RustQt" {// QObject 定义// 我们告诉 CXX-Qt 我们想要一个名为 MyObject 的 QObject 类// 基于 Rust 结构体 MyObjectRust。#[qobject]#[qml_element]#[qproperty(i32, number)]#[qproperty(QString, string)]#[namespace = "my_object"]type MyObject = super::MyObjectRust;
}
此外,你可以使用以下属性配置 QML 注册:
-
#[qml_element]:将类型声明为 QML 元素。可以使用替代类型名称,例如 #[qml_element = “MyName”]。
-
#[qml_uncreatable]:标记该类型无法从 QML 创建。它仍然可以通过 C++/Rust 代码返回。
-
#[qml_singleton]:QObject 的实例将作为单例在 QML 中实例化。
Rust 文件必须包含在 build.rs 文件中的 QML 模块中。
base 属性
使用 base 属性指定 C++ 类,C++ QObject 将从该类继承。基类必须直接或间接继承自 QObject。如果未指定 base 属性,它将直接继承自 QObject。
extern "RustQt" {#[qobject]#[base = "QAbstractListModel"]#[qml_element]#[qproperty(State, state)]type CustomBaseClass = super::CustomBaseClassRust;
}
使用 CXX 的 include! 宏包含基类的适当 C++ 头文件:
unsafe extern "C++" {include!(<QtCore/QAbstractListModel>);/// Qt 类型的基类type QAbstractListModel;
}
有关继承和如何重写方法的更多信息,请参阅 继承与重写 页面。
完整示例
Traits
#[qobject] 标记的结构体需要实现 Default trait,可以通过手动实现或使用 #[derive(Default)] 宏。或者,类型需要实现 cxx_qt::Constructor trait。
有关更多文档,请参阅 traits 页面。
属性
可以在 #[qobject] 标记的类型上指定 #[qproperty(TYPE, NAME, …)] 属性,以在生成的 QObject 上暴露一个 Q_PROPERTY。
unsafe extern "RustQt" {// QObject 定义// 我们告诉 CXX-Qt 我们想要一个名为 MyObject 的 QObject 类// 基于 Rust 结构体 MyObjectRust。#[qobject]#[qml_element]#[qproperty(i32, number)]#[qproperty(QString, string)]#[namespace = "my_object"]type MyObject = super::MyObjectRust;
}
如果未在属性上指定其他属性,CXX-Qt 将自动生成 setter 和 getter,以及一个 “changed” 信号。属性的类型和名称必须与内部 Rust 结构体中的字段匹配。
#[derive(Default)]
pub struct MyObjectRust {number: i32,string: QString,
}
CXX-Qt 将生成以下函数:
| C++ | Rust | |
|---|---|---|
| setter | set | set_ |
| getter | get | |
| changed signal | Changed | _changed |
与任何信号一样,CXX-Qt 将在 Rust 端生成相应的连接函数:
-
connect: connect__changed
-
on: on__changed
其中 是属性的名称。
这些 setter 和 getter 确保每次编辑属性时都会发出 changed 信号。
自定义属性
如果自动生成的函数不适用于你的用例,你可以禁用 CXX-Qt 的自动生成并编写完全自定义的属性。例如,如果你的属性不对应于内部 Rust 结构体中的任何单个字段,则可能需要这样做。
你可以使用标志指定自定义 getter、setter 和通知信号,例如:#[qproperty(TYPE, NAME, READ = myGetter, WRITE = mySetter, NOTIFY = myOnChanged)]。
📝 注意:标志的键使用全大写字母,如 Qt 版本的 `qproperty`。
可以组合使用标志或完全省略某些标志,但如果指定了任何标志,则必须包含 READ 标志。
如果为标志指定了自定义函数,则必须在桥接中声明该函数,并且必须存在相应的实现。
某些标志可以在不指定函数的情况下传递(例如 READ 和 READ=…)。对于这些标志,如果未提供函数,CXX-Qt 将自动生成实现,如上一节所述。例如,#[qproperty(i32, num, READ)] 将自动生成一个名为 get_num 的 getter 函数(在 Rust 中)和 getNum(在 C++ 中)。因此,#[qproperty(i32, num)] 只是 #[qproperty(i32, num, READ, WRITE, NOTIFY)] 的简写。
此外,可以像其他项目上的属性一样使用 cxx_name 和 rust_name。例如,#[qproperty(i32, num, cxx_name = “numberProp”)]。
示例
-
#[qproperty(TYPE, NAME, READ)]:具有自动生成的 getter 的只读属性。
-
#[qproperty(TYPE, NAME, READ = myGetter, WRITE, NOTIFY)]:提供自定义 getter,但自动生成 setter 和 changed 信号。
-
#[qproperty(TYPE, NAME)]:是 #[qproperty(TYPE, NAME, READ, WRITE, NOTIFY)] 的简写。
-
#[qproperty(TYPE, NAME, WRITE)]:错误,因为需要 READ 标志。
可用标志
-
READ 或 READ = my_getter:指定属性应为可读的(如果传递了标志,则始终需要),带有可选的用户定义 getter。
-
WRITE 或 WRITE = my_setter:指定属性应为可写的,带有可选的用户定义 setter。
-
NOTIFY 或 NOTIFY = my_on_changed:指定属性应在更改时发出通知信号,带有可选的用户定义信号名称。
-
CONSTANT:指定属性应为常量(意味着 getter 对于该特定实例始终返回相同的值)。
CONSTANT 不可用于使用 WRITE 或 NOTIFY 的属性,并且不会编译。
-
REQUIRED:指定属性必须由类的用户设置,在 QML 中很有用,因为除非设置了属性,否则无法实例化类。
-
FINAL:指定属性不会被派生类覆盖。
-
RESET = my_reset:指定重置属性为默认值的函数,必须提供用户函数,否则不会编译。
-
cxx_name = “myCxxName”:指定在 C++ 端使用的替代名称,适用于属性名称以及自动生成的函数。
-
rust_name = “my_rust_name”:指定在 Rust 端使用的替代名称,适用于属性名称以及自动生成的函数。
方法
任何带有 self 参数的签名都被解释为 Rust 方法,并暴露给给定类型的 C++ 方法。类型必须是共享引用 self: &T 或固定可变引用 self: Pin<&mut T>,其中 T 是 QObject 类型。
unsafe extern "RustQt" {/// 仅 C++ 方法,返回红色值#[cxx_name = "redValue"]fn red_value(self: &RustInvokables) -> f32;
}
然后在桥接外部正常编写方法的实现。
impl qobject::RustInvokables {/// 仅 C++ 方法,返回红色值pub fn red_value(&self) -> f32 {self.red}
}
注意这里使用 `impl qobject::T` 而不是 `impl T`,其中 `qobject` 是桥接模块名称。
可调用方法
可以在签名上指定 #[qinvokable] 属性,以在 C++ 中将其暴露为 Q_INVOKABLE。
unsafe extern "RustQt" {/// 不可变的可调用方法,返回 QColor#[qinvokable]#[cxx_name = "loadColor"]fn load_color(self: &RustInvokables) -> Result<QColor>;/// 可变的可调用方法,存储颜色#[qinvokable]#[cxx_name = "storeColor"]fn store_color(self: Pin<&mut RustInvokables>, red: f32, green: f32, blue: f32);/// 可变的可调用方法,使用枚举存储颜色#[qinvokable]#[cxx_name = "storeColorWithEnum"]fn store_color_with_enum(self: Pin<&mut RustInvokables>, color: Color);/// 无可变参数的可调用方法,重置颜色#[qinvokable]fn reset(self: Pin<&mut RustInvokables>);
}
然后实现与非可调用方法没有区别。
impl qobject::RustInvokables {/// 不可变的可调用方法,返回 QColorpub fn load_color(&self) -> Result<QColor, i32> {Ok(self.as_qcolor())}/// 可变的可调用方法,存储颜色pub fn store_color(self: Pin<&mut Self>, red: f32, green: f32, blue: f32) {self.store_helper(red, green, blue);}/// QENUMS!pub fn store_color_with_enum(self: Pin<&mut Self>, color: qobject::Color) {use qobject::Color;let (r, g, b) = match color {Color::Red => (1.0, 0.0, 0.0),Color::Green => (0.0, 1.0, 0.0),Color::Blue => (0.0, 0.0, 1.0),_ => (0.0, 0.0, 0.0),};self.store_helper(r, g, b);}/// 无可变参数的可调用方法,重置颜色pub fn reset(self: Pin<&mut Self>) {self.store_helper(0.0, 0.4667, 0.7843);}
}
继承
可以通过 #[inherit] 属性访问基类上已存在的方法或信号。
有关文档,请参阅 继承页面。
说明符
生成的方法可以具有实现继承所需的 C++ 说明符。
| C++ 关键字 | CXX-Qt 属性 |
|---|---|
| override | #[cxx_override] |
| virtual | #[cxx_virtual] |
| final | #[cxx_final] |
这些说明符作为方法签名上的属性指定。
rust
unsafe extern “RustQt” {
#[qinvokable]
#[cxx_override]
fn data(self: &CustomBaseClass, index: &QModelIndex, role: i32) -> QVariant;
}
### 信号
qsignal 属性用于在 extern "RustQt" 块中为 QObject 定义信号。```rust
unsafe extern "RustQt" {/// 当连接发生时发出的 Q_SIGNAL#[qsignal]fn connected(self: Pin<&mut RustSignals>, url: &QUrl);/// 当断开连接发生时发出的 Q_SIGNAL#[qsignal]fn disconnected(self: Pin<&mut RustSignals>);/// 当发生错误时发出的 Q_SIGNAL#[qsignal]fn error(self: Pin<&mut RustSignals>, message: QString);
}
对于 extern 块中的每个函数签名,CXX-Qt 将在相应的 QObject 上生成一个信号。如果函数有参数,它们将成为相应信号的参数。信号函数不需要手动实现。
如果信号在 QObject 的基类上定义,则可以使用 #[inherit],这将导致 CXX-Qt 访问基类中现有的 Q_SIGNAL。
完整示例可以在 qml 特性示例 中找到。
📝 注意:可以在信号上使用 `#[cxx_name="..."]` 和 `#[rust_name="..."]` 来声明 C++ 和 Rust 中的不同名称。
📝 注意:使用 `pub(self)` 作为信号的可见性允许声明私有信号。
连接到信号
对于每个信号,CXX-Qt 将生成两个方法来连接到它。
-
on_<signal_name>
-
connect_<signal_name>
on_<signal_name> 方法将处理函数作为参数,当信号发出时将调用该函数。该处理函数的第一个参数是发出信号的 QObject,其余参数是信号参数。
connect_<signal_name> 函数还接受 Qt 连接类型作为参数。
let connections = [qobject.as_mut().on_connected(|_, url| {println!("Connected: {}", url);}),qobject.as_mut().on_disconnected(|_| {println!("Disconnected");}),// 演示使用不同连接类型进行连接qobject.as_mut().connect_error(|_, message| {println!("Error: {}", message);},ConnectionType::QueuedConnection,),
];
qobject.as_mut().rust_mut().connections = Some(connections);
每个连接返回一个 QMetaObjectConnectionGuard,它是 QMetaObject::Connection 的 RAII 包装器,并在守卫被丢弃时自动断开连接。这类似于 C++ 的 std::lock_guard、std::unique_ptr 或 Rust 的 Box。
示例:
// 通过将 connections 设置为 None,我们触发连接的丢弃
// 这将导致断开连接
qobject.as_mut().rust_mut().connections = None;
如果你不想存储 QMetaObjectConnectionGuard,可以调用 release,这将将其转换为内部的 QMetaObjectConnection,它是 QMetaObject::Connection 的直接包装器,不会在丢弃时断开连接。
📝 注意:`QMetaObjectConnection` 有一个 `disconnect` 方法,可以稍后手动调用。
发出信号
调用 extern “RustQt” 块中定义的函数签名以发出信号。
请注意,这些函数是在生成的 QObject qobject::T 上定义的,因此可以从任何可变的 #[qinvokable] 中调用。
该函数将立即发出信号。根据连接类型,连接的槽将立即调用或从事件循环中调用(参见不同的连接类型)。要将调用排队到 Qt 事件循环的下一个周期,可以使用 CxxQtThread。
信号继承
如果信号在 QObject 的基类上定义,则可以使用 #[inherit] 属性来指示 CXX-Qt 不需要在 C++ 中创建 Q_SIGNAL。
unsafe extern "RustQt" {/// 从 QAbstractListModel 基类继承 DataChanged 信号#[inherit]#[qsignal]#[cxx_name = "dataChanged"]fn data_changed(self: Pin<&mut CustomBaseClass>,top_left: &QModelIndex,bottom_right: &QModelIndex,roles: &QVector_i32,);
}
相关文章:
【CXX-Qt】4.1 extern “RustQt“
QObjects Properties Methods Signals #[cxx_qt::bridge] mod ffi {extern "RustQt" {} }extern “RustQt” 部分是 CXX-Qt 桥接的核心,用于声明 Rust 类型和签名,使其可用于 Qt 和 C。 CXX-Qt 代码生成器使用你的 extern “RustQt” 部…...
每日一题-力扣-2829. k-avoiding 数组的最小总和 0326
解决"k-avoiding 数组的最小总和"问题 这道题有两种主要解法。 解法一:直接数学计算(最优解) 通过数学推导直接计算出结果,不需要构建实际的数组。 class Solution:def minimumSum(self, n: int, k: int) -> int…...
React 中的错误边界(Error Boundaries),如何使用它们捕获组件错误
大白话React 中的错误边界(Error Boundaries),如何使用它们捕获组件错误 在 React 里,错误边界就像是一个“小卫士”,专门负责在组件出现错误时挺身而出,避免整个应用因为一个小错误就崩溃掉。接下来我会详…...
OSI模型_TCP/IP模型_五层模型
文章目录 OSI模型_TCP/IP模型_五层模型模型对比模型层级对比关键区别对比 OSI模型OSI模型概述举例说明流程图示 TCP/IP 四层模型模型结构举例说明流程图示 TCP/IP 五层模型模型的结构举例说明流程图示 OSI模型_TCP/IP模型_五层模型 学OSI,用TCP/IP,分析选…...
HarmonyOS Next应用架构设计与模块化开发详解
引言 在HarmonyOS Next开发中,合理的应用架构设计和模块化开发是构建高效、可维护应用的关键。本文将深入探讨HarmonyOS Next应用的架构设计思路,并通过实际代码示例展示如何实现模块化开发。 应用架构设计 HarmonyOS Next应用通常采用分层架构设计&…...
SpringCould微服务架构之Docker(2)
Docker和虚拟机的差别: 虚拟机是在操作系统中模拟硬件设备,然后运行另外一个操作系统。...
LINUX基础IO [六] - 文件理解与操作
目录 前言 C语言文件操作回顾 文件的打开与关闭 文件的增删改查 文件系统调用 比特位方式的标志位传递原理 访问文件的本质 文件描述符fd 理解文件描述符fd 三个流的理解 文件描述符的分配规则 重定向再理解 输出重定向 输入重定向 如何理解一切皆文件 理解…...
拥抱人工智能大模型时代:大模型会改变我们的生活吗?
在这个科技日新月异的时代,人工智能(AI)正以前所未有的速度改变着我们的生活和工作方式。尤其是随着人工智能大模型(如ChatGPT、DeepSeek等)的崛起,人们对于AI技术的期待和关注达到了前所未有的高度。那么&…...
常见框架漏洞攻略-ThinkPHP篇
漏洞名称:Thinkphp5x远程命令执行及getshell 第一步:开启靶场 第二步:准备工具 第三步:启动工具,进行漏洞检测 #存在漏洞 1.目标存在tp5_invoke_func_code_exec_1漏洞2.目标存在tp5_dbinfo_leak漏洞payload:http://47…...
若依框架二次开发——若依集成 JSEncrypt 实现密码加密传输方式
文章目录 一、问题场景二、相关技术介绍1. RSA 加密算法2. JSEncrypt三、实现步骤1. 前端加密处理2. 后端解密处理3. 登录逻辑处理四、测试流程1. 前端测试2. 后端测试3. 运行效果五、总结一、问题场景 在 RuoYi 系统中,默认情况下,用户在登录时会将明文密码直接传输到服务器…...
rabbitmq承接MES客户端服务器
文章目录 背景整体架构概述方案详细步骤1. 数据库选型与搭建2. 设备端数据上传至数据库3. 搭建 RabbitMQ 服务器4. 数据同步模块(数据库到 RabbitMQ)5. MES 服务器从 RabbitMQ 接收数据6. 指令接收模块(RabbitMQ 到设备端) 7. MES…...
Linux touch命令
参考资料 Linux 常用命令 - touch 【创建空文件与修改时间戳】 目录 一. 用法简介二. 配合扩展字符,批量创建文件三. 修改文件的时间戳3.1 -t 配置项3.2 -d 配置项3.3 配合find命令实现批量时间戳修改 四. 结合 find 批量创建相同时间的新文件 一. 用法简介 ⏹当指…...
LlamaFactory部署及模型微调【win10环境】
1.Llama-Factory简介 LLaMA-Factory,全称 Large Language Model Factory,旨在简化大模型的微调过程,帮助开发者快速适应特定任务需求,提升模型表现。它支持多种预训练模型和微调算法,适用于智能客服、语音识别、机器翻…...
vue3配置代理实现axios请求本地接口返回PG库数据【前后端实操】
前端编写 安装 axios 如果当前未安装axios,可以执行如下指令安装 npm install axios配置代理 当前为基于Vite构建的项目,在 vite.config.ts 中配置代理,在defineConfig中新增server配置,主要关注两个点: 一、需要代…...
trae 配置 gradle springboot项目
一 本机安装gradle 1.下载gradle : https://github.com/gradle/gradle-distributions/releases/download/v8.13.0/gradle-8.13-all.zip 2.配置相关环境变量: GRADLE_HOME:本地的gradle路径。 GRADLE_USER_HOME:gradle 本地仓…...
uv:Rust 驱动的 Python 包管理新时代
在 Python 包管理工具层出不穷的今天,pip、pip-tools、poetry、conda 等各有千秋。而今天要介绍的 uv,则是一款由 Astral 团队推出、采用 Rust 编写的全新工具,目标直指成为 “Python 的 Cargo”。它不仅在性能上表现优异,而且在功…...
sqlserver 阻止保存要求重新创建表的更改
1 选择 “工具” 菜单,然后点击 “选项” 2 进入选项界面后,选择 “设计器”,取消勾选 “阻止保存要求重新创建表的更改” 选项,点击 “确定”...
5.Excel:从网上获取数据
一 用 Excel 数据选项卡获取数据的方法 连接。 二 要求获取实时数据 每1分钟自动更新数据。 A股市场_同花顺行情中心_同花顺财经网 用上面方法将数据加载进工作表中。 在表格内任意区域右键,刷新。 自动刷新: 三 缺点 Excel 只能爬取网页上表格类型的…...
初阶8 list
本章重点 list的介绍list的基本使用list的模拟实现 1.list的介绍 list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。 list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在…...
在word中使用zotero添加参考文献并附带超链接
一、引言 在写大论文时,为了避免文中引用与文末参考文献频繁对照、修改文中引用顺序/引用文献时手动维护参考文献耗易出错,拟在 word 中使用 zotero 插入参考文献,并为每个参考文献附加超链接,实现交互式阅读。 版本:…...
性能测试、负载测试、压力测试的全面解析
在软件测试领域,性能测试、负载测试和压力测试是评估系统稳定性和可靠性的关键手段。它们各自关注不同的测试目标和应用场景,理解这些差异对于制定有效的测试策略至关重要。 本文对性能测试、负载测试和压力测试进行深入分析,探讨其定义、…...
mysqloracledb2 (uuid函数)
项目场景: 创建一个32位的UUID 问题描述 原因分析: 解决方案: mysql内置UUID函数 SELECT UUID(); SELECT UUID_SHORT();oracle内置UUID函数 SELECT sys_guid() FROM dual;db2,模拟UUID函数 SELECT TEST || substr (CONCAT…...
工具介绍《WireShark》
Wireshark 过滤命令中符号含义详解 一、比较运算符 Wireshark 支持两种比较运算符语法:英文缩写(如 eq)和 C语言风格符号(如 ),两者功能等价。 符号(英文缩写)C语言风格符号含义示…...
Redis中的数据类型与适用场景
目录 前言1. 字符串 (String)1.1 特点1.2 适用场景 2. 哈希 (Hash)2.1 特点2.2 适用场景 3. 列表 (List)3.1 特点3.2 适用场景 4. 集合 (Set)4.1 特点4.2 适用场景 5. 有序集合 (Sorted Set)5.1 特点5.2 适用场景 6. Redis 数据类型的选型建议结语 前言 Redis 作为一款高性能的…...
gz sim机器人SDF模型 [持续更新]
机器人SDF模型 linklink的一级pose材质 plugin话题信息通信键盘操作plugin Sensor传感器imu 不算教学,个人的记录 sdf的格式跟urdf有所不同,必须是完整的一个包括,比如< pose></ pose>这样前一个后一个,urdf中是有<…...
【Qt】Ubuntu22.04使用命令安装Qt5和Qt6
1、安装Qt5 注意:Ubuntu22.04已经没有 qt5-default ,因此不能一键安装啦 1)安装核心组件 sudo apt install qtbase5-dev qtchooser qt5-qmake qtcreator2)安装QtCreator sudo apt install qtcreator3)安装工具包、Qt Quick 开发的核心库(qtdeclarative5-dev) sudo a…...
Pytest的Fixture使用
概述 Pytest中的Fixture可以使得测试代码高度复用,同时对资源进行安全的管理,以及在复杂的测试场景用进行灵活的组合。 概念 Fixture:可重用的函数,用@pytest.fixture来进行装饰,用于为测试提供数据、环境或者服务作用域:控制Fixture的生命周期,默认是function,可设置…...
【MySQL | 六、索引特性(进一步理解)】
目录 索引的理解索引的作用MySQL与磁盘的IOPage单个Page的分类多个Page的组织B树的特点 B树和B树的区别聚簇索引 VS 非聚簇索引聚簇索引的优缺点非聚簇索引的优缺点 创建索引常见索引分为:主键索引InnoDB主键索引的生成过程(1)初始化…...
计算机网络高频(三)UDP基础
计算机网络高频(三)UDP基础 1.UDP的头部格式是什么样的?⭐ UDP 头部具有以下字段: 源端口(Source Port):16 位字段,表示发送方的端口号。目标端口(Destination Port):16 位字段,表示接收方的端口号。长度(Length):16 位字段,表示 UDP 数据报(包括头部和数据部…...
【测试开发】OKR 小程序端黑盒测试报告
【测试报告】OKR 小程序端 项目名称版本号测试负责人测试完成日期联系方式OKR 小程序端4.0马铭胜2025-03-2515362558972 1、项目背景 1.1 OKR 用户端 在如今这个快节奏的时代中,个人和组织的成长往往依赖于清晰、明确且意义深远的目标。然而,如何设定…...
