c++ - QGraphicsScene 中元素的迭代器

标签 c++ qt iterator qgraphicsitem qgraphicsscene

我有一个 QGraphicsScene,其中包含自定义对象(以 QGraphicsItem 作为基类)。我可以通过以下方式检索这些对象:

foreach (QGraphicsItem* item, items()) {
    if (item->type() == CustomItem::Type)
        qgraphicsitem_cast<CustomItem*>(item).doStuff(...);
}

是否可以为这种类型的对象编写自定义迭代器?我希望能够使用 STL 中基于范围的 for 循环和算法。

for(CustomItem& a : custom_items())
    a.doStuff(...);

我想避免手动存储指向这些对象的指针(例如在 vector 中),因为这涉及在删除对象时进行额外的簿记,这很容易出错。这样的迭代器会是什么样子。我将如何实现 custom_items() 方法?

最佳答案

来自 this answer 的布局迭代器适配器可以很容易地被迫处理场景项目:

// https://github.com/KubaO/stackoverflown/tree/master/questions/scene-iterator-adapter-32567814
#include <QGraphicsScene>
#include <QList>
#include <QDebug>
#include <utility>

template<class> class IterableSceneAdapter;

template<typename IT>
class ItemListIterator {
   const QList<QGraphicsItem*> * m_items;
   int m_index;
   friend class IterableSceneAdapter<IT>;
   ItemListIterator(const QList<QGraphicsItem*> * items, int dir) :
      m_items(items), m_index(dir>0 ? -1 : items->count()) {
      if (dir > 0) ++*this;
   }
   friend QDebug operator<<(QDebug dbg, const ItemListIterator & it) {
      return dbg << (it.m_items->isEmpty() ? nullptr : it.m_items->first()->scene())
                 << it.m_index;
   }
   friend void swap(ItemListIterator& a, ItemListIterator& b) {
      using std::swap;
      swap(a.m_items, b.m_items);
      swap(a.m_index, b.m_index);
   }
   template <typename T> T item_cast(QGraphicsItem* item) {
      // use qgraphicsitem_cast if you don't have RTTI enabled
      return dynamic_cast<T>(item);
   }
public:
   ItemListIterator() : m_index(0) {}
   ItemListIterator(const ItemListIterator & o) :
      m_items(o.m_items), m_index(o.m_index) {}
   ItemListIterator(ItemListIterator && o) { swap(*this, o); }
   ItemListIterator & operator=(ItemListIterator o) {
      swap(*this, o);
      return *this;
   }
   IT * operator*() const { return static_cast<IT*>(m_items->at(m_index)); }
   const ItemListIterator & operator++() {
      while (++m_index < m_items->count() && !item_cast<IT*>(m_items->at(m_index)));
      return *this;
   }
   ItemListIterator operator++(int) {
      ItemListIterator temp{*this};
      ++*this;
      return temp;
   }
   const ItemListIterator & operator--() {
      while (!item_cast<IT*>(m_items->at(--m_index)) && m_index > 0);
      return *this;
   }
   ItemListIterator operator--(int) {
      ItemListIterator temp(*this);
      --*this;
      return temp;
   }
   bool operator==(const ItemListIterator & o) const { return m_index == o.m_index; }
   bool operator!=(const ItemListIterator & o) const { return m_index != o.m_index; }
};

template <class IT = QGraphicsItem>
class IterableSceneAdapter {
   const QList<QGraphicsItem*> m_items;
public:
   typedef ItemListIterator<IT> iterator;
   typedef iterator const_iterator;
   IterableSceneAdapter(QGraphicsScene * scene) : m_items(scene->items()) {}
   const_iterator begin() const { return const_iterator(&m_items, 1); }
   const_iterator end() const { return const_iterator(&m_items, -1); }
   const_iterator cbegin() const { return const_iterator(&m_items, 1); }
   const_iterator cend() const { return const_iterator(&m_items, -1); }
};

template <class IT = QGraphicsItem>
class ConstIterableSceneAdapter : public IterableSceneAdapter<const IT> {
public:
   ConstIterableSceneAdapter(const QGraphicsScene * scene) :
      IterableSceneAdapter<const IT>(const_cast<QGraphicsScene*>(scene)) {}
};

用法如下:

int main(int argc, char ** argv) {
   QApplication app(argc, argv);
   tests();
   QGraphicsScene s;
   QGraphicsLineItem b1, b3;
   QGraphicsEllipseItem b2;

   s.addItem(&b1);
   s.addItem(&b2);
   s.addItem(&b3);

   // Iterate all item types as constant items
   qDebug() << "all, range-for";
   for (auto item : ConstIterableSceneAdapter<>(&s)) qDebug() << item;
   qDebug() << "all, Q_FOREACH";
   Q_FOREACH (const QGraphicsItem * item, ConstIterableSceneAdapter<>(&s)) qDebug() << item;

   // Iterate ellipses only
   qDebug() << "ellipses, range-for";
   for (auto item : IterableSceneAdapter<QGraphicsEllipseItem>(&s)) qDebug() << item;
   qDebug() << "ellipses, Q_FOREACH";
   Q_FOREACH (QGraphicsEllipseItem * item, IterableSceneAdapter<QGraphicsEllipseItem>(&s)) qDebug() << item;
}

一些基本的测试:

#include <QtWidgets>

void tests() {
   QGraphicsScene s;
   QGraphicsLineItem b1, b3;
   QGraphicsEllipseItem b2;

   IterableSceneAdapter<> s0{&s};
   auto i0 = s0.begin();
   qDebug() << i0; Q_ASSERT(i0 == s0.begin() && i0 == s0.end());

   s.addItem(&b1);
   s.addItem(&b2);
   s.addItem(&b3);

   IterableSceneAdapter<> s1{&s};
   auto i1 = s1.begin();
         qDebug() << i1; Q_ASSERT(i1 == s1.begin() && i1 != s1.end());
   ++i1; qDebug() << i1; Q_ASSERT(i1 != s1.begin() && i1 != s1.end());
   ++i1; qDebug() << i1; Q_ASSERT(i1 != s1.begin() && i1 != s1.end());
   ++i1; qDebug() << i1; Q_ASSERT(i1 != s1.begin() && i1 == s1.end());
   --i1; qDebug() << i1; Q_ASSERT(i1 != s1.begin() && i1 != s1.end());
   --i1; qDebug() << i1; Q_ASSERT(i1 != s1.begin() && i1 != s1.end());
   --i1; qDebug() << i1; Q_ASSERT(i1 == s1.begin() && i1 != s1.end());

   IterableSceneAdapter<QGraphicsEllipseItem> s2{&s};
   auto i2 = s2.begin();
         qDebug() << i2; Q_ASSERT(i2 == s2.begin() && i2 != s2.end());
   ++i2; qDebug() << i2; Q_ASSERT(i2 != s2.begin() && i2 == s2.end());
   --i2; qDebug() << i2; Q_ASSERT(i2 == s2.begin() && i2 != s2.end());
}

关于c++ - QGraphicsScene 中元素的迭代器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32567814/

相关文章:

c++ - 为什么调用直接父类的函数而不是祖父类的函数。这称为函数覆盖吗?

c++ - OpenAL 捕获音频时出错

c++ - 简单的 C++ 运算符重载帮助

c++ - 从 xml QT 4.8 C++ 获取类型

未捕获 C++ 异常(Qt 项目)

c++ - 如果列表为空,std list 反向迭代器的尾后迭代器将失效

java - 在树集中的迭代器中具有起点的迭代器

c++ - 如何确保 vector 中访问的索引存在?

c++ - 我正在尝试使用 No matching function for call to 解决问题

python - 从大量对象中创建对象的最优化方法