#widgets #bindings #ui #graphics

qt-cb

在 rust-qt 中使用闭包!

6 个版本

0.1.6 2023 年 7 月 30 日
0.1.5 2023 年 7 月 29 日

#1203 in GUI

每月 43 次下载

MIT 许可证

17KB
184 代码行

qt-cb

此包提供了一些 traits,这些 traits 定义了方便的方法来为 qt_widgets 类型。这些方法的形式为 connect_<signal>,它接受一个闭包。闭包的参数是触发信号的控件,以及信号的参数。如果你熟悉 Gtk-rs api,这应该对你来说也很熟悉。

本质上,它实例化了必要的 Slot,并直接将闭包传递给它。它还将控件本身传递到闭包中,因此你可以在闭包中访问控件。

即它给你

checkbox.connect_clicked(|checkbox, checked| {
    // access checkbox
    // handle signal argument checked
});

而不是

checkbox.clicked().connect(&SlotOfBool::new({ 
    let checkbox = checkbox.as_ptr(); 
    move |checked| {
        // access checkbox
        // handle signal argument checked
    }
}));

或者

struct SomeStruct {
    checkbox: QBox<QCheckBox>,
}

impl StaticUpcast<QObject> for SomeStruct {
    // snipped
}

impl SomeStruct {
    unsafe fn init(self: &Rc<Self>) {
        self.checkbox.clicked().connect(&self.slot_on_checkbox_clicked());
    }
    #[slot(SlotOfBool)]
    unsafe fn connect_checkbox_clicked(self: &Rc<Self>, checked: bool) {
        // access self.checkbox
        // handle signal argument checked
    }
}

要求

此包具有与 rust-qt 相同的要求。

用法

[dependencies]
qt-cb = "0.1.0"
# other rust-qt packages
qt_core = "0.5.0"
qt_widgets = "0.5.0"
# other deps

在你的 Rust 源代码中,use qt_cb::prelude::*;

use qt_cb::prelude::*;
use qt_core::qs;
use qt_widgets::{
    QApplication, QCheckBox, QHBoxLayout, QLineEdit, QPushButton, QVBoxLayout, QWidget,
};

fn main() {
    QApplication::init(|_| unsafe {
        QApplication::set_style_q_string(&qs("Fusion"));
        let win = QWidget::new_0a();
        win.set_fixed_size_2a(400, 300);
        let vbox = QVBoxLayout::new_1a(&win);
        let ed = QLineEdit::new();
        ed.set_placeholder_text(&qs("Enter name"));
        ed.connect_text_changed(|_ed, txt| {
            println!("current lineedit text: {}", txt.to_std_string());
        });
        vbox.add_widget(&ed);
        let hbox = QHBoxLayout::new_0a();
        vbox.add_layout_1a(&hbox);
        let checkbox = QCheckBox::new();
        hbox.add_widget(&checkbox);
        checkbox.set_text(&qs("Check me!"));
        checkbox.connect_clicked(|b, checked| {
            println!(
                "{} is {}checked",
                b.text().to_std_string(),
                if checked { "" } else { "un" }
            );
        });
        let button = QPushButton::new();
        hbox.add_widget(&button);
        button.set_text(&qs("Greet!"));
        button.connect_pressed(move |_b| {
            println!("Hello {}", ed.text().to_std_string());
        });
        win.show();
        QApplication::exec()
    })
}

理由

定义 slots,尤其是在 Rust 端,可能会很冗长。你可能只需要快速调试某事或向控件添加一些简单的功能,而不必通过定义 slots 的仪式。在 rust-qt 中定义 slots 有两种主要方法,简要介绍如下:(以下程序与 README 中的第一个程序执行相同的操作,该程序使用此包)

1- 使用 slot 宏,这需要定义一个类型,该类型也实现了 StaticUpcast<QObject>

use cpp_core::{Ptr, Ref, StaticUpcast};
use qt_core::{qs, slot, QBox, QObject, QString, SlotNoArgs, SlotOfBool, SlotOfQString};
use qt_widgets::{
    QApplication, QCheckBox, QHBoxLayout, QLineEdit, QPushButton, QVBoxLayout, QWidget,
};
use std::rc::Rc;

struct Form {
    win: QBox<QWidget>,
    ed: QBox<QLineEdit>,
    checkbox: QBox<QCheckBox>,
    button: QBox<QPushButton>,
}

impl StaticUpcast<QObject> for Form {
    unsafe fn static_upcast(ptr: Ptr<Self>) -> Ptr<QObject> {
        ptr.win.as_ptr().static_upcast()
    }
}

impl Form {
    fn new() -> Rc<Form> {
        unsafe {
            let win = QWidget::new_0a();
            win.set_fixed_size_2a(400, 300);
            let vbox = QVBoxLayout::new_1a(&win);
            let ed = QLineEdit::new();
            ed.set_placeholder_text(&qs("Enter name"));
            vbox.add_widget(&ed);
            let hbox = QHBoxLayout::new_0a();
            vbox.add_layout_1a(&hbox);
            let checkbox = QCheckBox::new();
            hbox.add_widget(&checkbox);
            checkbox.set_text(&qs("Check me!"));
            let button = QPushButton::new();
            hbox.add_widget(&button);
            button.set_text(&qs("Greet!"));
            win.show();

            let this = Rc::new(Self {
                win,
                ed,
                checkbox,
                button,
            });
            this.init();
            this
        }
    }

    unsafe fn init(self: &Rc<Self>) {
        self.ed
            .text_changed()
            .connect(&self.slot_on_lineedit_text_changed());
        self.button
            .pressed()
            .connect(&self.slot_on_button_pressed());
        self.checkbox
            .clicked()
            .connect(&self.slot_on_checkbox_clicked());
    }

    #[slot(SlotNoArgs)]
    unsafe fn on_button_pressed(self: &Rc<Self>) {
        println!("Hello {}", self.ed.text().to_std_string());
    }

    #[slot(SlotOfBool)]
    unsafe fn on_checkbox_clicked(self: &Rc<Self>, checked: bool) {
        println!(
            "{} is {}checked",
            self.checkbox.text().to_std_string(),
            if checked { "" } else { "un" }
        );
    }

    #[slot(SlotOfQString)]
    unsafe fn on_lineedit_text_changed(self: &Rc<Self>, txt: Ref<QString>) {
        println!("current lineedit text: {}", txt.to_std_string());
    }
}

fn main() {
    QApplication::init(|_| unsafe {
        let _form = Form::new();
        QApplication::exec()
    })
}

2- 定义 slots 的第二种方法,是实例化与信号提供的参数相对应的 Slot 对象,即 SlotOfBool,SlotOfQString 等。闭包不暴露控件在参数中,因此需要手动捕获(可以使用 Rc 智能指针或获取 Ptr<Widget>

use qt_core::{qs, SlotNoArgs, SlotOfBool, SlotOfQString};
use qt_widgets::{
    QApplication, QCheckBox, QHBoxLayout, QLineEdit, QPushButton, QVBoxLayout, QWidget,
};

fn main() {
    QApplication::init(|_| unsafe {
        QApplication::set_style_q_string(&qs("Fusion"));
        let win = QWidget::new_0a();
        win.set_fixed_size_2a(400, 300);
        let vbox = QVBoxLayout::new_1a(&win);
        let ed = QLineEdit::new();
        ed.set_placeholder_text(&qs("Enter name"));
        ed.text_changed().connect(&SlotOfQString::new(&ed, |txt| {
            println!("current lineedit text: {}", txt.to_std_string());
        }));
        vbox.add_widget(&ed);
        let hbox = QHBoxLayout::new_0a();
        vbox.add_layout_1a(&hbox);
        let checkbox = QCheckBox::new();
        hbox.add_widget(&checkbox);
        checkbox.set_text(&qs("Check me!"));
        checkbox.clicked().connect(&SlotOfBool::new(&checkbox, {
            let checkbox = checkbox.as_ptr();
            move |checked| {
                println!(
                    "{} is {}checked",
                    checkbox.text().to_std_string(),
                    if checked { "" } else { "un" }
                );
            }
        }));
        let button = QPushButton::new();
        hbox.add_widget(&button);
        button.set_text(&qs("Greet!"));
        button.pressed().connect(&SlotNoArgs::new(&button, move || {
            println!("Hello {}", ed.text().to_std_string());
        }));
        win.show();
        QApplication::exec()
    })
}

依赖关系

~104MB
~816K SLoC