1. 界面排布
m_pVLayout->addWidget(m_pMenuBar);
m_pHLayout->addWidget(m_pToolBar);
m_pHLayout->addWidget(m_pSvgMainWin);
m_pHLayout->addWidget(m_pCanvasParamsBar);
m_pVLayout->addLayout(m_pHLayout);
m_pVLayout->addWidget(m_pColorBar);
ui.centralWidget->setLayout(m_pVLayout);
m_pToolBar 工具栏类垂直布局了KToolButton类;
m_pCanvasParamsBar 属性框类列表布局了KColorBox(颜色选择框)、 KValueBox(属性设置框)、KCanvasScaleBox(画布比例框)。
其中 KValueBox采用了正则表达式,对用户输入数据进行判断。
//正则表达式
QRegExp reg("[1-9][0-9]+$");
QValidator* validator = new QRegExpValidator(reg, m_pParamEdit);
m_pParamEdit->setValidator(validator);
m_pVLayout->addWidget(m_pParamEdit);
}
2. 基本图形绘制
pen画笔类
判断绘画类型,如果是画笔类,调用addPos,记录每一个路径点。
else if (flag == KGlobalData::KDrawFlag::PenFlag)
{
m_isSelected = false;
m_pCurrentShape = KShapeFactory::createShape(KGlobalData::getGlobalDataIntance()->getDrawFlag());
if (m_pCurrentShape != NULL)
{
qreal scale = KGlobalData::getGlobalDataIntance()->getCanvasScale();
static_cast<KPen*>(m_pCurrentShape)->addPos(event->pos() / scale);
}
}
void KPen::addPos(QPoint pos)
{
if (m_posVector.isEmpty())
{
m_startPoint = pos;
m_endPoint = pos;
}
else
{
if (pos.x() < m_startPoint.x())
m_startPoint.rx() = pos.x();
if (pos.y() < m_startPoint.y())
m_startPoint.ry() = pos.y();
if (pos.x() > m_endPoint.x())
m_endPoint.rx() = pos.x();
if (pos.y() > m_endPoint.y())
m_endPoint.ry() = pos.y();
}
m_posVector.append(pos);
}
绘制画笔时,使用drawPath( )画出每一个点
void KPen::drawShape(QPaintDevice* parent)
{
QPainter painter(parent);
QPainterPath path;
QVector<QPoint>::iterator pos = m_posVector.begin();
path.moveTo(*pos);
while (pos != m_posVector.end())
{
path.lineTo(*pos);
pos++;
}
qreal scale = KGlobalData::getGlobalDataIntance()->getCanvasScale();
painter.scale(scale, scale);
painter.setRenderHint(QPainter::Antialiasing);
QPen pen;
pen.setColor(QColor("#" + m_borderColor));
pen.setWidthF(m_borderWidth);
painter.setPen(pen);
painter.drawPath(path);
}
line直线类
记录起始点,并绘制线条
void KLine::drawShape(QPaintDevice* parent)
{
QPainter painter(parent);
qreal scale = KGlobalData::getGlobalDataIntance()->getCanvasScale();
painter.scale(scale, scale);
painter.setRenderHint(QPainter::Antialiasing);
QPen pen;
pen.setColor(QColor("#" + m_borderColor));
pen.setWidth(m_borderWidth);
painter.setPen(pen);
painter.drawLine(getStartPoint(), getEndPoint());
}
circle圆类
记录鼠标起始点,绘制椭圆(正圆)。
void KCircle::drawShape(QPaintDevice* parent)
{
QPainter painter(parent);
qreal scale = KGlobalData::getGlobalDataIntance()->getCanvasScale();
painter.scale(scale, scale);
painter.setRenderHint(QPainter::Antialiasing);
QPen pen;
pen.setColor(QColor("#" + m_borderColor));
pen.setWidth(m_borderWidth);
painter.setPen(pen);
painter.setBrush(QColor("#" + m_fillColor));
painter.drawEllipse(QRect(getStartPoint(), getEndPoint()));
}
rect矩形
记录鼠标起始点,绘制图形。
void KRect::drawShape(QPaintDevice* parent)
{
QPainter painter(parent);
qreal scale = KGlobalData::getGlobalDataIntance()->getCanvasScale();
painter.scale(scale, scale);
painter.setRenderHint(QPainter::Antialiasing);
QPen pen;
pen.setColor(QColor("#" + m_borderColor));
pen.setWidth(m_borderWidth);
painter.setPen(pen);
painter.setBrush(QColor("#" + m_fillColor));
painter.drawRect(QRect(getStartPoint(), getEndPoint()));
}
pent五边形类
记录起始点,配合我自己计算出的比例,算出五个顶点相对坐标,数组记录并绘图。
void KPent::drawShape(QPaintDevice* parent)
{
QPainter painter(parent);
qreal scale = KGlobalData::getGlobalDataIntance()->getCanvasScale();
painter.scale(scale, scale);
painter.setRenderHint(QPainter::Antialiasing);
// 绘制五边形
int l = getEndPoint().x() - getStartPoint().x();
int h = getEndPoint().y() - getStartPoint().y();
int x1 = getStartPoint().x();
int y1 = getStartPoint().y();
int x2 = getEndPoint().x();
int y2 = getEndPoint().y();
QPoint p1(x1 + (l / 2), y1);
QPoint p2(x1 + (0.206 * l), y2 );
QPoint p3(x1 + (0.794 * l), y2 );
QPoint p4(x1 + 0.05 * l, y1 + (h * 0.383));
QPoint p5(x2 - 0.05 * l, y1 + (h * 0.383));
m_polygon[0] = p1;
m_polygon[1] = p4;
m_polygon[2] = p2;
m_polygon[3] = p3;
m_polygon[4] = p5;
QPen pen;
pen.setColor(QColor("#" + m_borderColor));
pen.setWidth(m_borderWidth);
painter.setPen(pen);
painter.setBrush(QColor("#" + m_fillColor));
painter.drawPolygon(m_polygon,5);
}
hexa六边形类
记录起始点,配合我自己计算出的比例,算出六个顶点相对坐标,数组记录并绘图。
void KHexa::drawShape(QPaintDevice* parent)
{
QPainter painter(parent);
qreal scale = KGlobalData::getGlobalDataIntance()->getCanvasScale();
painter.scale(scale, scale);
painter.setRenderHint(QPainter::Antialiasing);
// 绘制六边形
int l = getEndPoint().x() - getStartPoint().x();
int h = getEndPoint().y() - getStartPoint().y();
int x1 = getStartPoint().x();
int y1 = getStartPoint().y();
int x2 = getEndPoint().x();
int y2 = getEndPoint().y();
//top
QPoint p1(x1 + (l / 2), y1);
//bottom
QPoint p2(x2 - (l / 2), y2);
//others
QPoint p3(x2 - 0.067 * l, y1 + (h * 0.25));
QPoint p4(x2 - 0.067 * l, y1 + (h * 0.75));
QPoint p5(x1 + 0.067 * l, y1 + (h * 0.75));
QPoint p6(x1 + 0.067 * l, y1 + (h * 0.25));
m_polygon[0] = p1;
m_polygon[1] = p3;
m_polygon[2] = p4;
m_polygon[3] = p2;
m_polygon[4] = p5;
m_polygon[5] = p6;
QPen pen;
pen.setColor(QColor("#" + m_borderColor));
pen.setWidth(m_borderWidth);
painter.setPen(pen);
painter.setBrush(QColor("#" + m_fillColor));
painter.drawPolygon(m_polygon, 6);
}
star 五角星类
记录起始点,配合我自己计算出的比例,算出10个顶点相对坐标,数组记录并绘图。
void KStar::drawShape(QPaintDevice* parent)
{
QPainter painter(parent);
qreal scale = KGlobalData::getGlobalDataIntance()->getCanvasScale();
painter.scale(scale, scale);
painter.setRenderHint(QPainter::Antialiasing);
int l = getEndPoint().x() - getStartPoint().x();
int h = getEndPoint().y() - getStartPoint().y();
int x1 = getStartPoint().x();
int y1 = getStartPoint().y();
int x2 = getEndPoint().x();
int y2 = getEndPoint().y();
//top
QPoint p1(x1 + (l / 2), y1);
QPoint p2(x1 + (l * 0.618), y1 + (h * 0.338));
QPoint p3(x2 - (l * 0.05), y1 + (h * 0.338));
QPoint p4(x2 - 0.31 * l, y2 - (h * 0.438));
QPoint p5(x2 - 0.206 * l, y2 - (h * 0.1));
QPoint p6(x1 + (l / 2), y2 - (h * 0.3));
QPoint p7(x1 + 0.206 * l, y2 - (h * 0.1));
QPoint p8(x1 + 0.31 * l, y2 - (h * 0.438));
QPoint p9(x1 + (l * 0.05), y1 + (h * 0.338));
QPoint p10(x2 - (l * 0.618), y1 + (h * 0.338));
m_polygon[0] = p1;
m_polygon[1] = p2;
m_polygon[2] = p3;
m_polygon[3] = p4;
m_polygon[4] = p5;
m_polygon[5] = p6;
m_polygon[6] = p7;
m_polygon[7] = p8;
m_polygon[8] = p9;
m_polygon[9] = p10;
QPen pen;
pen.setColor(QColor("#" + m_borderColor));
pen.setWidth(m_borderWidth);
painter.setPen(pen);
painter.setBrush(QColor("#" + m_fillColor));
painter.drawPolygon(m_polygon, 10);
}
text文字类
在主画布调用该函数,设置lineeditor父窗口为主画布
else if (flag == KGlobalData::KDrawFlag::TextFlag)
{
m_isSelected = false;
m_pCurrentShape = KShapeFactory::createShape(
KGlobalData::getGlobalDataIntance()->getDrawFlag());
KText* temp = (KText*)m_pCurrentShape;
temp->setParent(this);
temp = NULL;
delete temp;
if (m_pCurrentShape != NULL)
{
qreal scale = KGlobalData::getGlobalDataIntance()->getCanvasScale();
m_pCurrentShape->setStartPoint(event->pos() / scale);
}
}
void KText::setParent(QWidget *parent)
{
m_textLine->setParent(parent);
}
对m_text判断,关闭编辑框,否则会形成二重叠加态。
KText::KText()
{
m_textLine = new QLineEdit;
m_textLine->setFrame(false);
m_textLine->setPlaceholderText("Please enter text");
m_borderColor = "000000";
}
KText::~KText()
{
}
void KText::drawShape(QPaintDevice* parent)
{
QPainter painter(parent);
qreal scale = KGlobalData::getGlobalDataIntance()->getCanvasScale();
painter.scale(scale, scale);
painter.setRenderHint(QPainter::Antialiasing);
QPen pen;
pen.setColor(QColor("#" + m_borderColor));
painter.setPen(pen);
QRectF r1(getStartPoint(), getEndPoint());
setEndPoint(QPoint(getStartPoint().x() + 100, getStartPoint().y() + 50));
if (m_text == "")
{
m_textLine->move(getStartPoint() * scale);
m_text = m_textLine->text();
painter.drawText(r1, m_text);
m_textLine->show();
}
if (m_text != "")
{
m_textLine->close();
painter.drawText(r1, m_text);
}
}
3. 信号与槽
详见注释。
void KSvgEditor::initConnection()
{
(void)connect(m_pMenuBar->m_pSaveFileAction, SIGNAL(triggered()), m_pSvgMainWin->m_pCanvas, SLOT(saveSvg())); //保存
(void)connect(m_pCanvasParamsBar->m_pWidthBox->m_pParamEdit, SIGNAL(editingFinished()), this, SLOT(validateCanvasParams())); //设置画布宽度
(void)connect(m_pCanvasParamsBar->m_pHeightBox->m_pParamEdit, SIGNAL(editingFinished()), this, SLOT(validateCanvasParams())); //设置画布高度
(void)connect(m_pCanvasParamsBar->m_pColorBox, SIGNAL(pickedColor()), this, SLOT(validateCanvasParams())); //设置画布颜色
(void)connect(m_pCanvasParamsBar->m_pfillColorBox, SIGNAL(pickedColor()), this, SLOT(fillColor())); //设置填充颜色
(void)connect(m_pCanvasParamsBar->m_pBorderColorBox, SIGNAL(pickedColor()), this, SLOT(borderColor())); //设置边框颜色(pen,line颜色)
(void)connect(m_pCanvasParamsBar->m_pBorderWidthBox->m_pParamEdit, SIGNAL(editingFinished()), this, SLOT(borderWidth())); //设置边框宽度(pen,line线宽)
(void)connect(m_pCanvasParamsBar->m_pScaleBox->m_pParamEdit, SIGNAL(editingFinished()), this, SLOT(validateCanvasParams())); //设置缩放比例修改
(void)connect(m_pSvgMainWin, SIGNAL(changeScale(qreal)), this, SLOT(setScale(qreal))); //设置缩放比例显示
(void)connect(m_pToolBar->m_pZoomBtn, SIGNAL(showParam()), this, SLOT(closeParamsbar())); //属性栏选择性显示
(void)connect(m_pSvgMainWin->m_pCanvas, SIGNAL(mouseChange()), this, SLOT(showItemParambar()));//属性栏选择性显示
(void)connect(m_pSvgMainWin->m_pCanvas, SIGNAL(penChange()), this, SLOT(showLineParamsbar()));//属性栏选择性显示
(void)connect(m_pSvgMainWin->m_pCanvas, SIGNAL(textChange()), this, SLOT(showTextParamsbar()));//属性栏选择性显示
(void)connect(m_pSvgMainWin->m_pCanvas, SIGNAL(paramChange()), this, SLOT(showParamsbar()));//属性栏选择性显示
}
4. 重写滚轮事件
通过重写滚轮事件,设置图形和画布的缩放。
void KSvgMainWindow::wheelEvent(QWheelEvent* event)
{
KGlobalData::KDrawFlag flag = KGlobalData::getGlobalDataIntance()->getDrawFlag();
qreal scale = KGlobalData::getGlobalDataIntance()->getCanvasScale();
if (flag != KGlobalData::KDrawFlag::ZoomFlag)
{
return;
}
if (event->delta() > 0) //若delta大于0,则表示滚轮向前(远离用户的方向),小于零则表明向后
{
scale += 0.05;
int width = KGlobalData::getGlobalDataIntance()->getCanvasWidth();
int height = KGlobalData::getGlobalDataIntance()->getCanvasHeight();
m_pCanvas->resize(width * scale, height * scale);
KGlobalData::getGlobalDataIntance()->setCanvasScale(scale);
}
else
{
scale -= 0.05;
if (scale < 0.05)
return;
int width = KGlobalData::getGlobalDataIntance()->getCanvasWidth();
int height = KGlobalData::getGlobalDataIntance()->getCanvasHeight();
m_pCanvas->resize(width * scale, height * scale);
KGlobalData::getGlobalDataIntance()->setCanvasScale(scale);
}
QScrollArea::wheelEvent(event);
emit changeScale(scale);
}
5. 删除图形
void KSvgMainWindow::keyPressEvent(QKeyEvent* event)
{
if (event->key() == Qt::Key_Delete || event->key() == Qt::Key_Backspace )
{
for (QList<KShape*>::iterator it = m_pCanvas->m_pShapeList->begin(); it != m_pCanvas->m_pShapeList->end(); it++)
{
if ((*it)->getShapeRect().contains(m_pCanvas->m_lastPos))
{
m_pCanvas->m_pShapeList->erase(it);
break;
}
}
}
update();
}
6. 单例模式析构,及QShapeList析构
单例模式析构,采取了嵌套类方式,自动释放堆区内存。
class KGlobalData
{
public:
class KAutoDestory
{
public:
KAutoDestory() {}
~KAutoDestory()
{
if (s_globalDataObj != NULL)
{
delete s_globalDataObj;
s_globalDataObj = NULL;
}
}
};
static KGlobalData* getGlobalDataIntance();
private:
static KGlobalData* s_globalDataObj;
static KAutoDestory m_autoDestory;
};
#include "kglobaldata.h"
KGlobalData* KGlobalData::s_globalDataObj = NULL;
KGlobalData::KAutoDestory KGlobalData::m_autoDestory;
QShapeList析构
不只要释放QShapeList本身,还要释放其内部存储的数据(new 的各种图形)。
KSvgCanvas::~KSvgCanvas()
{
if (m_pShapeList != NULL)
{
for (QList<KShape*>::iterator it = m_pShapeList->begin(); it != m_pShapeList->end(); it++)
{
delete(*it);
}
}
delete m_pShapeList;
m_pShapeList = NULL;
}