一、功能描述

需要完成带吸附效果的线段就需要完成一下功能点。

  1. 线段绘制。
  2. 当鼠标接近绘制好的线段时线段变粗。
  3. 当线段在线段两段时,按下鼠标选择定点移动。
  4. 当选择线段中间时,线段平移。

二、代码分析

实现以上功能,需要完成关键的三个鼠标响应事件。

// 鼠标按下事件
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; }

在mainwindow中定义变量m_listLineInfos,保存所有的已经绘制的线段数据。

QList<LineInfo> m_listLineInfos

LineInfo * m_currectSelectedLine 保存当前被选中的线段的指针。

  • 记录按下的点以及线段绘制的起始点。
  • 如果线段被选中,记录选中的线段的起始点和结束点
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) { // 如果线段选中状态不是outLine那么必然在线段上,要么在开始点,要么在结束点,要么在线段上 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;

git传送门

代码编译指令

mkdir build
qmake ..

CustomLine3 · master · 啊渊 / QT博客案例 · GIT CODE

一、功能描述需要完成带吸附效果的线段就需要完成一下功能点。线段绘制。 当鼠标接近绘制好的线段时线段变粗。 当线段在线段两段时,按下鼠标选择定点移动。 当选择线段中间时,线段平移。二、代码分析实现以上功能,需要完成关键的三个鼠标响应事件。// 鼠标按下事件void mousePressEvent(QMouseEvent *event);// 鼠标释放事件void mouseReleaseEvent(QMouseEvent *event);// 鼠标要移动事件void m..
某日,闲来无事,无聊的翻看着电脑中的APP图标,忽然萌生一个想法:用Axure原型制作一个连连看类型的游戏原型,于是说做就做,打开AxureRP7,开始搞。一天,两天。。。(由于只有业余时间搞)。。。五天,原型终于搞定。期间经过了不同实现方法的尝试、逻辑的梳理、原型的测试等等等。。。。开始界面:点击PLAY按钮即可进入游戏游戏界面:点击翻开图片,图片等待将近1s的时间后自动恢复,如果依次翻开的两个图片如果相同,这两个图片则自动消失,显示OVER字样结束界面:点击REPLAY可以重新开始游戏补充一些废话,做事情要有始有终,所以原型设计也是这样,要有头有尾。这个过程真的不是那么顺利,从开始只是有一
这次主要和大家分享一下常用的一个功能,截图工具的实现。我仿照常用的QQ截图工具用Qt5做了一个功能上的实能。功能目前实现了常用的一些,有几个功能还未实现,以后有机会实现吧(应该没机会了)。 已经实现的功能: 实现单屏幕上的矩形选择截图。 可拖动、缩放、重绘矩形选框。 支持线条、矩形框、椭圆、箭头、字体的绘制。 颜色、大小可供选择。 支持撤销、保存操作。 鼠标放大镜功能。 未实现的功能: 截图边框吸附功能 马赛克功能(做了一半) 还有未测试出的数不清的bug 学习DSP需要一些前置知识,如信号与系统、傅里叶变换、滤波等。如果对这些概念不熟悉,需要先进行相关学习。 首先,需要了解DSP的基本概念和原理,可以通过阅读相关书籍或材来学习。其中,推荐书籍有《数字信号处理》、《实时数字信号处理》等。 其次,需要熟练使用Matlab或C语言等编程语言,在实践中不断操练。可以通过搭建实验环境,如使用FPGA开发板或软件仿真平台等,来进行具体实践项目。 在学习过程中还要注重理论与实践结合,可以将学习知识应用于项目中,如音频信号处理、数字滤波器设计等,这样可以更深入地理解DSP的应用。 最后,可以通过查阅一些DSP相关文章和论文来拓宽知识面,关注业界最新技术和发展趋势。 总之,DSP学习需要坚持不懈,理论与实践相结合,多读书、多实践,才能逐步掌握。 ### 回答2: 关于如何学习并掌握数字信号处理 (DSP) 的知识,我建议从以下几个方面入手: 1. 基础数学知识:DSP 依赖于一些基本的数学知识,如线性代数、概率论、微积分等。因此,拥有坚实的数学基础是非常重要的。如果你已经有了这些基础,那么可以直接开始学习 DSP。 2. 学习 DSP 理论:DSP 可以从理论与实践两个方面来进行学习。首先,你可以阅读相关的书籍和材,如“数字信号处理”(Alan V. Oppenheim)、"数字信号处理导论" (John G. Proakis) 等,通过了解 DSP 的理论知识,进一步深入 DSP 的概念和算法。 3. 学习 DSP 实践:除了理论部分的学习,你还应该了解 DSP 的实践应用。这可以通过实验或者模拟得到实现。在这一方面,你可以从学习一些常用的 DSP 硬件平台开始,如 Texas Instruments 的 C2000 和 C6000 DSP 系列,或者者一些常见的软件平台,如 Matlab 和 Simulink。 4. 不断练习与实践:这是最重要的步骤,只有通过不断的练习和实践才能真正掌握 DSP 的技能。你可以通过做一些设计项目来实践,如滤波器的设计、语音信号处理、图像处理等。此外,也可以参加一些 DSP 相关的竞赛和实习项目来提升自己的实践经验。 总之,DSP 是一个需要持续不断学习和实践的领域。通过这些学习和实践的方式,我们可以不断地提高自己的 DSP 技能,从而更好地应用于我们的实际工作中。 ### 回答3: 学习 DSP(数字信号处理)是一项有挑战性的任务,但掌握了这个领域的基础知识,你就可以应用它来解决各种实际问题。以下是一些手把手你学 DSP 的建议和步骤: 1. 找到一本优质的学习资料,如《数字信号处理与MATLAB》或《数字信号处理》。这些书籍不仅会你基本概念和原理,还提供了实际案例和代码示例。 2. 熟悉 DSP 的数学基础,包括傅里叶变换、Z 变换、LaPlace 变换等。同时,了解采样定理和滤波器设计的基础知识也很重要。 3. 安装 MATLAB 软件并学会使用它进行数字信号处理。有很多关于 Matlab 的学习资料可以帮助你学会基本的编程和信号处理技能。 4. 尝试使用不同类型的滤波器,如 FIR(有限脉冲响应)和 IIR(无限脉冲响应)滤波器。掌握滤波器的设计和实现技巧对于 DSP 任务至关重要。 5. 学习数字滤波的原理和应用。数字滤波器可以用于去除噪声和干扰,在信号恢复和增强等方面也具有广泛的应用。 总之,学习 DSP 需要坚定的决心和充分的时间投入。通过系统学习、实践和探究,你将能够深入理解 DSP 的原理和应用,掌握数字信号处理的核心技能。