TreeViews 中的分层数据和 TreeView 更新技术

发布于 2024-07-29 19:31:22 字数 754 浏览 13 评论 0原文

我在 TreeView 中显示了很多(分层)数据(可能是大约 20K 项或更多,包括子项)。 我的数据的特殊问题是树视图中显示的每个对象都可以存在于许多树视图项中。 我的意思是,我可能有一个像这样的层次结构:

  1. Item_A -> 项目_B -> ItemC
  2. Item_B -> Item_C
  3. ItemC

让我们假设 Item_A 包含 Item_B,而 Item_B 又包含 Item_C,如上所示。 这意味着我的列表还将显示 Item_BItem_C 的层次结构。 现在考虑一下显示为 Item_B 的对象发生了一些事情(例如名称更改)。 然后当然是这两个项目 必须更新。 现在考虑树视图中具有复杂层次结构的数千个项目。 您将使用什么策略来更新树视图? 速度当然是这里主要考虑的问题,但也易于使用和维护。 目前,我保存列表项到对象的内部映射,反之亦然,以便快速查找和更新项目。 这是正确的策略吗? 通过在每次更新后重新创建列表,我可以扔掉大量代码,但我不知道哪些项目路径被展开或折叠。 我该如何解决这个问题? 我应该将扩展路径存储在内部容器中吗?

谢谢。

PS:编程语言是C++,GUI库是QT3。

I have lots of (hierachical) data that I show in a TreeView (could be around 20K items or more, including children items). The peculiar problem with my data is that each object shown in the treeview can exist in many treeview items. What I mean by that is that I might have an hierarchy like this one:

  1. Item_A -> Item_B -> ItemC
  2. Item_B -> Item_C
  3. ItemC

Lets assume that Item_A contains Item_B which contains Item_C as it is shown above. This means that my list will also show the hierarchy of Item_B and Item_C. Consider now that something happens to an an object shown as Item_B (e.g name change). Then of course both items
must be updated. Consider now thousands of items in the treeview with complex hierarchies. What strategy would you use to update the treeview? Speed is of course the main concern here but also ease of use and maintenance. Currently I hold internal mappings of list items to objects and vice-versa to find and update items quickly. Is that a correct strategy? By recreating the list after each update I can throw lots of code away, but I wouldn't know which item paths were expanded or collapsed. How could I solve this problem? Should I store expanded paths in an internal container?

Thank you.

PS: Programming language is C++, and GUI lib is QT3.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

奢望 2024-08-05 19:31:24

我很久以前就做过类似的事情,使用windows TreeView通用控件。

我所做的是设置 CUSTOMDRAW 标志,保留每个可能的不同节点的单个实例,并使每个节点都指向该实例:3 个 Item_C 节点每个都有一个指向同一个唯一 Item_C 实例的指针。

因此,当我更改 Item_C 上的数据时,我只需要在 3 个 Item_C 节点上调用 InvalidateRect() 即可反映对(单个)更改数据所做的更改。

我想你可以在这里应用相同的策略。

I did something similar long time ago, using the windows TreeView common control.

What I did was set the CUSTOMDRAW flag, keep a single instance of each possible different node and make each node point to this instance: The 3 Item_C nodes would each have a pointer to the same unique Item_C instance.

So, when I change the data on Item_C I only needed to Invoke InvalidateRect() on the 3 Item_C nodes in order to reflect the changes done on the (single) changed data.

I suppose you could apply the same strategy here.

初心未许 2024-08-05 19:31:24

如果您可以在项目中使用 Qt4,请使用 Qt4 模型/视图。

您将必须编写自己的模型,如果您从未这样做过,这可能会很乏味,但是一旦设置,您就可以轻松引用/更新同一对象的多个实例。 也可以处理选择/多项选择。

我不太喜欢 Qt 的模型/视图实现(给定的模型/视图/控制器设计模式相当旧),但它有助于在 GUI 中组织数据

Use Qt4 model/view if you can use Qt4 in your project.

You will have to write your own model which can be tedious if you've never done so, but once setup you can reference/update multiple instances of the same object easily. Selections/Multiple selection can be handled too.

I'm not a big fan of Qt's model/view implementation (given model/view/controller design pattern is quite old) but it helps to organize data in gUIs

残月升风 2024-08-05 19:31:24

使用 widget->setUpdatesEnabled(false) 禁用更新,然后编辑所需的所有内容,然后使用 widget->setUpdatesEnabled(true) 重新启用它。

请参阅 Qt 文档

Disable updates using widget->setUpdatesEnabled(false), then edit all you want, and then renable it with widget->setUpdatesEnabled(true).

See the Qt documentation.

我乃一代侩神 2024-08-05 19:31:24

我使用 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;
};

因此,给定任何 TreeData,您可以在引用计数器中查找具有匹配 id 的所有其他 TreeData。 这样可以轻松快速地保持名称和内容的最新状态。 我们的树可以毫无问题地处理超过 1,000,000 个节点。 在我的实现中,我将迭代内容包装在 boost::iterator_facade 类中,以便于使用。

I solved a similar problem using the wxWidgets tree control. I used a singleton reference counter to track the objects I was putting in the control, and an iterator to traverse them. Here's an example.

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

So given any TreeData, you can look up all the other TreeData's with matching ids in the reference counter. This makes it easy and fast keeping names and stuff up to date. Our tree handles over 1,000,000 nodes without a problem. In my implementation, I wrapped the iteration stuff up in a boost::iterator_facade class for easier use.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文