gtk4/rust 从 ListView 中删除 EditableLabel 时断言失败
gtk4/rust failed assertion when removing EditableLabel from ListView
在下面的 gtk4 代码中,我试图通过删除对应的 StringObject
从显示中删除 EditableLabel
。这应该在 connect_editing_notify
回调指定的编辑完成后发生。
如果我 运行 程序并使用 return 键退出编辑模式,那么它会按预期工作:标签消失。但是,如果我通过选择不同的标签退出编辑模式,那么我会得到一个重复的控制台打印,说明断言失败:
(viewdb:13773): Gtk-CRITICAL **: 17:35:26.754: gtk_widget_get_parent: assertion 'GTK_IS_WIDGET (widget)' failed
(viewdb:13773): Gtk-CRITICAL **: 17:35:26.754: gtk_widget_get_parent: assertion 'GTK_IS_WIDGET (widget)' failed
(viewdb:13773): Gtk-CRITICAL **: 17:35:26.754: gtk_widget_get_parent: assertion 'GTK_IS_WIDGET (widget)' failed
如何在编辑后彻底删除 EditableLabel?
use gtk::glib::clone;
use gtk::prelude::*;
use gtk::{
Application, ApplicationWindow, EditableLabel, ListView, SignalListItemFactory,
SingleSelection, StringList, StringObject, Widget,
};
fn build_ui(app: &Application) {
let model: StringList = vec!["One", "Two", "Three", "Four"].into_iter().collect();
let selection_model = SingleSelection::new(Some(&model));
let factory = SignalListItemFactory::new();
factory.connect_setup(move |_, listitem| {
let label = EditableLabel::builder().build();
listitem.set_child(Some(&label));
listitem
.property_expression("item")
.chain_property::<StringObject>("string")
.bind(&label, "text", Widget::NONE);
label.connect_editing_notify(
clone!( @strong model, @strong listitem => move |lbl: &EditableLabel| {
if !lbl.is_editing() {
let position = listitem.position();
model.remove(position); // remove model entry cooresponding to edit.
}
}),
);
});
let view = ListView::new(Some(&selection_model), Some(&factory));
let window = ApplicationWindow::builder()
.application(app)
.child(&view)
.build();
window.present();
}
fn main() {
let app = Application::builder().build();
app.connect_activate(build_ui);
app.run();
}
在项目仍在处理某些事件时从其父项目中删除项目有时会导致问题。
简单的解决方法是在延迟函数中删除。在 C 语言中,用 g_idle_add()
来完成这对正确使用来说很麻烦。但在 Rust 中是 super-easy:
gtk::glib::source::idle_add_local_once(
clone!(@strong model =>
move || model.remove(position)
)
);
但请注意,由于 idle
函数是 运行 未来某个未指定的时间,因此 position
的值(一个简单的索引在列表)当回调为 运行 时将引用不同的项目。
我没有测试它,但我认为将 listitem
本身保留在回调中更明智:
gtk::glib::source::idle_add_local_once(
clone!(@strong model, @weak listitem =>
move || {
let position = listitem.position();
if position != INVALID_LIST_POSITION {
model.remove(position)
}
}
)
);
弱克隆将确保只有在项目仍然存在时才会调用它。
在下面的 gtk4 代码中,我试图通过删除对应的 StringObject
从显示中删除 EditableLabel
。这应该在 connect_editing_notify
回调指定的编辑完成后发生。
如果我 运行 程序并使用 return 键退出编辑模式,那么它会按预期工作:标签消失。但是,如果我通过选择不同的标签退出编辑模式,那么我会得到一个重复的控制台打印,说明断言失败:
(viewdb:13773): Gtk-CRITICAL **: 17:35:26.754: gtk_widget_get_parent: assertion 'GTK_IS_WIDGET (widget)' failed
(viewdb:13773): Gtk-CRITICAL **: 17:35:26.754: gtk_widget_get_parent: assertion 'GTK_IS_WIDGET (widget)' failed
(viewdb:13773): Gtk-CRITICAL **: 17:35:26.754: gtk_widget_get_parent: assertion 'GTK_IS_WIDGET (widget)' failed
如何在编辑后彻底删除 EditableLabel?
use gtk::glib::clone;
use gtk::prelude::*;
use gtk::{
Application, ApplicationWindow, EditableLabel, ListView, SignalListItemFactory,
SingleSelection, StringList, StringObject, Widget,
};
fn build_ui(app: &Application) {
let model: StringList = vec!["One", "Two", "Three", "Four"].into_iter().collect();
let selection_model = SingleSelection::new(Some(&model));
let factory = SignalListItemFactory::new();
factory.connect_setup(move |_, listitem| {
let label = EditableLabel::builder().build();
listitem.set_child(Some(&label));
listitem
.property_expression("item")
.chain_property::<StringObject>("string")
.bind(&label, "text", Widget::NONE);
label.connect_editing_notify(
clone!( @strong model, @strong listitem => move |lbl: &EditableLabel| {
if !lbl.is_editing() {
let position = listitem.position();
model.remove(position); // remove model entry cooresponding to edit.
}
}),
);
});
let view = ListView::new(Some(&selection_model), Some(&factory));
let window = ApplicationWindow::builder()
.application(app)
.child(&view)
.build();
window.present();
}
fn main() {
let app = Application::builder().build();
app.connect_activate(build_ui);
app.run();
}
在项目仍在处理某些事件时从其父项目中删除项目有时会导致问题。
简单的解决方法是在延迟函数中删除。在 C 语言中,用 g_idle_add()
来完成这对正确使用来说很麻烦。但在 Rust 中是 super-easy:
gtk::glib::source::idle_add_local_once(
clone!(@strong model =>
move || model.remove(position)
)
);
但请注意,由于 idle
函数是 运行 未来某个未指定的时间,因此 position
的值(一个简单的索引在列表)当回调为 运行 时将引用不同的项目。
我没有测试它,但我认为将 listitem
本身保留在回调中更明智:
gtk::glib::source::idle_add_local_once(
clone!(@strong model, @weak listitem =>
move || {
let position = listitem.position();
if position != INVALID_LIST_POSITION {
model.remove(position)
}
}
)
);
弱克隆将确保只有在项目仍然存在时才会调用它。