You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Qt自定义QGraphicsItemGroup子类qgraphicsitem_cast转换失败问题

问题描述

我尝试使用line、arc、circle图元绘制AND、NAND等数字门电路符号,开发中使用QGraphicsItemGroup对构成单个符号的子图元做分组管理:单个符号包含3条直线、2段圆弧、1个圆形时,我会将所有子图元添加到QGraphicsItemGroup实例中,再将分组对象添加到scene场景中。

自定义分组类头文件定义如下:

// myGroupItem.h
class myGroupItem: public QGraphicsItemGroup 
{
      virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
}

myGroupItem.cpp实现代码如下:

void myGroupItem ::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
{
    auto copied_option = *option;
    copied_option.state &= ~QStyle::State_Selected;
    auto selected = option->state & QStyle::State_Selected;
    QGraphicsItemGroup::paint(painter, &copied_option, widget);
    if (selected) 
   {
      foreach (QGraphicsItem* item, _scene->selectedItems()) 
         {
            myGroupItem* inst = qgraphicsitem_cast<myGroupItem*>(item);
            if (!inst)
                continue;
            QList<QGraphicsItem*> childItems = inst->childItems();
            foreach (QGraphicsItem* chItem, childItems) 
            {
                if (myPoly* poly = dynamic_cast<myPoly*>(chItem)) 
                {
                    painter->save();
                    painter->setPen(QPen(option->palette.windowText(), 0, Qt::SolidLine));
                    painter->drawPath(poly->shape());
                    painter->restore();
                } 
              } else {
                    if (myLine* line = dynamic_cast<myLine*>(chItem)) {
                        painter->save();
                        painter->setPen(QPen(option->palette.windowText(), 0, Qt::SolidLine));
                        painter->drawPath(line->shape());
                        painter->restore();
                    } else {
                        if (myEllipse* ell = dynamic_cast<myEllipse*>(chItem)) {
                            painter->save();
                            painter->setPen(QPen(option->palette.windowText(), 0, Qt::SolidLine));
                            painter->drawPath(ell->shape());
                            painter->restore();
                        }
                    }
                }
            }
        }
    }
}

其余自定义图元头文件定义:

// myLine.h
class myLine : public QGraphicsLineItem 
{
  
}

// myEllipse.h
class myEllipse : public QGraphicsEllipseItem
{
  
}

门电路符号构建逻辑:

class BuildDesign()
{
    myGroupItem* groupItem = new myGroupItem();
    // 初始化所有直线图元
    myLine* line = new myLine(co-ordinate of lines);
    groupItem->addToGroup(line);      
 
    // 添加圆弧图元到分组
    groupItem->addToGroup(arc); 
 
    // 添加圆形图元到分组
    groupItem->addToGroup(ellipse);                         
     
    scene->addItem(groupItem);    
}

按照上述逻辑可正常生成门电路符号,但选中符号执行后续处理时出现异常,选中相关代码如下:

// 检测鼠标点击位置是否存在图元,存在则调用高亮函数,不存在则刷新场景
void BuildDesign::SelectObject(QEvent* event)
{
    QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
    QPointF mousePoint = _view->mapToScene(mouseEvent->pos());
    QGraphicsItem* itemUnderClick = _scene->itemAt(mousePoint, QTransform());
    
    if (!itemUnderClick)
        RefreshScene();
    else
        HighlightSelectedObject(itemUnderClick);
}
     
void BuildDesign::HighlightSelectedObject(QGraphicsItem* itemUnderClick)
{  
    myGroupItem* gItem = qgraphicsitem_cast<myGroupItem*>(itemUnderClick);
    if (gItem) {
       gItem->setSelected(true);   // 该段代码始终无法执行
    }
}   

调试时发现鼠标点击获取到的itemUnderClick有有效内存地址,但用qgraphicsitem_cast转myGroupItem*时始终返回空地址0x0,导致选中逻辑无法触发,后续遍历场景选中项列表也拿不到目标分组对象,阻塞后续开发。


问题原因

两个核心问题导致转换失败:

  • QGraphicsItemGroup默认不直接响应鼠标命中,scene->itemAt()点击组区域时,返回的是最上层的组内子图元(myLine/myEllipse/圆弧对象),根本不是myGroupItem实例,自然转换失败。
  • 自定义QGraphicsItem子类没有实现type()接口、没有定义自定义类型ID,qgraphicsitem_cast无法识别自定义类型,即使拿到组对象也可能转换失败。

修复方案

1. 兼容子图元点击选中分组

点击命中子图元时,向上遍历父对象链找到所属的myGroupItem实例,替换HighlightSelectedObject逻辑:

void BuildDesign::HighlightSelectedObject(QGraphicsItem* itemUnderClick)
{  
    QGraphicsItem* current = itemUnderClick;
    myGroupItem* targetGroup = nullptr;
    // 向上查找所属的自定义分组
    while(current) {
        targetGroup = qgraphicsitem_cast<myGroupItem*>(current);
        if(targetGroup) break;
        current = current->parentItem();
    }
    if (targetGroup) {
       // 选中前可根据需求决定是否清空其他选中状态
       _scene->clearSelection();
       targetGroup->setSelected(true);
    }
}

同时在myGroupItem构造函数中开启选中支持:

myGroupItem::myGroupItem()
{
    setFlag(QGraphicsItem::ItemIsSelectable, true);
}

2. 为自定义图元类添加类型支持

每个自定义QGraphicsItem子类都需要在类定义中声明类型枚举、重写type()方法,才能保证qgraphicsitem_cast正常工作,以myGroupItem为例:

// myGroupItem.h
class myGroupItem: public QGraphicsItemGroup 
{
public:
    // 自定义类型ID,取值需大于QGraphicsItem::UserType
    enum { Type = UserType + 1 };
    int type() const override { return Type; }

    virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
}

myLine、myEllipse、myPoly等自定义子类按相同规则添加即可,Type值依次递增不重复。

3. 修复paint函数语法错误

当前贴出的paint函数中遍历子项的大括号匹配错误,else分支和myPoly判断的层级不对应,会直接编译失败,调整大括号对应关系即可。


内容的提问来源于stack exchange,提问作者tushar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 13:51:46