1. 界面排布

KSvgEditor开发文档 - 图1

  1. m_pVLayout->addWidget(m_pMenuBar);
  2. m_pHLayout->addWidget(m_pToolBar);
  3. m_pHLayout->addWidget(m_pSvgMainWin);
  4. m_pHLayout->addWidget(m_pCanvasParamsBar);
  5. m_pVLayout->addLayout(m_pHLayout);
  6. m_pVLayout->addWidget(m_pColorBar);
  7. ui.centralWidget->setLayout(m_pVLayout);

m_pToolBar 工具栏类垂直布局了KToolButton类;

m_pCanvasParamsBar 属性框类列表布局了KColorBox(颜色选择框)、 KValueBox(属性设置框)、KCanvasScaleBox(画布比例框)。

其中 KValueBox采用了正则表达式,对用户输入数据进行判断。

  1. //正则表达式
  2. QRegExp reg("[1-9][0-9]+$");
  3. QValidator* validator = new QRegExpValidator(reg, m_pParamEdit);
  4. m_pParamEdit->setValidator(validator);
  5. m_pVLayout->addWidget(m_pParamEdit);
  6. }

2. 基本图形绘制

pen画笔类

判断绘画类型,如果是画笔类,调用addPos,记录每一个路径点。

  1. else if (flag == KGlobalData::KDrawFlag::PenFlag)
  2. {
  3. m_isSelected = false;
  4. m_pCurrentShape = KShapeFactory::createShape(KGlobalData::getGlobalDataIntance()->getDrawFlag());
  5. if (m_pCurrentShape != NULL)
  6. {
  7. qreal scale = KGlobalData::getGlobalDataIntance()->getCanvasScale();
  8. static_cast<KPen*>(m_pCurrentShape)->addPos(event->pos() / scale);
  9. }
  10. }
  1. void KPen::addPos(QPoint pos)
  2. {
  3. if (m_posVector.isEmpty())
  4. {
  5. m_startPoint = pos;
  6. m_endPoint = pos;
  7. }
  8. else
  9. {
  10. if (pos.x() < m_startPoint.x())
  11. m_startPoint.rx() = pos.x();
  12. if (pos.y() < m_startPoint.y())
  13. m_startPoint.ry() = pos.y();
  14. if (pos.x() > m_endPoint.x())
  15. m_endPoint.rx() = pos.x();
  16. if (pos.y() > m_endPoint.y())
  17. m_endPoint.ry() = pos.y();
  18. }
  19. m_posVector.append(pos);
  20. }

绘制画笔时,使用drawPath( )画出每一个点

  1. void KPen::drawShape(QPaintDevice* parent)
  2. {
  3. QPainter painter(parent);
  4. QPainterPath path;
  5. QVector<QPoint>::iterator pos = m_posVector.begin();
  6. path.moveTo(*pos);
  7. while (pos != m_posVector.end())
  8. {
  9. path.lineTo(*pos);
  10. pos++;
  11. }
  12. qreal scale = KGlobalData::getGlobalDataIntance()->getCanvasScale();
  13. painter.scale(scale, scale);
  14. painter.setRenderHint(QPainter::Antialiasing);
  15. QPen pen;
  16. pen.setColor(QColor("#" + m_borderColor));
  17. pen.setWidthF(m_borderWidth);
  18. painter.setPen(pen);
  19. painter.drawPath(path);
  20. }

line直线类

记录起始点,并绘制线条

  1. void KLine::drawShape(QPaintDevice* parent)
  2. {
  3. QPainter painter(parent);
  4. qreal scale = KGlobalData::getGlobalDataIntance()->getCanvasScale();
  5. painter.scale(scale, scale);
  6. painter.setRenderHint(QPainter::Antialiasing);
  7. QPen pen;
  8. pen.setColor(QColor("#" + m_borderColor));
  9. pen.setWidth(m_borderWidth);
  10. painter.setPen(pen);
  11. painter.drawLine(getStartPoint(), getEndPoint());
  12. }

circle圆类

记录鼠标起始点,绘制椭圆(正圆)。

  1. void KCircle::drawShape(QPaintDevice* parent)
  2. {
  3. QPainter painter(parent);
  4. qreal scale = KGlobalData::getGlobalDataIntance()->getCanvasScale();
  5. painter.scale(scale, scale);
  6. painter.setRenderHint(QPainter::Antialiasing);
  7. QPen pen;
  8. pen.setColor(QColor("#" + m_borderColor));
  9. pen.setWidth(m_borderWidth);
  10. painter.setPen(pen);
  11. painter.setBrush(QColor("#" + m_fillColor));
  12. painter.drawEllipse(QRect(getStartPoint(), getEndPoint()));
  13. }

rect矩形

记录鼠标起始点,绘制图形。

  1. void KRect::drawShape(QPaintDevice* parent)
  2. {
  3. QPainter painter(parent);
  4. qreal scale = KGlobalData::getGlobalDataIntance()->getCanvasScale();
  5. painter.scale(scale, scale);
  6. painter.setRenderHint(QPainter::Antialiasing);
  7. QPen pen;
  8. pen.setColor(QColor("#" + m_borderColor));
  9. pen.setWidth(m_borderWidth);
  10. painter.setPen(pen);
  11. painter.setBrush(QColor("#" + m_fillColor));
  12. painter.drawRect(QRect(getStartPoint(), getEndPoint()));
  13. }

pent五边形类

记录起始点,配合我自己计算出的比例,算出五个顶点相对坐标,数组记录并绘图。

KSvgEditor开发文档 - 图2

  1. void KPent::drawShape(QPaintDevice* parent)
  2. {
  3. QPainter painter(parent);
  4. qreal scale = KGlobalData::getGlobalDataIntance()->getCanvasScale();
  5. painter.scale(scale, scale);
  6. painter.setRenderHint(QPainter::Antialiasing);
  7. // 绘制五边形
  8. int l = getEndPoint().x() - getStartPoint().x();
  9. int h = getEndPoint().y() - getStartPoint().y();
  10. int x1 = getStartPoint().x();
  11. int y1 = getStartPoint().y();
  12. int x2 = getEndPoint().x();
  13. int y2 = getEndPoint().y();
  14. QPoint p1(x1 + (l / 2), y1);
  15. QPoint p2(x1 + (0.206 * l), y2 );
  16. QPoint p3(x1 + (0.794 * l), y2 );
  17. QPoint p4(x1 + 0.05 * l, y1 + (h * 0.383));
  18. QPoint p5(x2 - 0.05 * l, y1 + (h * 0.383));
  19. m_polygon[0] = p1;
  20. m_polygon[1] = p4;
  21. m_polygon[2] = p2;
  22. m_polygon[3] = p3;
  23. m_polygon[4] = p5;
  24. QPen pen;
  25. pen.setColor(QColor("#" + m_borderColor));
  26. pen.setWidth(m_borderWidth);
  27. painter.setPen(pen);
  28. painter.setBrush(QColor("#" + m_fillColor));
  29. painter.drawPolygon(m_polygon,5);
  30. }

hexa六边形类

记录起始点,配合我自己计算出的比例,算出六个顶点相对坐标,数组记录并绘图。

  1. void KHexa::drawShape(QPaintDevice* parent)
  2. {
  3. QPainter painter(parent);
  4. qreal scale = KGlobalData::getGlobalDataIntance()->getCanvasScale();
  5. painter.scale(scale, scale);
  6. painter.setRenderHint(QPainter::Antialiasing);
  7. // 绘制六边形
  8. int l = getEndPoint().x() - getStartPoint().x();
  9. int h = getEndPoint().y() - getStartPoint().y();
  10. int x1 = getStartPoint().x();
  11. int y1 = getStartPoint().y();
  12. int x2 = getEndPoint().x();
  13. int y2 = getEndPoint().y();
  14. //top
  15. QPoint p1(x1 + (l / 2), y1);
  16. //bottom
  17. QPoint p2(x2 - (l / 2), y2);
  18. //others
  19. QPoint p3(x2 - 0.067 * l, y1 + (h * 0.25));
  20. QPoint p4(x2 - 0.067 * l, y1 + (h * 0.75));
  21. QPoint p5(x1 + 0.067 * l, y1 + (h * 0.75));
  22. QPoint p6(x1 + 0.067 * l, y1 + (h * 0.25));
  23. m_polygon[0] = p1;
  24. m_polygon[1] = p3;
  25. m_polygon[2] = p4;
  26. m_polygon[3] = p2;
  27. m_polygon[4] = p5;
  28. m_polygon[5] = p6;
  29. QPen pen;
  30. pen.setColor(QColor("#" + m_borderColor));
  31. pen.setWidth(m_borderWidth);
  32. painter.setPen(pen);
  33. painter.setBrush(QColor("#" + m_fillColor));
  34. painter.drawPolygon(m_polygon, 6);
  35. }

star 五角星类

记录起始点,配合我自己计算出的比例,算出10个顶点相对坐标,数组记录并绘图。

  1. void KStar::drawShape(QPaintDevice* parent)
  2. {
  3. QPainter painter(parent);
  4. qreal scale = KGlobalData::getGlobalDataIntance()->getCanvasScale();
  5. painter.scale(scale, scale);
  6. painter.setRenderHint(QPainter::Antialiasing);
  7. int l = getEndPoint().x() - getStartPoint().x();
  8. int h = getEndPoint().y() - getStartPoint().y();
  9. int x1 = getStartPoint().x();
  10. int y1 = getStartPoint().y();
  11. int x2 = getEndPoint().x();
  12. int y2 = getEndPoint().y();
  13. //top
  14. QPoint p1(x1 + (l / 2), y1);
  15. QPoint p2(x1 + (l * 0.618), y1 + (h * 0.338));
  16. QPoint p3(x2 - (l * 0.05), y1 + (h * 0.338));
  17. QPoint p4(x2 - 0.31 * l, y2 - (h * 0.438));
  18. QPoint p5(x2 - 0.206 * l, y2 - (h * 0.1));
  19. QPoint p6(x1 + (l / 2), y2 - (h * 0.3));
  20. QPoint p7(x1 + 0.206 * l, y2 - (h * 0.1));
  21. QPoint p8(x1 + 0.31 * l, y2 - (h * 0.438));
  22. QPoint p9(x1 + (l * 0.05), y1 + (h * 0.338));
  23. QPoint p10(x2 - (l * 0.618), y1 + (h * 0.338));
  24. m_polygon[0] = p1;
  25. m_polygon[1] = p2;
  26. m_polygon[2] = p3;
  27. m_polygon[3] = p4;
  28. m_polygon[4] = p5;
  29. m_polygon[5] = p6;
  30. m_polygon[6] = p7;
  31. m_polygon[7] = p8;
  32. m_polygon[8] = p9;
  33. m_polygon[9] = p10;
  34. QPen pen;
  35. pen.setColor(QColor("#" + m_borderColor));
  36. pen.setWidth(m_borderWidth);
  37. painter.setPen(pen);
  38. painter.setBrush(QColor("#" + m_fillColor));
  39. painter.drawPolygon(m_polygon, 10);
  40. }

text文字类

在主画布调用该函数,设置lineeditor父窗口为主画布

  1. else if (flag == KGlobalData::KDrawFlag::TextFlag)
  2. {
  3. m_isSelected = false;
  4. m_pCurrentShape = KShapeFactory::createShape(
  5. KGlobalData::getGlobalDataIntance()->getDrawFlag());
  6. KText* temp = (KText*)m_pCurrentShape;
  7. temp->setParent(this);
  8. temp = NULL;
  9. delete temp;
  10. if (m_pCurrentShape != NULL)
  11. {
  12. qreal scale = KGlobalData::getGlobalDataIntance()->getCanvasScale();
  13. m_pCurrentShape->setStartPoint(event->pos() / scale);
  14. }
  15. }
  1. void KText::setParent(QWidget *parent)
  2. {
  3. m_textLine->setParent(parent);
  4. }

对m_text判断,关闭编辑框,否则会形成二重叠加态。

  1. KText::KText()
  2. {
  3. m_textLine = new QLineEdit;
  4. m_textLine->setFrame(false);
  5. m_textLine->setPlaceholderText("Please enter text");
  6. m_borderColor = "000000";
  7. }
  8. KText::~KText()
  9. {
  10. }
  11. void KText::drawShape(QPaintDevice* parent)
  12. {
  13. QPainter painter(parent);
  14. qreal scale = KGlobalData::getGlobalDataIntance()->getCanvasScale();
  15. painter.scale(scale, scale);
  16. painter.setRenderHint(QPainter::Antialiasing);
  17. QPen pen;
  18. pen.setColor(QColor("#" + m_borderColor));
  19. painter.setPen(pen);
  20. QRectF r1(getStartPoint(), getEndPoint());
  21. setEndPoint(QPoint(getStartPoint().x() + 100, getStartPoint().y() + 50));
  22. if (m_text == "")
  23. {
  24. m_textLine->move(getStartPoint() * scale);
  25. m_text = m_textLine->text();
  26. painter.drawText(r1, m_text);
  27. m_textLine->show();
  28. }
  29. if (m_text != "")
  30. {
  31. m_textLine->close();
  32. painter.drawText(r1, m_text);
  33. }
  34. }

3. 信号与槽

详见注释。

  1. void KSvgEditor::initConnection()
  2. {
  3. (void)connect(m_pMenuBar->m_pSaveFileAction, SIGNAL(triggered()), m_pSvgMainWin->m_pCanvas, SLOT(saveSvg())); //保存
  4. (void)connect(m_pCanvasParamsBar->m_pWidthBox->m_pParamEdit, SIGNAL(editingFinished()), this, SLOT(validateCanvasParams())); //设置画布宽度
  5. (void)connect(m_pCanvasParamsBar->m_pHeightBox->m_pParamEdit, SIGNAL(editingFinished()), this, SLOT(validateCanvasParams())); //设置画布高度
  6. (void)connect(m_pCanvasParamsBar->m_pColorBox, SIGNAL(pickedColor()), this, SLOT(validateCanvasParams())); //设置画布颜色
  7. (void)connect(m_pCanvasParamsBar->m_pfillColorBox, SIGNAL(pickedColor()), this, SLOT(fillColor())); //设置填充颜色
  8. (void)connect(m_pCanvasParamsBar->m_pBorderColorBox, SIGNAL(pickedColor()), this, SLOT(borderColor())); //设置边框颜色(pen,line颜色)
  9. (void)connect(m_pCanvasParamsBar->m_pBorderWidthBox->m_pParamEdit, SIGNAL(editingFinished()), this, SLOT(borderWidth())); //设置边框宽度(pen,line线宽)
  10. (void)connect(m_pCanvasParamsBar->m_pScaleBox->m_pParamEdit, SIGNAL(editingFinished()), this, SLOT(validateCanvasParams())); //设置缩放比例修改
  11. (void)connect(m_pSvgMainWin, SIGNAL(changeScale(qreal)), this, SLOT(setScale(qreal))); //设置缩放比例显示
  12. (void)connect(m_pToolBar->m_pZoomBtn, SIGNAL(showParam()), this, SLOT(closeParamsbar())); //属性栏选择性显示
  13. (void)connect(m_pSvgMainWin->m_pCanvas, SIGNAL(mouseChange()), this, SLOT(showItemParambar()));//属性栏选择性显示
  14. (void)connect(m_pSvgMainWin->m_pCanvas, SIGNAL(penChange()), this, SLOT(showLineParamsbar()));//属性栏选择性显示
  15. (void)connect(m_pSvgMainWin->m_pCanvas, SIGNAL(textChange()), this, SLOT(showTextParamsbar()));//属性栏选择性显示
  16. (void)connect(m_pSvgMainWin->m_pCanvas, SIGNAL(paramChange()), this, SLOT(showParamsbar()));//属性栏选择性显示
  17. }

4. 重写滚轮事件

通过重写滚轮事件,设置图形和画布的缩放。

  1. void KSvgMainWindow::wheelEvent(QWheelEvent* event)
  2. {
  3. KGlobalData::KDrawFlag flag = KGlobalData::getGlobalDataIntance()->getDrawFlag();
  4. qreal scale = KGlobalData::getGlobalDataIntance()->getCanvasScale();
  5. if (flag != KGlobalData::KDrawFlag::ZoomFlag)
  6. {
  7. return;
  8. }
  9. if (event->delta() > 0) //若delta大于0,则表示滚轮向前(远离用户的方向),小于零则表明向后
  10. {
  11. scale += 0.05;
  12. int width = KGlobalData::getGlobalDataIntance()->getCanvasWidth();
  13. int height = KGlobalData::getGlobalDataIntance()->getCanvasHeight();
  14. m_pCanvas->resize(width * scale, height * scale);
  15. KGlobalData::getGlobalDataIntance()->setCanvasScale(scale);
  16. }
  17. else
  18. {
  19. scale -= 0.05;
  20. if (scale < 0.05)
  21. return;
  22. int width = KGlobalData::getGlobalDataIntance()->getCanvasWidth();
  23. int height = KGlobalData::getGlobalDataIntance()->getCanvasHeight();
  24. m_pCanvas->resize(width * scale, height * scale);
  25. KGlobalData::getGlobalDataIntance()->setCanvasScale(scale);
  26. }
  27. QScrollArea::wheelEvent(event);
  28. emit changeScale(scale);
  29. }

5. 删除图形

  1. void KSvgMainWindow::keyPressEvent(QKeyEvent* event)
  2. {
  3. if (event->key() == Qt::Key_Delete || event->key() == Qt::Key_Backspace )
  4. {
  5. for (QList<KShape*>::iterator it = m_pCanvas->m_pShapeList->begin(); it != m_pCanvas->m_pShapeList->end(); it++)
  6. {
  7. if ((*it)->getShapeRect().contains(m_pCanvas->m_lastPos))
  8. {
  9. m_pCanvas->m_pShapeList->erase(it);
  10. break;
  11. }
  12. }
  13. }
  14. update();
  15. }

6. 单例模式析构,及QShapeList析构

单例模式析构,采取了嵌套类方式,自动释放堆区内存。

  1. class KGlobalData
  2. {
  3. public:
  4. class KAutoDestory
  5. {
  6. public:
  7. KAutoDestory() {}
  8. ~KAutoDestory()
  9. {
  10. if (s_globalDataObj != NULL)
  11. {
  12. delete s_globalDataObj;
  13. s_globalDataObj = NULL;
  14. }
  15. }
  16. };
  17. static KGlobalData* getGlobalDataIntance();
  18. private:
  19. static KGlobalData* s_globalDataObj;
  20. static KAutoDestory m_autoDestory;
  21. };
  1. #include "kglobaldata.h"
  2. KGlobalData* KGlobalData::s_globalDataObj = NULL;
  3. KGlobalData::KAutoDestory KGlobalData::m_autoDestory;

QShapeList析构

不只要释放QShapeList本身,还要释放其内部存储的数据(new 的各种图形)。

  1. KSvgCanvas::~KSvgCanvas()
  2. {
  3. if (m_pShapeList != NULL)
  4. {
  5. for (QList<KShape*>::iterator it = m_pShapeList->begin(); it != m_pShapeList->end(); it++)
  6. {
  7. delete(*it);
  8. }
  9. }
  10. delete m_pShapeList;
  11. m_pShapeList = NULL;
  12. }