C++ TreeView中的层次数据和TreeView更新技术
我在树视图中显示了很多(层次)数据(可能是大约20K个项目或更多,包括子项目)。我的数据的特殊问题是,树视图中显示的每个对象都可以存在于许多树视图项中。我的意思是,我可能有这样一个层次结构:C++ TreeView中的层次数据和TreeView更新技术,c++,qt,treeview,design-patterns,qt3,C++,Qt,Treeview,Design Patterns,Qt3,我在树视图中显示了很多(层次)数据(可能是大约20K个项目或更多,包括子项目)。我的数据的特殊问题是,树视图中显示的每个对象都可以存在于许多树视图项中。我的意思是,我可能有这样一个层次结构: 项目A->项目B->项目c Item_B->Item_C ItemC 假设项目A包含项目B,其中包含项目C,如上所示。这意味着我的列表还将显示项目B和项目C的层次结构。现在考虑一个对象发生的事情,如 ItMyB.(例如名称更改)。那当然两项都有 必须更新。现在考虑具有复杂层次结构的树视图中的数以千计项。您将
项目A->项目B->项目c
Item_B->Item_C
ItemC
项目A
包含项目B
,其中包含项目C
,如上所示。这意味着我的列表还将显示项目B
和项目C
的层次结构。现在考虑一个对象发生的事情,如<代码> ItMyB.<代码>(例如名称更改)。那当然两项都有
必须更新。现在考虑具有复杂层次结构的树视图中的数以千计项。您将使用什么策略来更新treeview?速度当然是这里主要关注的问题,但也很容易使用和维护。目前,我持有列表项到对象的内部映射,反之亦然,以便快速查找和更新项。这是正确的策略吗?通过在每次更新后重新创建列表,我可以扔掉很多代码,但我不知道哪些项目路径被展开或折叠。我怎样才能解决这个问题?我应该将扩展路径存储在内部容器中吗
多谢各位
PS:编程语言是C++,GUI LIB是QT3。
< P> >我使用Windows TeeVIEW通用控件。 我所做的是设置CUSTOMDRAW标志,为每个可能的不同节点保留一个实例,并使每个节点指向该实例:3个Item_C节点都有一个指向相同唯一Item_C实例的指针 因此,当我更改Item_C上的数据时,我只需要在3个Item_C节点上调用invalidate installet(),以反映对(单个)更改数据所做的更改我想您可以在这里应用相同的策略。如果可以在项目中使用Qt4,请使用Qt4模型/视图 您将不得不编写自己的模型,如果您从未这样做过,这可能会很乏味,但一旦设置好,您就可以轻松地引用/更新同一对象的多个实例。也可以处理选择/多个选择
我不太喜欢Qt的模型/视图实现(给定的模型/视图/控制器设计模式非常古老),但它有助于在GUI中组织数据使用
widget->setUpdatesEnabled(false)
禁用更新,然后编辑所有需要的内容,然后使用widget->setUpdatesEnabled(true)
重新启用它
请参阅。我使用wxWidgets树控件解决了类似的问题。我使用一个单例引用计数器跟踪我放在控件中的对象,并使用迭代器遍历它们。这里有一个例子
class ReferenceCounter
{
public:
// Singleton pattern. Implementation left up to you.
static ReferenceCounter& get();
void add(const TreeData& data) {
mCounter[data.getId()].push_back(&data);
}
void remove(const TreeData& data) {
const CounterType::const_iterator itr = mCounter.find(data.getId());
if (itr != mCounter.end()) {
ItemType& items = itr->second;
items.erase(std::remove(items.begin(), items.end(), &data), items.end());
if (items.empty()) {
mCounter.erase(itr);
}
}
}
typedef std::vector<TreeData*> ItemType;
ItemType::iterator begin(const TreeData& data) {
const CounterType::const_iterator itr = mCounter.find(data.getId());
if (itr != mCounter.end()) {
return itr->second.begin();
}
// Else condition handling left up to you.
}
ItemType::iterator end(const TreeData& data) {
const CounterType::const_iterator itr = mCounter.find(data.getId());
if (itr != mCounter.end()) {
return itr->second.end();
}
// Else condition handling left up to you.
}
private:
typedef std::map<int, ItemType> CounterType;
CounterType mCounter;
};
class TreeData
{
public:
TreeData() { ReferenceCounter::get().add(*this); }
~TreeData() { ReferenceCounter::get().remove(*this); }
// Get database rows or whatever your tree is tracking.
int getId() const;
};
类引用计数器
{
公众:
//单例模式。实现由您决定。
静态引用计数器&get();
无效添加(常量树数据和数据){
mCounter[data.getId()]。推回(&data);
}
无效删除(常量树数据和数据){
常量计数器类型::常量迭代器itr=mCounter.find(data.getId());
if(itr!=mCounter.end()){
ItemType&items=itr->second;
items.erase(std::remove(items.begin()、items.end()、和data)、items.end());
if(items.empty()){
mCounter.擦除(itr);
}
}
}
typedef std::vector ItemType;
ItemType::迭代器开始(const TreeData&data){
常量计数器类型::常量迭代器itr=mCounter.find(data.getId());
if(itr!=mCounter.end()){
返回itr->second.begin();
}
//其他情况由你决定。
}
ItemType::迭代器端(const TreeData和data){
常量计数器类型::常量迭代器itr=mCounter.find(data.getId());
if(itr!=mCounter.end()){
返回itr->second.end();
}
//其他情况由你决定。
}
私人:
typedef std::映射计数器类型;
计数器式计数器;
};
树形纲
{
公众:
TreeData(){ReferenceCounter::get().add(*this);}
~TreeData(){ReferenceCounter::get().remove(*this);}
//获取数据库行或树正在跟踪的任何内容。
int getId()常量;
};
因此,给定任何TreeData,您可以在引用计数器中查找具有匹配ID的所有其他TreeData。这使得名字和内容保持最新变得简单快捷。我们的树处理超过1000000个节点,没有问题。在我的实现中,为了便于使用,我将迭代内容封装在boost::iterator\u facade
类中