#widgets #bindings #ui #graphics

已删除 qt-cb-sys

在 rust-qt 中使用回调!

1 个不稳定版本

0.1.0 2023年7月28日

#277 in #widgets

MIT 协议

13KB
133 代码行

qt-cb

为 rust-qt 提供了多个便利的回调函数。

理由

尽管 Qt 的信号和槽机制非常强大,允许编写非常动态的代码和编程模型,但并非总是需要,尤其是对于简单的功能。

在 C++ 和 Rust 中,定义信号和槽有时可能变得冗长,Qt 本身也支持在 C++ 端使用 lambda 函数。

例如,比较以下两个程序的可读性和编写舒适性。这两个程序执行相同的功能,第一个程序没有使用回调,它为所需的信号定义了槽。第二个程序使用了这个包,并使用回调而不需要在单独的对象中定义槽,也不需要 derive SlotNoArgs/SlotOfBool/SlotOfQString 等样板代码。

第一个程序(无回调)

// no callbacks
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()
    })
}

第二个程序(有回调)

// with callbacks
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.on_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.on_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.on_pressed(move |_b| {
            println!("Hello {}", ed.text().to_std_string());
        });
        win.show();
        QApplication::exec()
    })
}

注意

    ed.on_text_changed(|_ed, txt| {
        println!("current lineedit text: {}", txt.to_std_string());
    });
    checkbox.on_clicked(|b, checked| {
        println!(
            "{} is {}checked",
            b.text().to_std_string(),
            if checked { "" } else { "un" }
        );
    });
    button.on_pressed(move |_b| {
        println!("Hello {}", ed.text().to_std_string());
    });

而不是

    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());
    }

使用方法

[dependencies]
qt-cb = "0.1.0"
# the rest of the dependencies or rust-qt

需求

此包需要 C++17 编译器和可以从您的 shell 中调用的 qmake 可执行文件(即在 PATH 中)。安装 Qt5(基本上与 rust-qt 的要求相同)。rust-qt 还需要 CMake。在 Linux 上,您可以通过包管理器安装 Qt(假设使用 Debian)

sudo apt-get install -y qtbase5-dev libqt5widgets5

在 Windows msys2/mingw64 上

pacman -S $MINGW_PACKAGE_PREFIX-qt5-base

无运行时依赖

~195KB