当前位置: 首页 > news >正文

GraphicsView之DiagramScene案例

GraphicsView之DiagramScene案例

GraphicsView之DiagramScene案例

目录
  • GraphicsView之DiagramScene案例
    • 1. DiagramScene使用
      • 1.1 官方案例源代码
      • 1.2 运行截图
    • 2.1 核心功能分析实现
      • 2.2 场景背景替换
      • 2.2 添加基础图形
      • 2.3 添加指向箭头
      • 2.4 移除基础图形
    • 3. 小功能实现
      • 3.1颜色 + 按钮菜单
      • 3.2 DiagramItem的缩略图


箴言:

淮上与友人别 [唐] 郑谷

扬子江头杨柳春,杨花愁杀渡江人。
数声风笛离亭晚,君向潇湘我向秦

1. DiagramScene使用

1.1 官方案例源代码

在Qt Creator的案例中,搜寻 “Diagram Scene”即可打开项目源码。

下面展示的代码可能和在命名上不一致,但是功能一样。

1.2 运行截图

image-20260411083957227

2.1 核心功能分析实现

2.2 场景背景替换

直接通过 setBackgroundBrush 完成

void MainWindow::backgroundButtonGroupClicked(QAbstractButton *button)
{QList<QAbstractButton *> buttons = backgroundButtonGroup->buttons();foreach (QAbstractButton *myButton, buttons) {if (myButton != button)button->setChecked(false);}QString text = button->text();if (text == tr("Blue Grid"))scene->setBackgroundBrush(QPixmap(":/images/background1.png"));else if (text == tr("White Grid"))scene->setBackgroundBrush(QPixmap(":/images/background2.png"));else if (text == tr("Gray Grid"))scene->setBackgroundBrush(QPixmap(":/images/background3.png"));elsescene->setBackgroundBrush(QPixmap(":/images/background4.png"));scene->update();view->update();
}

2.2 添加基础图形

  1. 选择左侧QToolBox中的基础流程图,进行点击时,设置当前Scene的状态为下面中的一种:
enum Mode { InsertItem, InsertLine, InsertText, MoveItem 
  • InsertItem: 插入基本图形;
  • InsertLine:两个基本图元之间的线条;
  • InsertText:插入文本;
  • MoveItem:移动图元。
void MainWindow::buttonGroupClicked(int id)
{QList<QAbstractButton *> buttons = m_buttonGroup->buttons();foreach (QAbstractButton *button, buttons){if (m_buttonGroup->button(id) != button)button->setChecked(false);}if (id == InsertTextButton){m_scene->setMode(UDiagramScene::InsertText);}else{m_scene->setItemType(UDiagramItem::EDiagramType(id));m_scene->setMode(UDiagramScene::InsertItem);}
}
  1. 点击中间视图中的任意位置,将会生成一个对应的图元。

基本的图形形状为:

UDiagramItem::UDiagramItem(UDiagramItem::EDiagramType diagramType, QMenu *contextMenu, QGraphicsItem *parent) :QGraphicsPolygonItem(parent)
{myDiagramType = diagramType;myContextMenu = contextMenu;QPainterPath path;switch (myDiagramType){case StartEnd: // 有问题, 绘制的坐标应该由负数的,现导致形状出现在全正的区间.path.moveTo(200, 50);path.arcTo(150, 0, 50, 50, 0, 90);path.arcTo(50, 0, 50, 50, 90, 90);path.arcTo(50, 50, 50, 50, 180, 90);path.arcTo(150, 50, 50, 50, 270, 90);path.lineTo(200, 25);myPolygon = path.toFillPolygon();break;case Conditional:myPolygon << QPointF(-100, 0) << QPointF(0, 100)<< QPointF(100, 0) << QPointF(0, -100)<< QPointF(-100, 0);break;case Step:myPolygon << QPointF(-100, -100) << QPointF(100, -100)<< QPointF(100, 100) << QPointF(-100, 100)<< QPointF(-100, -100);break;default:myPolygon << QPointF(-120, -80) << QPointF(-70, 80)<< QPointF(120, 80) << QPointF(70, -80)<< QPointF(-120, -80);break;}setPolygon(myPolygon);setFlag(QGraphicsItem::ItemIsMovable, true);setFlag(QGraphicsItem::ItemIsSelectable, true);setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
}
  1. 在场景中生成图形,同时发送信号,更改状态,防止点击视图时,再次生成。
void UDiagramScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent)
{if (mouseEvent->button() != Qt::LeftButton)return;UDiagramItem *item;switch (myMode){case InsertItem:item = new UDiagramItem(myItemType, myItemMenu);item->setBrush(myItemColor);addItem(item);item->setPos(mouseEvent->scenePos());emit itemInserted(item);break;case InsertLine:line = new QGraphicsLineItem(QLineF(mouseEvent->scenePos(), mouseEvent->scenePos()));line->setPen(QPen(myLineColor, 2));addItem(line);break;default:;}QGraphicsScene::mousePressEvent(mouseEvent);
}
void MainWindow::itemInserted(UDiagramItem *item)
{// 更改模式为 Movem_pointerTypeGroup->button(int(UDiagramScene::MoveItem))->setChecked(true);m_scene->setMode(UDiagramScene::Mode(m_pointerTypeGroup->checkedId()));m_buttonGroup->button(int(item->diagramType()))->setChecked(false);
}

2.3 添加指向箭头

当存在两个基本图元时,点击工具栏上的 "连线" 按钮,在两个图元之间绘制一条直线,将会自动调整位置来显示指向的箭头。

更改模式为插入指向的箭头

// pointerTypeGroup->addButton(linePointerButton, int(DiagramScene::InsertLine));
void MainWindow::pointerGroupClicked(int)
{scene->setMode(DiagramScene::Mode(pointerTypeGroup->checkedId()));
}

在场景的鼠标事件中,来处理线条的绘制:

void DiagramScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent)
{if (mouseEvent->button() != Qt::LeftButton)return;DiagramItem *item;switch (myMode) {case InsertLine:line = new QGraphicsLineItem(QLineF(mouseEvent->scenePos(),mouseEvent->scenePos()));line->setPen(QPen(myLineColor, 2));addItem(line);break;// ...}// ...
}void DiagramScene::mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent)
{if (myMode == InsertLine && line != 0) {QLineF newLine(line->line().p1(), mouseEvent->scenePos());line->setLine(newLine);} else if (myMode == MoveItem) {QGraphicsScene::mouseMoveEvent(mouseEvent);}
}
void DiagramScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent)
{// 1. 获取包含直线首尾点的基本图元 if (line != 0 && myMode == InsertLine) {QList<QGraphicsItem *> startItems = items(line->line().p1());if (startItems.count() && startItems.first() == line)startItems.removeFirst();QList<QGraphicsItem *> endItems = items(line->line().p2());if (endItems.count() && endItems.first() == line)endItems.removeFirst();// 删除绘制的临时直线removeItem(line);delete line;// 绘制 箭头, 添加绑定关系 + 自动更新箭头的位置信息if (startItems.count() > 0 && endItems.count() > 0 &&startItems.first()->type() == DiagramItem::Type &&endItems.first()->type() == DiagramItem::Type &&startItems.first() != endItems.first()){DiagramItem *startItem = qgraphicsitem_cast<DiagramItem *>(startItems.first());DiagramItem *endItem = qgraphicsitem_cast<DiagramItem *>(endItems.first());Arrow *arrow = new Arrow(startItem, endItem);arrow->setColor(myLineColor);startItem->addArrow(arrow);endItem->addArrow(arrow);arrow->setZValue(-1000.0);addItem(arrow);arrow->updatePosition();}}line = 0;QGraphicsScene::mouseReleaseEvent(mouseEvent);
}
void Arrow::paint(QPainter *painter, const QStyleOptionGraphicsItem *,QWidget *)
{if (myStartItem->collidesWithItem(myEndItem))return;QPen myPen = pen();myPen.setColor(myColor);qreal arrowSize = 20;painter->setPen(myPen);painter->setBrush(myColor);
//! [4] //! [5]QLineF centerLine(myStartItem->pos(), myEndItem->pos());QPolygonF endPolygon = myEndItem->polygon();QPointF p1 = endPolygon.first() + myEndItem->pos();QPointF p2;QPointF intersectPoint;QLineF polyLine;for (int i = 1; i < endPolygon.count(); ++i) {p2 = endPolygon.at(i) + myEndItem->pos();polyLine = QLineF(p1, p2);QLineF::IntersectType intersectType =polyLine.intersect(centerLine, &intersectPoint);if (intersectType == QLineF::BoundedIntersection)break;p1 = p2;}setLine(QLineF(intersectPoint, myStartItem->pos()));
//! [5] //! [6]double angle = std::atan2(-line().dy(), line().dx());QPointF arrowP1 = line().p1() + QPointF(sin(angle + M_PI / 3) * arrowSize,cos(angle + M_PI / 3) * arrowSize);QPointF arrowP2 = line().p1() + QPointF(sin(angle + M_PI - M_PI / 3) * arrowSize,cos(angle + M_PI - M_PI / 3) * arrowSize);arrowHead.clear();arrowHead << line().p1() << arrowP1 << arrowP2;
//! [6] //! [7]painter->drawLine(line());painter->drawPolygon(arrowHead);if (isSelected()) {painter->setPen(QPen(myColor, 1, Qt::DashLine));QLineF myLine = line();myLine.translate(0, 4.0);painter->drawLine(myLine);myLine.translate(0,-8.0);painter->drawLine(myLine);}
}

同时在移动基本图元时,如果基本图元存在指向箭头,也会自动更新箭头的位置。

QVariant DiagramItem::itemChange(GraphicsItemChange change, const QVariant &value)
{if (change == QGraphicsItem::ItemPositionChange) {foreach (Arrow *arrow, arrows) {arrow->updatePosition();}}return value;
}

2.4 移除基础图形

void MainWindow::deleteItem()
{foreach (QGraphicsItem *item, scene->selectedItems()) {if (item->type() == Arrow::Type) {scene->removeItem(item);Arrow *arrow = qgraphicsitem_cast<Arrow *>(item);arrow->startItem()->removeArrow(arrow);arrow->endItem()->removeArrow(arrow);delete item;}}foreach (QGraphicsItem *item, scene->selectedItems()) {if (item->type() == DiagramItem::Type)qgraphicsitem_cast<DiagramItem *>(item)->removeArrows();scene->removeItem(item);delete item;}
}

3. 小功能实现

3.1颜色 + 按钮菜单

一个QToolButton,内置一个QMenu菜单,在下拉菜单选择颜色时,可以在 下方显示对应的色块。

image-20260411092603917

创建QToolButton + QMenu

    fontColorToolButton = new QToolButton;fontColorToolButton->setPopupMode(QToolButton::MenuButtonPopup);fontColorToolButton->setMenu(createColorMenu(SLOT(textColorChanged()), Qt::black));textAction = fontColorToolButton->menu()->defaultAction();fontColorToolButton->setIcon(createColorToolButtonIcon(":/images/textpointer.png", Qt::black));fontColorToolButton->setAutoFillBackground(true);

绘制QToolButton的整体QIcon

QIcon MainWindow::createColorToolButtonIcon(const QString &imageFile, QColor color)
{QPixmap pixmap(50, 80);pixmap.fill(Qt::transparent);// 在 pixmap 上作画QPainter painter(&pixmap);QPixmap image(imageFile);// 剧中绘制的矩形,可简化为 painter.drawPixmap(image, target);QRect target(4, 0, 42, 43);QRect source(0, 0, 42, 43);painter.fillRect(QRect(0, 60, 50, 80), color);painter.drawPixmap(target, image, source);return QIcon(pixmap);
}

进行下拉切换颜色菜单时,动态创建QIcon

void MainWindow::textColorChanged()
{textAction = qobject_cast<QAction *>(sender());fontColorToolButton->setIcon(createColorToolButtonIcon(":/images/textpointer.png",qvariant_cast<QColor>(textAction->data())));textButtonTriggered();
}

3.2 DiagramItem的缩略图

可以通过绘制方式,获取DiagramIemIcon,在QToolBox中进行显示。

QPixmap DiagramItem::image() const
{QPixmap pixmap(250, 250);pixmap.fill(Qt::transparent);QPainter painter(&pixmap);painter.setPen(QPen(Qt::black, 8));painter.translate(125, 125);painter.drawPolyline(myPolygon);return pixmap;
}
http://www.jsqmd.com/news/622833/

相关文章:

  • ESP32CAM无线刷固件避坑指南:从Docker版ESPHome到HomeAssistant全流程
  • **发散创新:基于RSA与AES混合加密策略的文件安全传输方案设计与实践**
  • 别再折腾 CMake 了!Craft:让 C++ 拥有了如同 Rust 般丝滑的开发体验!
  • Matlab美化box图:隐藏特定边框刻度线的实用技巧
  • HunyuanVideo-Foley与Ollama集成:在本地便捷管理和调用音效模型
  • golang如何实现备忘录模式_golang备忘录模式实现方案
  • 永辉超市卡回收攻略:使用范围解析与回收心得 - 团团收购物卡回收
  • BAAI/bge-m3语义分析引擎5分钟快速部署:小白也能搭建的RAG检索验证工具
  • 不止于看图说话:用GLM-4.5V和vLLM API快速搭建一个智能图片分析小工具(附完整Python代码)
  • PowerPaint-V1 Gradio快速部署:国内镜像加速,消费级显卡也能流畅运行
  • RaspberryPi 4B 中文输入法配置全攻略:从Fcitx安装到实战应用
  • 快速部署AI图像编辑环境:Qwen-Image-2512-ComfyUI教程
  • WarcraftHelper技术指南:让经典魔兽争霸III在现代系统上完美运行
  • 郑州金诺售后服务费用怎么收费靠谱吗 - 工业设备
  • 4大核心技术解析:WorkshopDL如何实现跨平台Steam创意工坊下载
  • OneAPI Istio服务网格集成:微服务架构下API流量治理实践
  • DHT_N128库深度解析:嵌入式单总线温湿度驱动设计与移植
  • 保姆级教程:给你的UniApp安卓项目集成Keep_Alive保活插件(附完整代码)
  • TP4592 带使能控制的锂电池充放电解决方案
  • Mermaid Live Editor:颠覆性实时图表创作工具完全指南
  • 如何快速掌握AMD处理器调优:SMUDebugTool终极配置指南
  • 2026年沧州靠谱的文化展厅设计施工企业,费用怎么算 - 工业品牌热点
  • 如何免费破解网盘限速:网盘直链下载助手完全指南
  • MedGemma X-Ray效果实录:AI在急诊场景下对气胸、大量胸腔积液等危急征象秒级预警
  • 实践指南:vxe-table单元格合并规则的高效封装与性能优化
  • 3步解决可视化协作困境:Mermaid在线编辑器的颠覆性应用
  • AD20新手必看:5分钟搞定Gerber文件生成,避免打板翻车
  • 3分钟免费搞定Microsoft Word APA第7版格式:学术论文参考文献终极解决方案
  • 讲讲2026年长沙特产伴手礼,承源百年古酒馆产品靠谱吗 - myqiye
  • Kandinsky-5.0-I2V-Lite-5s在AI Agent工作流中的应用:自动生成任务执行演示