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

Qt中如何使用wrapper class实现QGraphics组合符号整体选中

问题描述

基于Qt Graphics View框架开发的绘图程序中,QGraphicsScene承载大量QGraphicsItem图元:程序从文件读取直线、圆弧、圆形的坐标数据存入map结构,绘制阶段遍历map将坐标传入Qt对应API完成渲染。
当前存在组合符号无法整体选中的异常:单个符号由4条直线、1段圆弧、1个圆形组合构成时,点击其中任意一条直线仅会选中该直线本身,点击圆弧也仅选中对应圆弧,不符合「点击符号任意部分即可选中整个符号」的预期。

现有建议是通过wrapper class(包装类)实现需求,但不清楚具体实现逻辑。


现有代码结构

mySymbol 类定义

class mySymbol 
{
    struct Line {
       std::tuple<int, int, int, int> line;
    };
   struct Arc {
       std::tuple<int, int, int, int, int, int> arc;  
    };
  struct Circle {
       std::tuple<int, int, int> circle;
   };

  struct Rect 
  {
      std::vector<Line> allLines;
      std::vector<Arc> allArcs;
      std::vector<Circle> allCircles;
  }

std::map<std::string, Rect> allShapes;
}

DrawShapes.h

class DrawShapes
{
  void createSymbol(map<std::string, Rect>);
}

DrawShapes.cpp

void DrawShapes :: createSymbol(map<std::string, Rect> allShapes)
{
   QGraphicsItemGroup * parentGroup = new QGraphicsItemGroup;
   for (auto iter = allShapes.begin(); iter != allShapes.end(); ++iter)
  {
     if (iter->second.allLines.size() > 0) 
      {
         for (unsigned int i = 0; i < iter->second.allLines.size(); i++) 
           {
              // 提取直线的4个坐标值first、second、third、fourth
              myLine* line = new myLine(first,second,third,fourth);
              line->DrawLine();
              parentGroup->addToGroup(line);
              scene->addItem(static_cast<QGraphicsLineItem*>(line));
           }
     }
     // 圆弧、圆形的处理逻辑与直线一致
}      

myLine.h

class myLine: public QGraphicsLineItem
{
public:
    myLine();
    myLine(int x1,int y1,int x2 ,int y2,QGraphicsItem *parent = nullptr)
        : QGraphicsLineItem(x1,y1,x2,y2,parent)
    {}
    void DrawLine();
};     

myLine.cpp

void myLine::DrawLine()
{
    this->setPen(QPen(QColor("blue"), 1));
    this->setFlag(QGraphicsItem::ItemIsSelectable);
}

根因分析

现有代码存在3个核心逻辑错误,导致无法实现整体选中:

  • 所有子图元(直线、圆弧、圆形)都单独设置了ItemIsSelectable标志,子图元自身可捕获选中事件,不会将事件传递给上层容器
  • 每个子图元被单独调用scene->addItem()加入场景,虽然调用了addToGroup,但图元归属逻辑错误,QGraphicsItemGroup没有真正成为子图元的父容器
  • 仅创建了一个全局的parentGroup,所有符号的所有图元都被加入同一个组,最终会导致所有内容成为一个不可分割的整体,不符合单个符号独立选中的需求

修复方案

基础版:基于QGraphicsItemGroup实现

1. 调整子图元属性

子图元不需要单独持有选中能力,移除子图元的可选中标志,将选中逻辑统一交给上层符号容器处理。修改myLine::DrawLine:

void myLine::DrawLine()
{
    this->setPen(QPen(QColor("blue"), 1));
    // 移除原有的setFlag(QGraphicsItem::ItemIsSelectable)逻辑
    this->setFlag(QGraphicsItem::ItemIsSelectable, false);
}

圆弧、圆形类做相同修改。

2. 修正组创建与图元挂载逻辑

每个独立符号对应一个独立的QGraphicsItemGroup,禁止所有图元共用同一个全局组;子图元创建时直接将组作为父对象传入,不需要单独将子图元加入场景,组被加入场景时会自动将所有子图元挂载到场景下。
修改后的createSymbol核心逻辑:

void DrawShapes :: createSymbol(map<std::string, Rect> allShapes)
{
   // 移除全局parentGroup定义
   for (auto iter = allShapes.begin(); iter != allShapes.end(); ++iter)
  {
     // 为每个独立符号创建专属组
     QGraphicsItemGroup * symbolGroup = new QGraphicsItemGroup;
     // 仅给组设置可选中属性,组是选中事件的响应主体
     symbolGroup->setFlag(QGraphicsItem::ItemIsSelectable, true);
     
     if (iter->second.allLines.size() > 0) 
      {
         for (unsigned int i = 0; i < iter->second.allLines.size(); i++) 
           {
              // 提取直线4个坐标值
              // 创建直线时直接将symbolGroup作为父对象传入,自动完成组挂载
              myLine* line = new myLine(first,second,third,fourth, symbolGroup);
              line->DrawLine();
              // 移除单独调用addToGroup、scene->addItem添加子图元的逻辑
           }
     }
     // 圆弧、圆形做相同处理,创建时传入symbolGroup作为父对象
     
     // 最后仅需将整个符号组加入场景即可
     scene->addItem(symbolGroup);
}
}

进阶版:自定义包装类实现

如果需要给符号绑定业务属性(比如符号ID、自定义样式、业务逻辑),可以继承QGraphicsItemGroup实现自定义包装类,替代原生的QGraphicsItemGroup:

// 自定义符号包装类
class MySymbolItem : public QGraphicsItemGroup
{
public:
    MySymbolItem(const std::string& symbolId, QGraphicsItem* parent = nullptr)
        : QGraphicsItemGroup(parent), m_symbolId(symbolId)
    {
        // 组默认持有可选中属性
        this->setFlag(QGraphicsItem::ItemIsSelectable, true);
    }
    // 可扩展自定义业务方法,比如获取符号ID、批量修改图元样式等
    std::string getSymbolId() const { return m_symbolId; }
private:
    std::string m_symbolId; // 存储符号业务标识
};

创建符号时实例化自定义的MySymbolItem作为容器即可,后续获取选中项时可直接转换为MySymbolItem*类型,读取绑定的业务数据,扩展性比原生组更强。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 00:18:15