8 个重大版本更新
0.9.0 | 2024年4月18日 |
---|---|
0.8.0 | 2023年8月19日 |
0.7.0 | 2022年9月6日 |
0.6.0 | 2021年7月9日 |
0.1.0 | 2020年9月2日 |
#176 in GUI
每月下载量:499
83KB
958 行
WoAB
WoAB (Widgets on Actors Bridge) 是一个将小部件工具包 GTK 与演员框架 Actix 结合的 GUI 微框架。它可以帮助
- 在 GTK 线程中运行演员,使消息处理程序可以直接与小部件交互。
- 通过异步运行时路由 GTK 信号,使处理它们的代码可以自然地继续与演员交互。
- 将来自 Cambalache 发射的 XML 文件中的小部件和信号映射到用户类型。
有关如何使用 WoAB 的更多信息,请参阅 文档,有关简短演示,请参阅 示例。
use actix::prelude::*;
use gtk4::prelude::*;
struct MyActor {
widgets: MyWidgets,
}
impl Actor for MyActor {
type Context = Context<Self>;
}
// Use this derive to automatically populate a struct with GTK objects from a builder using their
// object IDs.
#[derive(woab::WidgetsFromBuilder)]
struct MyWidgets {
window: gtk4::ApplicationWindow,
button: gtk4::Button,
}
// WoAB converts GTK signals (defined) to Actix messages, which the user defined actors need handle.
impl Handler<woab::Signal> for MyActor {
type Result = woab::SignalResult;
fn handle(&mut self, msg: woab::Signal, _ctx: &mut Self::Context) -> Self::Result {
// All the signals get the same message type (`woab::Signal`), and need to be matched by
// the handler name.
Ok(match msg.name() {
"button_clicked" => {
// Handlers can freely use the GTK widget handles stored inside the actor to
// interact with the UI.
self.widgets.button.set_label("Hello World");
// Some GTK signals require a `glib::Propagation` decision. Others, like
// `GtkButton::clicked` here, don't. It is up to the signal handler to return the
// correct type.
None
}
_ => msg.cant_handle()?,
})
}
}
fn main() -> woab::Result<()> {
// Factories can be used to create the GUI and connect the signals.
let factory = woab::BuilderFactory::from(
// Typically the UI XML will be generated with Cambalache and loaded from a file, but for
// the sake of this simple example it is inlined here.
r#"
<interface>
<object class="GtkApplicationWindow" id="window">
<child>
<object class="GtkButton" id="button">
<property name="label">Click Me!</property>
<signal name="clicked" handler="button_clicked"/>
</object>
</child>
</object>
</interface>
"#
.to_owned(),
);
// Setup the application inside `woab::main`. This handles starting/stopping GTK and Actix, and
// making them work together. The actual closure is run inside the application's `startup`
// signal.
woab::main(gtk4::Application::default(), move |app| {
// A useful helper so that when the last window is closed, the application will exit.
woab::shutdown_when_last_window_is_closed(app);
// We need the actor's address when instantiating the builder (because we need to connect
// the signals) and we need the builder result when we create the actor (because we want to
// provide it with the widgets). Thus, we usually want to use Actix's two-steps actor
// initialization.
let ctx = Context::new();
// This will create the UI widgets from the XML and route the signals to the actor.
let bld = factory.instantiate_route_to(ctx.address());
// Automatically assign all the windows inside the builder to the application. Without
// this, `woab::shutdown_when_last_window_is_closed` will be meaningless.
bld.set_application(app);
// Extract the newly created widgets from the builder.
let widgets: MyWidgets = bld.widgets()?;
// When the builder loads the window, it starts as hidden. We can use the extracted widgets
// to show it.
widgets.window.show();
// This is where the actor is actually launched.
ctx.run(MyActor { widgets });
Ok(())
})
}
陷阱
- 当从 Tokio/Actix 外部启动 Actix 演员时,必须使用
woab::block_on
。这是 Actix 的限制,需要遵守。 - 如果在一个
gtk::Application::connect_activate
中创建演员,其started
方法将在activate
信号完成后运行。这可能会成为像set_application
这样的方法的难题,如果它们在activate
信号之外被调用,可能会引发段错误。一种解决方案是在connect_activate
内部进行启动,或者使用woab::route_signal
将应用程序的activate
信号路由到演员,并在演员的信号处理程序中进行启动。 - 必须在
gtk::main()
之后调用woab::close_actix_runtime
,否则当 GTK 退出时,Tokio 将引发恐慌。如果有人知道如何自动化它,我愿意听取建议。
许可证
MIT 许可证下授权 (LICENSE 或 http://opensource.org/licenses/MIT))
依赖项
~21–31MB
~547K SLoC