#include "graphicsscene.h" #include #include #include #include #include #include #include #include #include #include #include "assert.h" GraphicsTextEdit::GraphicsTextEdit(QWidget* parent) : QTextEdit(parent) { viewport()->setWindowFlags(Qt::FramelessWindowHint); //viewport()->setAttribute(Qt::WA_TranslucentBackground); setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setContextMenuPolicy(Qt::ContextMenuPolicy::NoContextMenu); connect(this, SIGNAL(textChanged()), this, SLOT(on_textChanged())); setStyleSheet("QTextEdit{background-color: transparent; border: 3px solid red;}"); //setWindowOpacity(0.3); //setMouseTracking(true); } GraphicsTextEdit::~GraphicsTextEdit() { } void GraphicsTextEdit::setDefeaultColor(const QColor& color) { m_defeaultColor = color; setTextColor(m_defeaultColor); } #define BORDER_WIDTH 20 void GraphicsTextEdit::on_textChanged() { QFontMetrics fm(font()); QSize currentSize = this->size(); QStringList lineText = document()->toPlainText().split("\n", QString::SkipEmptyParts); int idealWidth = 0; for (const QString& str : lineText) { int width = fm.width(str); if (width > idealWidth) idealWidth = width; } int idelHeight = document()->lineCount() * fm.lineSpacing(); if (width() < idealWidth + BORDER_WIDTH) currentSize.setWidth(idealWidth + BORDER_WIDTH); if (height() < idelHeight + BORDER_WIDTH * 2) currentSize.setHeight(idelHeight + BORDER_WIDTH * 2); if (currentSize != this->size()) resize(currentSize); if (toPlainText().isEmpty()) setTextColor(m_defeaultColor); } GraphicsScene::GraphicsScene(QObject* parent) : QGraphicsScene(parent) , m_status(Normal) , m_activeItem(nullptr) , m_pen(QColor(255, 0, 0, 255)) , m_brush(QColor(255, 255, 255, 255)) , m_font(tr("SongTi")) , m_edit(nullptr) { m_ItemList.clear(); m_curItemIndex = -1; } GraphicsScene::~GraphicsScene() { for (int i = 0; i < (int)m_ItemList.size(); ++i) { delete m_ItemList[i]; m_ItemList[i] = nullptr; } m_ItemList.clear(); m_curItemIndex = -1; } void GraphicsScene::setColor(const QColor& color) { m_pen.setColor(color); m_brush.setColor(color); if (m_edit != nullptr) m_edit->setDefeaultColor(color); } void GraphicsScene::setTextSize(int size) { m_font.setPointSize(size); } void GraphicsScene::setLineWidth(int width) { m_pen.setWidth(width); } void GraphicsScene::setItemFlag(int flag) { flush(); switch (flag) { case Mask: m_status = CreateMask; break; case Rect: m_status = CreateRect; break; case Ellipse: m_status = CreateEllipse; break; case Line: m_status = CreateLine; break; case Text: m_status = CreateText; break; case Arrow: m_status = CreateArrow; break; case Pen: m_status = CreatePen; break; default: m_status = Normal; break; } } bool GraphicsScene::isCanUndo() { if (m_ItemList.empty() || -1 == m_curItemIndex) return false; return true; } void GraphicsScene::undo() { flush(); if (!isCanUndo()) return; --m_curItemIndex; updateShow(); } bool GraphicsScene::isCanRedo() { if (m_ItemList.empty() || (int)m_ItemList.size() - 1 == m_curItemIndex) return false; return true; } void GraphicsScene::redo() { flush(); if (!isCanRedo()) return; ++m_curItemIndex; updateShow(); } void GraphicsScene::clear() { flush(); if (m_ItemList.empty()) return; if (-1 == m_curItemIndex) return; if (NULL == m_ItemList[m_curItemIndex]) return; for (int i = (int)m_ItemList.size() - 1; i > m_curItemIndex; --i) { delete m_ItemList[i]; m_ItemList[i] = nullptr; m_ItemList.erase(m_ItemList.begin() + i); } m_ItemList.push_back(NULL); ++m_curItemIndex; updateShow(); } #define PI acos(-1) QPointF rotate(const QPointF& center, const QPointF& p, float angle) { float angle_ = angle; float x = (p.x() - center.x()) * cos(angle_) - (p.y() - center.y()) * sin(angle_) + center.x(); float y = (p.y() - center.y()) * cos(angle_) + (p.x() - center.x()) * sin(angle_) + center.y(); return QPointF(x, y); } float lineLength(const QPointF& p1, const QPointF& p2) { return sqrt(pow(p1.x() - p2.x(), 2) + pow(p1.y() - p2.y(), 2)); } void GraphicsScene::flush() { if (nullptr == m_activeItem) { return; } if (m_status == CreateText) { assert(typeid(*m_activeItem) == typeid(QGraphicsProxyWidget)); QString text = m_edit->document()->toPlainText(); QFont font = m_edit->font(); removeItem(m_activeItem); m_activeItem = nullptr; QGraphicsTextItem* item = addText(text, font); item->setPos(m_edit->mapToParent(QPoint())); item->setDefaultTextColor(m_pen.color()); for (int i = (int)m_ItemList.size() - 1; i > m_curItemIndex; --i) { delete m_ItemList[i]; m_ItemList[i] = nullptr; m_ItemList.erase(m_ItemList.begin() + i); } m_ItemList.push_back(item); ++m_curItemIndex; delete m_edit; m_edit = nullptr; } else { assert(typeid(*m_activeItem) != typeid(QGraphicsProxyWidget)); for (int i = (int)m_ItemList.size() - 1; i > m_curItemIndex; --i) { delete m_ItemList[i]; m_ItemList[i] = nullptr; m_ItemList.erase(m_ItemList.begin() + i); } m_ItemList.push_back(m_activeItem); ++m_curItemIndex; m_activeItem = nullptr; } } void GraphicsScene::updateShow() { if (m_ItemList.empty() || -1 == m_curItemIndex || NULL == m_ItemList[m_curItemIndex]) { while (1) { QList items = this->items(); if (items.count() < 2) break; removeItem(items.first()); } return; } int startIndex = m_curItemIndex; for (int i = m_curItemIndex; i >= 0; --i) { if (m_ItemList[i] == NULL) { break; } startIndex = i; } while (1) { QList items = this->items(); if (items.count() < 2) break; removeItem(items.first()); } for (int i = startIndex; i <= m_curItemIndex; ++i) { this->addItem(m_ItemList[i]); } } QPainterPath GraphicsScene::createArrowPath(const QPointF& p1, const QPointF& p2) { float angle; if (p2.x() == p1.x()) if (p2.y() > p1.y()) angle = PI / 2; else angle = -PI / 2; else if (p2.x() < p1.x() && std::abs(p1.y() - p2.y()) < 0.000001) angle = PI; else { angle = atan((p2.y() - p1.y()) / (p2.x() - p1.x())); if (p2.y() < p1.y() && p2.x() < p1.x()) angle -= PI; if (p2.y() > p1.y() && p2.x() < p1.x()) angle += PI; } float length = lineLength(p1, p2); QPointF p_1 = rotate(p1, QPointF(p1.x() + length - 25, p1.y() + 15), angle); QPointF p_2 = rotate(p1, QPointF(p1.x() + length - 25, p1.y() - 15), angle); QPointF p_1_half = rotate(p1, QPointF(p1.x() + length - 22, p1.y() + 10), angle); QPointF p_2_half = rotate(p1, QPointF(p1.x() + length - 22, p1.y() - 10), angle); QPainterPath path; path.moveTo(p2); path.lineTo(p_2); path.lineTo(p_2_half); path.lineTo(p1); path.lineTo(p_1_half); path.lineTo(p_1); path.lineTo(p2); return path; } void GraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent) { if (m_activeItem == nullptr) return; if (!sceneRect().contains(mouseEvent->scenePos())) return; if (m_status == CreateMask) { QGraphicsRectItem* item = reinterpret_cast(m_activeItem); QPointF pos = mouseEvent->scenePos(); float left = qMin(pos.x(), m_startPoint.x()); float top = qMin(pos.y(), m_startPoint.y()); float right = qMax(pos.x(), m_startPoint.x()); float bottom = qMax(pos.y(), m_startPoint.y()); item->setRect(QRectF(QPointF(left, top), QPointF(right, bottom))); } else if (m_status == CreateRect) { QGraphicsRectItem* item = reinterpret_cast(m_activeItem); QPointF pos = mouseEvent->scenePos(); float left = qMin(pos.x(), m_startPoint.x()); float top = qMin(pos.y(), m_startPoint.y()); float right = qMax(pos.x(), m_startPoint.x()); float bottom = qMax(pos.y(), m_startPoint.y()); item->setRect(QRectF(QPointF(left, top), QPointF(right, bottom))); } else if (m_status == CreateEllipse) { QGraphicsEllipseItem* item = reinterpret_cast(m_activeItem); QPointF pos = mouseEvent->scenePos(); float left = qMin(pos.x(), m_startPoint.x()); float top = qMin(pos.y(), m_startPoint.y()); float right = qMax(pos.x(), m_startPoint.x()); float bottom = qMax(pos.y(), m_startPoint.y()); item->setRect(QRectF(QPointF(left, top), QPointF(right, bottom))); } else if (m_status == CreateLine) { QGraphicsLineItem* item = reinterpret_cast(m_activeItem); item->setLine(QLineF(m_startPoint, mouseEvent->scenePos())); } else if (m_status == CreateArrow) { QGraphicsPathItem* item = reinterpret_cast(m_activeItem); item->setPath(createArrowPath(m_startPoint, mouseEvent->scenePos())); } else if (m_status == CreatePen) { QGraphicsPathItem* item = reinterpret_cast(m_activeItem); QPainterPath path = item->path(); path.lineTo(mouseEvent->scenePos()); item->setPath(path); } } void GraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent) { if (m_status == CreateMask) { m_activeItem = addRect(QRectF(mouseEvent->scenePos(), QSize(1, 1)), QPen(m_brush.color()), m_brush); m_startPoint = mouseEvent->scenePos(); } else if (m_status == CreateRect) { m_activeItem = addRect(QRectF(mouseEvent->scenePos(), QSize(1, 1)), m_pen, QBrush()); m_startPoint = mouseEvent->scenePos(); } else if (m_status == CreateEllipse) { m_activeItem = addEllipse(QRectF(mouseEvent->scenePos(), QSize(1, 1)), m_pen, QBrush()); m_startPoint = mouseEvent->scenePos(); } else if (m_status == CreateLine) { m_activeItem = addLine(QLineF(mouseEvent->scenePos(), mouseEvent->scenePos()), m_pen); m_startPoint = mouseEvent->scenePos(); } else if (m_status == CreateArrow) { m_startPoint = mouseEvent->scenePos(); m_activeItem = addPath(createArrowPath(m_startPoint, mouseEvent->scenePos()), m_pen, QBrush(m_pen.color())); } else if (m_status == CreatePen) { m_startPoint = mouseEvent->scenePos(); QPainterPath path(m_startPoint); path.lineTo(m_startPoint + QPointF(1, 1)); m_activeItem = addPath(path, m_pen, QBrush()); } } void GraphicsScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent) { if (mouseEvent->button() == Qt::MouseButton::RightButton) { QGraphicsScene::mouseReleaseEvent(mouseEvent); return; } if (m_status == CreateText) { if (m_activeItem != nullptr) { assert(typeid(*m_activeItem) == typeid(QGraphicsProxyWidget)); QRectF r(m_edit->mapToParent(QPoint()), m_edit->size()); if (r.contains(mouseEvent->scenePos())) { QGraphicsScene::mouseReleaseEvent(mouseEvent); return; } QString text = m_edit->document()->toPlainText(); QFont font = m_edit->font(); removeItem(m_activeItem); m_activeItem = nullptr; QGraphicsTextItem* item = addText(text, font); item->setPos(m_edit->mapToParent(QPoint())); item->setDefaultTextColor(m_pen.color()); for (int i = (int)m_ItemList.size() - 1; i > m_curItemIndex; --i) { delete m_ItemList[i]; m_ItemList[i] = nullptr; m_ItemList.erase(m_ItemList.begin() + i); } m_ItemList.push_back(item); ++m_curItemIndex; delete m_edit; m_edit = nullptr; emit itemChanged(); } else { m_edit = new GraphicsTextEdit(); m_edit->setDefeaultColor(m_pen.color()); m_edit->setFont(m_font); QGraphicsProxyWidget* widget = addWidget(m_edit, Qt::FramelessWindowHint); widget->setPos(mouseEvent->scenePos()); m_activeItem = widget; m_edit->setFocus(); } } else { if (m_activeItem != nullptr) { assert(typeid(*m_activeItem) != typeid(QGraphicsProxyWidget)); for (int i = (int)m_ItemList.size() - 1; i > m_curItemIndex; --i) { delete m_ItemList[i]; m_ItemList[i] = nullptr; m_ItemList.erase(m_ItemList.begin() + i); } m_ItemList.push_back(m_activeItem); ++m_curItemIndex; m_activeItem = nullptr; emit itemChanged(); } } }