如何从异步方法更新GTK ListBox?问题排查与实现
我仔细看了你的代码,发现几个关键问题导致items_changed信号没触发,进而ListBox无法更新,咱们一步步来修正:
1. 核心问题:被覆盖的集合引用
在AsyncFileContext的构造函数里,你先把传入的vcards赋值给_vCards,紧接着又用新创建的集合覆盖了它:
public AsyncFileContext(IObservableSet<IVCard> vcards, string path){ this._vCards = vcards; this._vCards = IObservableSet.wrap_set<IVCard>(new Gee.HashSet<IVCard>()); // 这里直接覆盖了传入的集合! // ... 其他代码 }
这就导致:你在read_file_async里往_vCards添加元素时,操作的是一个全新的集合,而VCardListModel监听的是最初传入的那个集合——两者完全没关系,自然收不到item_added信号,更不会触发items_changed。
修正方法:去掉覆盖的那行代码,保留传入的集合引用:
public AsyncFileContext(IObservableSet<IVCard> vcards, string path){ this._vCards = vcards; // 删掉 this._vCards = IObservableSet.wrap_set<...>(); 这一行 this.file = File.new_for_path(path); // ... 其他代码保持不变 }
2. vcard_buffer的逻辑错误
在read_file_async里,你添加元素后立刻从vcard_buffer移除所有已加入_vCards的元素,这会导致vcard_buffer刚添加的元素被清空,但逻辑上你应该是想在添加后清空缓冲区:
Idle.add(() => { _vCards.add_all(vcard_buffer); vcard_buffer.remove_all(_vCards); // 这里错误,应该是清空缓冲区 return false; });
修正方法:用clear()替代remove_all(_vCards):
Idle.add(() => { _vCards.add_all(vcard_buffer); vcard_buffer.clear(); // 清空缓冲区,准备下一批数据 return false; });
3. 优化VCardListModel的信号触发逻辑
在VCardListModel的item_added回调里,你用index_of查找位置,其实可以直接用集合的当前长度(因为是添加到末尾),既高效又避免可能的索引查找错误:
this.vcard_collection.item_added.connect((vcard) => { int pos = vcard_list.size; // 新元素的位置是当前集合长度(add到末尾后会自动+1) vcard_list.add(vcard); items_changed((uint)pos, 0, 1); });
另外,get_object方法是冗余的,ListModel的标准接口是get_item,可以删掉这个方法,避免混淆。
4. 简化异步操作的恢复逻辑
你在read_file_async里用Idle.add(read_file_async.callback)来恢复异步执行,其实不需要这么做——yield本身就会暂停并在合适时机恢复执行,直接去掉这行代码即可:
if(factory.vcard_ready){ message("creating..."); this.vcard_buffer.add(factory.create()); vcards_added++; message("Yielding..."); Idle.add(() => { _vCards.add_all(vcard_buffer); vcard_buffer.clear(); return false; }); yield; // 这里会自动恢复执行,不需要额外回调 message("Resuming"); }
验证信号触发
修正后,你在ContactList里绑定的list_model.items_changed信号应该能正常触发,ListBox也会自动更新列表项了。如果还是有问题,可以检查IObservableCollection的item_added信号是否正确发射——因为VCardListModel完全依赖这个信号来触发items_changed。
内容的提问来源于stack exchange,提问作者rasmus91

