-
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 {
/// 不可变的可调用方法,返回 QColor
pub 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,
);
}