一、功能描述
需要完成带吸附效果的线段就需要完成一下功能点。
- 线段绘制。
- 当鼠标接近绘制好的线段时线段变粗。
- 当线段在线段两段时,按下鼠标选择定点移动。
- 当选择线段中间时,线段平移。
二、代码分析
实现以上功能,需要完成关键的三个鼠标响应事件。
// 鼠标按下事件
void mousePressEvent(QMouseEvent *event);
// 鼠标释放事件
void mouseReleaseEvent(QMouseEvent *event);
// 鼠标要移动事件
void mouseMoveEvent(QMouseEvent *event);
关键的绘图事件
// 界面的绘制在这里
void paintEvent(QPaintEvent *event);
关键数据结构
enum SelStatus {
outLine, // 在线段外
onStartPoint, // 在起始点
onEndPoint, // 在结束点
onLine // 在线段上
};
struct LineSegment {
QPointF startPoint; // 线段起点
QPointF endPoint; // 点段终点
LineSegment(QPointF a, QPointF b)
{
startPoint = a;
endPoint = b;
}
LineSegment() {}
};
struct LineInfo {
bool bDraw; //是否绘制
SelStatus selStatus; // 线段的选中状态
LineSegment* seg;
LineInfo()
{
selStatus = outLine;
seg = new LineSegment;
}
~LineInfo() { delete seg; }
};
鼠标按下
- 记录按下的点以及线段绘制的起始点。
- 如果线段被选中,记录选中的线段的起始点和结束点
void QDrawingPaperView::mousePressEvent(QMouseEvent *event)
{
switch (event->button()) {
case Qt::LeftButton:
m_bLBtnDown = true; // 记录左键鼠标按下
m_currectSelectedLine = nullptr;
m_currectSelectedLine = getSeleledLine();
if (nullptr == m_currectSelectedLine) { // 未选中
m_startPoint = event->pos();
m_endPoint = m_startPoint;
// 记录绘画起始点
m_tempLine = new LineInfo;
m_tempLine->seg->startPoint.setX(event->x());
m_tempLine->seg->startPoint.setY(event->y());
} else {
m_tmpPoint = event->pos();
// 记录被选中的开始点和结束点,当鼠标移动时需要通过他们来计算移动的偏移量。
m_startPoint = m_currectSelectedLine->seg->startPoint;
m_endPoint = m_currectSelectedLine->seg->endPoint;
}
break;
default:
break;
}
}
鼠标释放
- 记录按下的点以及线段绘制的结束点。
- 如果线段被选中不做处理。
void QDrawingPaperView::mouseReleaseEvent(QMouseEvent *event)
{
switch (event->button()) {
case Qt::LeftButton:
m_bLBtnDown = false; // 记录左键鼠标按下
if (nullptr == m_currectSelectedLine) {
// 记录绘画结束点
m_tempLine->seg->endPoint.setX(event->pos().x());
m_tempLine->seg->endPoint.setY(event->pos().y());
m_tempLine->bDraw = true;
// 将线段信息保存在列表中,开始位置不能与起始位置重复.
if (m_tempLine->seg->startPoint != m_tempLine->seg->endPoint)
m_listLineInfos.push_back(m_tempLine);
} else {
m_currectSelectedLine = nullptr;
}
break;
default:
break;
}
update();
}
鼠标移动
关键的算法都在这里。
- 需要判断是否选中线段
- 需要判断选择线段的位置
- 如果选中需要判断是否已经按下。
- 如果按下更具选中的状态修改线段坐标点
void QDrawingPaperView::mouseMoveEvent(QMouseEvent *event)
{
QPointF movePt = event->pos();
if (!m_bLBtnDown) {
selSeg(movePt);
}
m_currectSelectedLine = getSeleledLine();
if (nullptr == m_currectSelectedLine) {
if (m_bLBtnDown) {
m_endPoint = movePt;
}
} else {
if (m_bLBtnDown) {
if (m_currectSelectedLine->selStatus == onLine) {
qInfo() << (movePt);
qInfo() << "m_tmpPoint" << m_tmpPoint;
qInfo() << "偏移:" << movePt - m_tmpPoint;
qInfo() << m_currectSelectedLine->seg->startPoint + movePt -
m_tmpPoint;
m_currectSelectedLine->seg->startPoint =
m_startPoint + movePt - m_tmpPoint;
m_currectSelectedLine->seg->endPoint =
m_endPoint + movePt - m_tmpPoint;
qInfo() << "endPoint" << (m_currectSelectedLine->seg->endPoint);
} else if (m_currectSelectedLine->selStatus == onStartPoint) {
m_currectSelectedLine->seg->startPoint =
m_startPoint + movePt - m_tmpPoint;
} else if (m_currectSelectedLine->selStatus == onEndPoint) {
m_currectSelectedLine->seg->endPoint =
m_endPoint + movePt - m_tmpPoint;
}
}
}
update();
}
线段绘制
线段绘制的入门基础可以查看文章QT线段画板实战_啊渊的专栏-CSDN博客
- 根据线段的顶点绘制
- 根据线段的选中状态绘制
void QDrawingPaperView::paintEvent(QPaintEvent *event)
{
QPainter p(this);
// 设置画笔的样式
// 绘制临时图像
if (m_bLBtnDown == true && nullptr == m_currectSelectedLine) {
p.setPen(QPen(Qt::red, 1));
p.drawLine(m_startPoint, m_endPoint);
}
// 绘制最终的数据列表
foreach (auto item, m_listLineInfos) {
if (true == item->bDraw) {
if (item->selStatus != outLine) {
p.setPen(QPen(Qt::red, 2));
if (item->selStatus == onStartPoint) {
p.setPen(QPen(Qt::red, 2));
p.drawEllipse(item->seg->startPoint, 3, 3);
} else if (item->selStatus == onEndPoint) {
p.setPen(QPen(Qt::red, 2));
p.drawEllipse(item->seg->endPoint, 3, 3);
}
} else {
p.setPen(QPen(Qt::red, 1));
}
p.drawLine(item->seg->startPoint, item->seg->endPoint);
}
}
}
关键算法计算垂点
详细分析可以查看文章求点到线段的最短距离(QT)_啊渊的专栏-CSDN博客
QPointF QDrawingPaperView::getPointToLineVerticalpoint(QPointF pt,
LineSegment seg)
{
QPointF np;
double x_se = seg.startPoint.x() - seg.endPoint.x();
double y_se = seg.startPoint.y() - seg.endPoint.y();
double x_se_2 = x_se * x_se;
double y_se_2 = y_se * y_se;
double x = (x_se_2 * pt.x() + (pt.y() - seg.startPoint.y()) * y_se * x_se +
seg.startPoint.x() * y_se_2) /
(x_se_2 + y_se_2);
double y = pt.y() + x_se * (pt.x() - x) / y_se;
np.setX(x);
np.setY(y);
return np;
}
源码
mkdir build
qmake ..
make
CustomLine3 · master · 啊渊 / QT博客案例 · GIT CODE