QT-可拖拉绘图工具

一、演示效果

在这里插入图片描述

二、关键程序

#include "diagramscene.h"
#include "arrow.h"

#include <QTextCursor>
#include <QGraphicsSceneMouseEvent>
#include <QDebug>

QPen const DiagramScene::penForLines = QPen(QBrush(QColor(Qt::black)), 2, Qt::PenStyle::DashLine);

DiagramScene::DiagramScene(QMenu *itemMenu, QObject *parent)
    : QGraphicsScene(parent)
{
   
    myItemMenu = itemMenu;
    myMode = MoveItem;
    myItemType = DiagramItem::Step;
    line = nullptr;
    textItem = nullptr;
    myItemColor = Qt::white;
    myTextColor = Qt::black;
    myLineColor = Qt::black;
}

void DiagramScene::setLineColor(const QColor &color)
{
   
    myLineColor = color;
    foreach (QGraphicsItem* p, selectedItems()) {
   
        if (p->type() == Arrow::Type) {
   
            Arrow* item = qgraphicsitem_cast<Arrow*>(p);
            item->setColor(myLineColor);
            update();
        }
    }
}

void DiagramScene::setTextColor(const QColor &color)
{
   
    myTextColor = color;
    foreach (QGraphicsItem* p, selectedItems()) {
   
        if (p->type() == DiagramTextItem::Type) {
   
            DiagramTextItem* item = qgraphicsitem_cast<DiagramTextItem*>(p);
            item->setDefaultTextColor(myTextColor);
        }
    }
}
//! [2]

//! [3]
void DiagramScene::setItemColor(const QColor &color)
{
   
    myItemColor = color;
    foreach (QGraphicsItem* p, selectedItems()) {
   
        if (p->type() == DiagramItem::Type) {
   
            DiagramItem* item = qgraphicsitem_cast<DiagramItem*>(p);
            item->setBrush(myItemColor);
        }
    }
}

void DiagramScene::setFont(const QFont &font)
{
   
    myFont = font;
    foreach (QGraphicsItem* p, selectedItems()) {
   
        if (p->type() == DiagramTextItem::Type) {
   
            DiagramTextItem* item = qgraphicsitem_cast<DiagramTextItem*>(p);
            item->setFont(myFont);
        }
    }
}

void DiagramScene::deleteItems(QList<QGraphicsItem*> const& items) {
   
    qDebug() << "delete items" << items;

    QList<QGraphicsItem*> diagramItems;
    foreach (QGraphicsItem *item, items) {
   
        if (item->type() == Arrow::Type) {
   
            removeItem(item);
            Arrow *arrow = qgraphicsitem_cast<Arrow *>(item);
            arrow->startItem()->removeArrow(arrow);
            arrow->endItem()->removeArrow(arrow);
            delete item;
        } else diagramItems.append(item);
    }

    foreach (QGraphicsItem *item, diagramItems) {
   
        if (item->type() == DiagramItem::Type)
             qgraphicsitem_cast<DiagramItem *>(item)->removeArrows();
        removeItem(item);
        delete item;
    }
}
//! [4]

void DiagramScene::setMode(Mode mode)
{
   
    myMode = mode;
}

void DiagramScene::setItemType(DiagramItem::DiagramType type)
{
   
    myItemType = type;
}

//! [5]
void DiagramScene::editorLostFocus(DiagramTextItem *item)
{
   
    QTextCursor cursor = item->textCursor();
    cursor.clearSelection();
    item->setTextCursor(cursor);

    if (item->toPlainText().isEmpty()) {
   
        removeItem(item);
        item->deleteLater();
    } else {
   
        if (item->contentIsUpdated()) {
   
            qDebug() << "content update ---";
            emit textChanged();
        }
    }

}

void DiagramScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
   
    if (mouseEvent->button() != Qt::LeftButton)
        return;

    DiagramItem *item;
    switch (myMode) {
   
        case InsertItem:
            item = new DiagramItem(myItemType, myItemMenu);
            item->setBrush(myItemColor);
            addItem(item);
            item->setPos(mouseEvent->scenePos());
            qDebug() << "insert item at: " << mouseEvent->scenePos();
            qDebug() << "\ttype: " << myItemType << " color: " << myItemColor;
            emit itemInserted(item);
            hasItemSelected = itemAt(mouseEvent->scenePos(), QTransform()) != nullptr;
            break;

        case InsertLine:
            if (itemAt(mouseEvent->scenePos(), QTransform()) == nullptr) break;
            line = new QGraphicsLineItem(QLineF(mouseEvent->scenePos(),
                                        mouseEvent->scenePos()));
            line->setPen(QPen(myLineColor, 2));
            addItem(line);
            break;

        case InsertText:
            textItem = new DiagramTextItem();
            textItem->setFont(myFont);
            textItem->setTextInteractionFlags(Qt::TextEditorInteraction);
            textItem->setZValue(1000.0);
            connect(textItem, SIGNAL(lostFocus(DiagramTextItem*)),
                    this, SLOT(editorLostFocus(DiagramTextItem*)));
            connect(textItem, SIGNAL(selectedChange(QGraphicsItem*)),
                    this, SIGNAL(itemSelected(QGraphicsItem*)));
            addItem(textItem);
            textItem->setDefaultTextColor(myTextColor);
            textItem->setPos(mouseEvent->scenePos());
            emit textInserted(textItem);
            qDebug() << "text inserted at" << textItem->scenePos();
            break;

    default:
        hasItemSelected = itemAt(mouseEvent->scenePos(), QTransform()) != nullptr;
    }
    QGraphicsScene::mousePressEvent(mouseEvent);
}

void DiagramScene::mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent) {
   
    if (myMode == InsertLine && line != nullptr) {
   
        QLineF newLine(line->line().p1(), mouseEvent->scenePos());
        line->setLine(newLine);
    } else if (myMode == MoveItem) {
   
        if (hasItemSelected)
            mouseDraggingMoveEvent(mouseEvent);
        QGraphicsScene::mouseMoveEvent(mouseEvent);
    }
}

void DiagramScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent) {
   
    hasItemSelected = false;

    // leave sticky mode
    horizontalStickyMode = false;
    verticalStickyMode = false;
    foreach(QGraphicsItem* p, selectedItems())
        p->setFlag(QGraphicsItem::ItemIsMovable);

    clearOrthogonalLines();
    if (line != nullptr && 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();
            emit arrowInserted();
        }
    }

    line = nullptr;
    QGraphicsScene::mouseReleaseEvent(mouseEvent);
}

void DiagramScene::wheelEvent(QGraphicsSceneWheelEvent* wheelEvent) {
   
    // ctrl key is being pressed
    if ((wheelEvent->modifiers() & Qt::KeyboardModifier::ControlModifier) != 0) {
   
        emit scaleChanging(wheelEvent->delta());
        wheelEvent->accept();
    } else {
   
        QGraphicsScene::wheelEvent(wheelEvent);
    }
}

void DiagramScene::mouseDraggingMoveEvent(QGraphicsSceneMouseEvent* event) {
   
    clearOrthogonalLines();
    if ((event->buttons() & Qt::LeftButton) != 0 && selectedItems().size() == 1) {
   
        QGraphicsItem* itemUnderCursor = selectedItems().first();
        QPointF curCenter = itemUnderCursor->scenePos();
        QPointF const& mousePos = event->scenePos();

        foreach(QGraphicsItem* p, items()) {
   
            if (p->type() != DiagramItem::Type || p == itemUnderCursor) continue;

            DiagramItem* item = qgraphicsitem_cast<DiagramItem*>(p);
            QPointF const& objPoint = item->scenePos();
            LineAttr lineAttr;

            tryEnteringStickyMode(itemUnderCursor, objPoint, mousePos);

            if ((lineAttr = getPointsRelationship(objPoint, curCenter)) != Other) {
   
                if ((lineAttr & Horizontal) != 0) {
   
                    QGraphicsLineItem* newHLine = new QGraphicsLineItem();
                    newHLine->setLine(QLineF(QPointF(0, objPoint.y()),
                                             QPointF(sceneRect().width(), objPoint.y())));
                    newHLine->setPen(penForLines);
                    orthogonalLines.append(newHLine);
                }
                if ((lineAttr & Vertical) != 0) {
   
                    QGraphicsLineItem* newVLine = new QGraphicsLineItem();
                    newVLine->setLine(QLineF(QPointF(objPoint.x(), 0),
                                             QPointF(objPoint.x(), sceneRect().height())));
                    newVLine->setPen(penForLines);
                    orthogonalLines.append(newVLine);
                }
            }
        }
        tryLeavingStickyMode(itemUnderCursor, mousePos);
    }
    foreach(QGraphicsLineItem* p, orthogonalLines) {
   
        addItem(p);
    }
}

void DiagramScene::clearOrthogonalLines() {
   
    foreach(QGraphicsLineItem* p, orthogonalLines) {
   
        removeItem(p);
        delete p;
    }
    orthogonalLines.clear();
}

bool DiagramScene::closeEnough(qreal x, qreal y, qreal delta) {
   
    return std::abs(x - y) < delta;
}

DiagramScene::LineAttr DiagramScene::getPointsRelationship(const QPointF& p1,
                                                           const QPointF& p2) {
   
    int ret = Other;
    ret |= closeEnough(p1.x(), p2.x(), Delta) ? Vertical : Other;
    ret |= closeEnough(p1.y(), p2.y(), Delta) ? Horizontal : Other;
    return static_cast<DiagramScene::LineAttr>(ret);
}

void DiagramScene::tryEnteringStickyMode(QGraphicsItem* item, const QPointF& target,
                                         const QPointF& mousePos) {
   
    QPointF const& itemPos = item->scenePos();
    if (!verticalStickyMode) {
   
        if (closeEnough(itemPos.x(), target.x(), stickyDistance)) {
     // enter stickyMode
            verticalStickyMode = true;
            verticalStickPoint = mousePos;
            item->setFlag(QGraphicsItem::ItemIsMovable, false);
            item->setPos(QPointF(target.x(), itemPos.y()));
        }
    }
    if (!horizontalStickyMode) {
   
        if (closeEnough(itemPos.y(), target.y(), stickyDistance)) {
   
            horizontalStickyMode = true;
            horizontalStickPoint = mousePos;
            item->setFlag(QGraphicsItem::ItemIsMovable, false);
            item->setPos(QPointF(itemPos.x(), target.y()));
        }
    }
}

void DiagramScene::tryLeavingStickyMode(QGraphicsItem* item, const QPointF& mousePos) {
   
    if (verticalStickyMode) {
    // already in stickyMode, item should be able to move vertically
        item->moveBy(0, mousePos.y() - verticalStickPoint.y());
        verticalStickPoint.setY(mousePos.y());

        // when to exit stickyMode?
        if (!closeEnough(mousePos.x(), verticalStickPoint.x(), stickyDistance)) {
   
            verticalStickyMode = false;
            item->setFlag(QGraphicsItem::ItemIsMovable, true);
        }
    }
    if (horizontalStickyMode) {
   
        item->moveBy(mousePos.x() - horizontalStickPoint.x(), 0);
        horizontalStickPoint.setX(mousePos.x());

        if (!closeEnough(mousePos.y(), horizontalStickPoint.y(), stickyDistance)) {
   
            horizontalStickyMode = false;
            item->setFlag(QGraphicsItem::ItemIsMovable, true);
        }
    }
}


三、下载链接

https://download.csdn.net/download/u013083044/88640602

相关推荐

  1. Qt无边框窗口拖动

    2023-12-20 00:18:02       20 阅读
  2. canvas图上绘制边框,放大缩小,拖动

    2023-12-20 00:18:02       17 阅读

最近更新

  1. TCP协议是安全的吗?

    2023-12-20 00:18:02       19 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2023-12-20 00:18:02       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2023-12-20 00:18:02       20 阅读
  4. 通过文章id递归查询所有评论(xml)

    2023-12-20 00:18:02       20 阅读

热门阅读

  1. 【缓存】一、Redis的基本使用与Redisson分布式锁

    2023-12-20 00:18:02       24 阅读
  2. 用VBA冻结excel文件的sheet1工作簿的第一行

    2023-12-20 00:18:02       39 阅读
  3. window.open的使用

    2023-12-20 00:18:02       39 阅读
  4. 【MySQL】数据类型

    2023-12-20 00:18:02       90 阅读
  5. 【难点】【LRU】146.LRU缓存

    2023-12-20 00:18:02       43 阅读
  6. 如何在Go中向错误中添加额外的信息

    2023-12-20 00:18:02       43 阅读
  7. C51--小车——PWM调速

    2023-12-20 00:18:02       35 阅读
  8. 微服务组件Gateway的学习

    2023-12-20 00:18:02       46 阅读
  9. c-Myc 抑制剂 2173505-97-8在科研领域的应用

    2023-12-20 00:18:02       38 阅读
  10. 4-Docker命令之docker history

    2023-12-20 00:18:02       29 阅读