#gtk #actix #actix-actor #actor #signal-handler #gui-framework

woab

Widgets on Actors Bridge - 将 GTK 与 Actix 结合的 GUI 微框架

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

Download history 47/week @ 2024-04-19 36/week @ 2024-04-26 3/week @ 2024-06-28 15/week @ 2024-07-05

每月下载量:499

MIT 许可证

83KB
958

Build Status Latest Version Rust Documentation - Latest Version Rust Documentation - Nightly

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 许可证下授权 (LICENSEhttp://opensource.org/licenses/MIT))

依赖项

~21–31MB
~547K SLoC