一、简述

由于 QGraphicsScene 和 QGraphicsItem 的大多数遍历函数例如items()、selectedItems()、collidingItems()、childItems()返回一个 QList 列表在遍历列表的时候通常需要对其中的 QGraphicsItem 进行类型检测与转换以确定实际的 item。

二、类型检测

QGraphicsItem 中包含两个与类型相关的枚举值

  1. enum {
  2. Type = 1,
  3. UserType = 65536
  4. };

2.1 QGraphicsItem::Type

QGraphicsItem::Type 是标准 item 类中 virtual type() 函数返回的类型值。所有标准 item 与唯一的 Type 值相关联。
例如QGraphicsPathItem::type() 返回的值为 2。

  1. class QGraphicsPathItem : public QAbstractGraphicsShapeItem
  2. {
  3. public:
  4. enum { Type = 2 };
  5. int type() const { return Type; }
  6. ...
  7. };

其他标准 item 类似Type 分别为 3、4、5……

2.2 QGraphicsItem::UserType

QGraphicsItem::UserType 是自定义 itemQGraphicsItem 或任何标准 item 的子类的最小允许类型值。该值与 QGraphicsItem::type() 的重新实现结合使用并声明一个 Type 枚举值。
例如:

  1. class CustomItem : public QGraphicsItem
  2. {
  3. public:
  4. enum { Type = UserType + 1 };
  5. int type() const
  6. {
  7. // 针对该 item 启用 qgraphicsitem_cast
  8. return Type;
  9. }
  10. ...
  11. };

注意要使 qgraphicsitem_cast 与自定义 item 一起正常工作需要为其重新实现 type() 函数。

三、类型转换

  1. T qgraphicsitem_cast(QGraphicsItem *item)

如果 item 是类型 T返回指定 item 转换为类型 T否则返回 0。

  1. template <class T> inline T qgraphicsitem_cast(const QGraphicsItem *item)
  2. {
  3. typedef typename QtPrivate::remove_cv<typename QtPrivate::remove_pointer<T>::type>::type Item;
  4. return int(Item::Type) == int(QGraphicsItem::Type)
  5. || (item && int(Item::Type) == item->type()) ? static_cast<T>(item) : 0;
  6. }

四、实际应用

下面以获取 QGraphicsScene 中的所有 items 为例

  1. QList<QGraphicsItem*> items = scene->items();
  2. foreach (QGraphicsItem* item, items) {
  3. if (item->type() == QGraphicsRectItem::Type) { // 矩形
  4. QGraphicsRectItem* rect = qgraphicsitem_cast<QGraphicsRectItem*>(item);
  5. // 访问 QGraphicsRectItem 的成员
  6. } else if (item->type() == QGraphicsLineItem::Type) { // 直线
  7. QGraphicsLineItem* line = qgraphicsitem_cast<QGraphicsLineItem*>(item);
  8. // 访问 QGraphicsLineItem 的成员
  9. } else if (item->type() == QGraphicsProxyWidget::Type) { // 代理 Widget
  10. QGraphicsProxyWidget* proxyWidget = qgraphicsitem_cast<QGraphicsProxyWidget*>(item);
  11. QLabel* label = qobject_cast<QLabel*>(proxyWidget->widget());
  12. // 访问 QLabel 的成员
  13. } else if (item->type() == CustomItem::Type) { // 自定义 Item
  14. CustomItem* customItem = qgraphicsitem_cast<CustomItem*>(item);
  15. // 访问 CustomItem 的成员
  16. } else {
  17. // 其他类型 item
  18. }
  19. }

首先根据 type() 判断 item 的类型。然后通过 qgraphicsitem_cast 进行转换。这样既可以访问标准 item也可以通过 QGraphicsProxyWidget 访问 QWidget以及访问自定义 item。