工程存放的路径在:Examples\Qt-XX.XX.XX\widgets\painting\affine

其中XX.XX.XX为Qt的版本号,如:5.14.1。

该工程有如下个人认为的难点、亮点:

  • 《利用QPainter、QColor绘制黑白棋盘功能》
  • 《QPushButton延时单击功能》
  • 《利用QCommonStyle绘制自定义的窗体部件》

对XFormView类的drawPixmapType函数解释:

void XFormView::drawPixmapType(QPainter *painter)
{QPointF center(m_pixmap.width() / qreal(2), m_pixmap.height() / qreal(2));/* 将painter移动(ctrlPoints.at(0) - center)距离,也即将HoverPoints类对象pts的第一个控制点移动到位图的中心位置,以便第一个控制点绘制在位图的中心*/painter->translate(ctrlPoints.at(0) - center);// 进行变换,以便位图绕着自己的中心轴旋转painter->translate(center);painter->rotate(m_rotation);painter->scale(m_scale, m_scale);painter->shear(0, m_shear);painter->translate(-center);.............................// 其它代码
}

如果被绘制对象(本例是指m_pixmap对象表示的图片)的中心点和绘图对象painter的中心点不重合,这时候旋转被绘制对象,则这个对象会围绕painter设置的中心点进行旋转,会转一个大圈。那么怎么做才能让它在任何位置的时候,都围绕自己的轴心进行旋转?解决思路如下:

1. 先保存物体在世界坐标系下的坐标,即物体在世界坐标系下的中心点坐标
2. 再将物体移动到世界坐标系的原点。
3. 在世界坐标系的原点旋转好后,再移动回原来的位置,即步骤1中的提到的坐标。

上段代码的思路就是按这样来的。

  • drawTextType函数同drawPixmapType函数,不再赘述。
  • HoverPoints分析

构造函数分析:

HoverPoints::HoverPoints(QWidget *widget, PointShape shape): QObject(widget)
{m_widget = widget;widget->installEventFilter(this);widget->setAttribute(Qt::WA_AcceptTouchEvents);m_connectionType = CurveConnection;m_sortType = NoSort;m_shape = shape;m_pointPen = QPen(QColor(255, 255, 255, 191), 1);m_connectionPen = QPen(QColor(255, 255, 255, 127), 2);m_pointBrush = QBrush(QColor(191, 191, 191, 127));m_pointSize = QSize(11, 11);m_currentIndex = -1;m_editable = true;m_enabled = true;connect(this, &HoverPoints::pointsChanged,m_widget, QOverload<>::of(&QWidget::update));
}

构造函数初始化了一些变量,对各个变量作用解释如下:

  • m_widget:外层传入的窗体部件对象,也即HoverPoints类附属在哪个窗体上,通过通读代码可以发现其是指外层的XFormView窗体对象。
  • 函数的第5行为外层传入的窗体部件对象安装了事件过滤器。installEventFilter函数的用法请参见《installEventFilter、eventFilter函数理解》
  • 第6行设置触屏事件,从后续代码可以看到本例支持触屏操作。
  • m_connectionType:连接类型。取值为:直线、曲线(贝塞尔曲线)和无
  • m_sortType: 点的排序类型。取值为:不排序、按点的x坐标排序、按点的y坐标排序。
  • m_shape:悬浮点的现状。取值为:圆形、矩形。
  • m_pointPen:用于画悬浮点的画笔。
  • m_connectionPen:用于画连接线的画笔。
  • m_pointBrush:用于点的画刷。
  • m_pointSize:点所在形状的外围矩形宽高。
  • m_currentIndex:当前在控制点容器m_points中的点的索引。
  • m_editable:是否可编辑。
  • m_enabled:是否可用。

     eventFilter 中的鼠标左键按下流程分析:

如下鼠标左键按下时的流程:(图片下载到本地放大看):

说明:

  1. 本程序不仅支持鼠标还支持触摸显示屏操作。上述流程是支持鼠标且鼠标左键按下流程。
  2. 程序根据m_sortType来决定坐标排序类型,在构造函数中默认不排序。当排序类型为XSort    时,则从控制点的容器m_points中找到第1个横坐标大于鼠标按下时鼠标单击点的横坐标的点。当排序类型为YSort 时,则从控制点的容器m_points中找到第1个纵坐标大于鼠标按下时鼠标单击点的纵坐标的点,并记录该点在m_points中的索引号。这样做的效果是为了实现如下效果:

起始m_points中只有A、B两个控制点,则画出上图类似的A到B的线,当鼠标左键单击的点为C时,且C在A、B之间时,则画出如下直线:

当C在B 的右侧时,则画出如下的:

3 .鼠标左键按下时,当步骤2完成后,将符合条件的鼠标左键单击时所在的点插入m_points、m_locks,插入的索引号为步骤2记录的索引号然后调用 firePointChange函数。

4. firePointChange函数中根据m_sortType表示的排序规则将m_points排序。排序后再次从m_points中找到步骤2鼠标左键按下时的点在m_points中的索引m_currentIndex,然后发送pointsChanged信号。

eventFilter 中的触摸事件流程分析:

即QEvent::TouchBegin、QEvent::TouchUpdate、Qt::TouchPointReleased、QEvent::TouchEnd事件。其中最需要搞懂的是QEvent::TouchBegin、QEvent::TouchUpdate中的Qt::TouchPointPressed,实现代码如下:

// find the point, move it
const auto mappedPoints = m_fingerPointMapping.values();
QSet<int> activePoints = QSet<int>(mappedPoints.begin(), mappedPoints.end());
int activePoint = -1;
qreal distance = -1;
const int pointsCount = m_points.size();
const int activePointCount = activePoints.size();
if (pointsCount == 2 && activePointCount == 1)
{ // only two pointsactivePoint = activePoints.contains(0) ? 1 : 0;
}
else
{for (int i = 0; i < pointsCount; ++i) {if (activePoints.contains(i))continue;qreal d = QLineF(touchPoint.pos(), m_points.at(i)).length();if ((distance < 0 && d < 12 * pointSize) || d < distance) {distance = d;activePoint = i;}}
}
if (activePoint != -1)
{m_fingerPointMapping.insert(touchPoint.id(), activePoint);movePoint(activePoint, touchPoint.pos());
}

其基本思路是:将触摸点的id和触摸点索引值作为QHash的键值对插入到类型为QHash的成员变量m_fingerPointMapping,以后移动、释放都从m_fingerPointMapping取出相应的点进行操作。

eventFilter 中的QEvent::Paint流程分析:

代码如下:

            QWidget *that_widget = m_widget;m_widget = nullptr;QCoreApplication::sendEvent(object, event);m_widget = that_widget;paintPoints();return true;

这里需要着重说明的是:在发送QEvent::Paint事件之前必须m_widget将先保存起来(第1句代码),然后将m_widget设置为nullptr(第2句),发送完后再将m_widget恢复为原来的。设置为nullptr是避免eventFilter 函数无限递归调用导致栈耗尽程序崩溃,恢复回来为了下次再次发送QEvent::Paint事件。

updateCtrlPoints函数分析:

void XFormView::updateCtrlPoints(const QPolygonF &points)
{QPointF trans = points.at(0) - ctrlPoints.at(0);if (qAbs(points.at(0).x() - points.at(1).x()) < 10&& qAbs(points.at(0).y() - points.at(1).y()) < 10)pts->setPoints(ctrlPoints);if (!trans.isNull()) {ctrlPoints[0] = points.at(0);ctrlPoints[1] += trans;pts->setPoints(ctrlPoints);}ctrlPoints = points;QLineF line(ctrlPoints.at(0), ctrlPoints.at(1));m_rotation = 360 - QLineF(0, 0, 1, 0).angleTo(line);if (trans.isNull())emit rotationChanged(int(m_rotation * 10));
}

第3行代码:就是计算通过信号pointsChanged发送过来的m_points第一个控制点和当前的第一个控制点的在x、y坐标上的偏移量。

第5-7行:检测m_points第1个控制点、第2个控制点x、y坐标上的偏移量都小于10,如果是,则控制点还是设置为当前控制点。

第8-11行:如果第3句代码算出的偏移量不为空,则当前第1个控制点更新为m_points中的第1个点,注意:在m_sortType为NoSort时,且鼠标左键按下时,m_points中的第1个点为鼠标左键按下时的点,而在XFormView类的paint函数绘制函数如:drawPixmapType、drawTextType经过如下代码将绘制中心移动到了被绘制对象(如:图片、文本)的中心了(参见前面对drawPixmapType的分析):

painter->translate(ctrlPoints.at(0) - center);

所以这样造成的现象是鼠标左键在哪单击,被绘制物体的中心就移动到鼠标左键单击的点。

当前第2个控制点累加第3行产生的偏移量,并重新设置HoverPoints类的控制点容器m_points

第15-16行根据更新后的控制点ctrlPoints算出其和水平线的夹角,并设置旋转成员变量m_rotation。根据Qt的元系统属性技术( 属性通过Q_PROPERTY关键字标识),一旦m_rotation被更新,则XFormView类的setRotation函数会自动调用,从而导致被绘制的物体呈现旋转。

程序bug说明:

HoverPoints类的m_sortType 为 XSort或YSort时,会崩溃,更改如下:

movePoint函数加入如下代码:

void HoverPoints::movePoint(int index, const QPointF &point, bool emitUpdate)
{// 索引越界了,要判断下if ((index < 0) || (m_points.size() <= index) ){return;}if ( m_locks.size() <= index ){return;}// 其它代码
}

firePointChange()函数加入如下代码:

void HoverPoints::firePointChange()
{//    printf("HoverPoints::firePointChange(), current=%d\n", m_currentIndex);if (m_sortType != NoSort) {QPointF oldCurrent;/*if (m_currentIndex != -1) {*/// 这里有崩溃,索引越界了,要判断下if ((0 <= m_currentIndex) && (m_currentIndex < m_points.size())){oldCurrent = m_points[m_currentIndex];}// 其它代码}

XFormView类的updateCtrlPoints函数加入如下判断:

void XFormView::updateCtrlPoints(const QPolygonF &points)
{// 右键在控制点单击时,这里有崩溃,索引越界了,要判断下if (points.size() < 2){return;}..................
}

未完,待续!

affine工程难点、亮点汇总相关推荐

  1. PHP 高级工程面试题汇总

    PHP高级工程面试题汇总(2018.05) 1.给你四个坐标点,判断它们能不能组成一个矩形,如判断([0,0],[0,1],[1,1],[1,0])能组成一个矩形. 勾股定理,矩形是对角线相等的四边形 ...

  2. python-重难点知识汇总

    重难点知识汇总 python使用的是动态语言,也就是说在变量定义时不需要指定对应的数据类型.但是输入进来还是用int()函数把输入字符串变成整形数据. 多行字符串可以用 ''' - ''' 表示. 前 ...

  3. 计算机二级word难点大全,2019年3月计算机二级Word难点重点汇总

    计算机二级Word难点重点汇总 1.新建word文件,命名word文件.另存为. 2.字体:字体,颜色,加粗,斜体,下划线,着重号,字符间距.字符位置的设置.[如果给你一个图片样张,让你设置字体字号颜 ...

  4. 关于机器人状态估计8-VSLAM工程与VIO工程难点

    最近做产设和综述把头都整晕了,本来想认真更新GPU那篇DSP的姊妹篇,突然觉得太长了实在是没有写的欲望,还是写篇杂文算了...一看这个标题就知道 首先如果没有关于机器人状态估计(4)-成长路径与能力提 ...

  5. 创维linux进入工厂模式,智能电视进入工厂/工程模式教程汇总!

    大部分品牌的智能电视产品出厂时都内置了应用商店,但是这些商店内下载的软件都是比较鸡肋的应用,没有实际作用.而且还有很多电视不支持U盘安装第三方软件,现在给大家介绍一种方法,解决智能电视无法安装第三方软 ...

  6. 计算机二级关于ppt背景音乐,计算机二级PPT难点重点汇总 拿分必备

    很多同学想知道计算机二级PPT难点重点汇总有哪些,下面是小编整理的相关内容,希望对大家有所帮助! 计算机二级PPT难点重点汇总 1.新建幻灯片(从大纲,重用幻灯片) 2.设置版式,分节,重命名节. 3 ...

  7. 腾讯三面:说说前端监控平台/监控SDK的架构设计和难点亮点?

    点击上方 前端Q,关注公众号 回复加群,加入前端Q技术交流群 前言 事情是这样的,上周,我的一位两年前端经验的发小,在 腾讯三轮面试 的时候被问了一个问题:说说你们公司前端监控项目的架构设计和亮点设计 ...

  8. python的难点_汇总Python初学者常见的学习难点

    初学Python,面对一个陌生的新生物,是有一些恐惧心理,在学习Python时难免会遇到这样或那样的槛,在这里,IT培训网老师汇总了一些Python初学者的常见的知识难点,希望大家有所帮助. Pyth ...

  9. 精彩回顾丨神策 2020 数据驱动用户大会亮点汇总

    10 月 13  日 - 14 日,神策 2020 数据驱动用户大会以"数字化 正当潮"为主题在北京香格里拉酒店成功举行,会议聚焦疫情之下国内各大企业转型线上的数字化"新 ...

最新文章

  1. android 三级界面返回直接到一级界面
  2. mysql中char与varchar的区别分析(补充一句,int和integer没区别)
  3. 慕课网Flask高级编程实战-10.鱼书业务处理
  4. Android—将Bitmap图片保存到SD卡目录下或者指定目录
  5. 车道线检测参考学习资料
  6. 花生增产万书波谋定中国农民丰收节交易会 山东科技最高奖
  7. c语言程序可以单独编译,c语言中的函数可不可以单独进行编译?_后端开发
  8. boost::geometry::detail::overlay::approximately_equals用法的测试程序)
  9. 网络克隆报a:\ghosterr.txt故障解决一例
  10. MySQL 数据库 like 语句通配符模糊查询小结
  11. HAOI2008 硬币购物
  12. 基于MATLAB的汽车出入库计时系统 _matlab课程设计
  13. tftpd64-SE使用
  14. POI读取Excel表格时遇到科学计数法处理
  15. 关于卸载office的问题:office无法卸载的办法(附office安装和注册表查看)
  16. GigabitEthernet和Ethernet接口的区别
  17. 【Flutter 问题系列第 15 篇】如何给 Flutter 中的图片设置透明度
  18. java 接口 练习题_JAVA 接口练习题
  19. 华为鸿蒙os状态栏,华为再推新版鸿蒙OS系统!UI外观设计大变样 多达19款机型可升级...
  20. linux中sl是什么命令,Linux系统使用sl命令制作火车动态桌面的技巧

热门文章

  1. 这100道Linux常见面试题,看看你会多少?
  2. 值得一看的PCB接地设计规范!
  3. LL-verilog语法:case用法
  4. 《c语言从入门到精通》看书笔记——第14章 文件
  5. linux nfs共享目录集群,CentOS7通过NFS共享文件夹,主要用于集群部署时候,文件的共享...
  6. 计算机显示有可移动存储,winxp系统中我的电脑出现很多个可移动磁盘怎么办
  7. php 启动手机浏览器,php实现读取手机客户端浏览器的类
  8. rabbitnq 源码安装_linux下源码安装rabbitMq
  9. java中如何判断aabb_java判断改错题
  10. 笔记本电脑没有鼠标怎么右键_联想笔记本电脑没有声音怎么修复