简述

坐标系统是由QPainter类控制的,再加上QPaintDevice和QPaintEngine类,就形成了Qt的绘图体系。

  • QPainter:用于执行绘图操作。

  • QPaintDevice:二维空间的抽象层,可以使用QPainter在它上面进行绘制。

  • QPaintEngine:提供了统一的接口,用于QPainter在不同的设备上进行绘制。

QPaintDevice类是可以被绘制的对象的基类,它的绘图功能由QWidget、QImage、QPixmap、QPicture和QOpenGLPaintDevice继承。默认坐标系统位于设备的左上角,即坐标原点(0, 0)。X轴由左向右增加,Y轴由上向下增加。在基于像素的设备上(比如:显示器),坐标的默认单位是1像素;在打印机上则是1点(1/72 英寸)。

QPainter逻辑坐标与QPaintDevice物理坐标的映射,由QPainter的变换矩阵(transformation matrix)、视口(viewport)和窗口(window)完成。默认情况下,物理坐标与逻辑坐标系统是重合的,QPainter也支持坐标转换,例如:旋转、缩放。

渲染

逻辑表示

一个图形图元的大小(宽度和高度)总是对应于它的数学模型,忽略绘制时画笔的宽度:

锯齿绘制

绘制的时候,像素渲染由QPainter::Antialiasing来控制。

枚举RenderHint用于指定QPainter的渲染标志,绘图引擎会用到。QPainter::Antialiasing表示引擎应该尽可能的让图元边缘抗锯齿,即:使用不同的颜色亮度让边缘平滑。

默认情况下,QPainter绘制时有锯齿,并且有其它规则:当用1像素宽的画笔绘制时,像素会被绘制在右下角。例如:

绘制时,如果画笔的宽度像素是偶数,则实际绘制会包裹住逻辑坐标值;如果是奇数,则是包裹住逻辑坐标值,再加上右下角1个像素的偏移。具体请看下面QRectF图示:

注意:由于历史原因,QRect::right()和QRect::bottom()的返回值并不是矩形右下角的真实坐标值。

QRect::right()返回:left() + width() – 1;QRect::bottom()返回:top() + height() – 1。上图中右下角的绿色点指出了这两个函数返回的坐标值。

为避免这个问题,建议是使用QRectF。QRectF使用浮点精度的坐标来定义一个平面矩形(QRect则使用整形坐标)。函数QRectF::right()和QRectF::bottom()会返回真正的右下角坐标值。

如果要使用QRect,可以利用x() + width()和y() + height()来替代right()和bottom()。

抗锯齿绘制

如果设置了QPainter的抗锯齿标志,像素就会被均匀地绘制在两侧。

坐标转换

默认情况下,QPainter操作相关设备自身的坐标系统,但它也完全支持仿射坐标转换。

你可以缩放坐标系统,使用QPainter::scale()函数并指定一个偏移量;可以使用QPainter::rotate()函数来顺时针旋转它;可以使用QPainter::translate()函数来平移它。

还可以使用QPainter::shear()函数绕着原点扭曲坐标系统,所有的转换操作都作用在QPainter的转换矩阵上,可以使用QPainter::worldTransform()进行检索,一个矩阵转换平面上的一个点到另一个点。

如果你需要进行反复的相同转换,可以使用QTransform对象和QPainter::worldTransform()、QPainter::setWorldTransform()函数。通过调用QPainter::save()函数(保存矩阵在内部堆栈上),你可以随时保存QPainter的转换矩阵,QPainter::restore()函数用于恢复上次的结果。

矩阵转换的频繁需求是在不同的绘图设备上使用相同的绘制代码。没有转换,结果被紧密地结合到绘图设备的分辨率上。打印机具有高分辨率,例如:600 DPI(每英寸点数),而屏幕的通常为72 - 100 DPI。

窗口-视口转换

当使用QPainter绘制时,我们规定了使用逻辑坐标的点,然后转换成绘图设备的物理坐标。

逻辑坐标到物理坐标的映射,由QPainter世界变换worldTransform()(“坐标转换”中所描述的部分)、及QPainter的viewport()、window()处理。视口表示由任意矩形指定的物理坐标;窗口则用逻辑坐标描述了相同的矩形。默认情况下,物理坐标和逻辑坐标一致,都等于绘图设备的矩形。

Qt使用窗口-视口(viewport-window)转换机制可以使逻辑坐标系统符合你的喜好,这也可以让绘图代码独立于绘图设备。例如:调用QPainter::setWindow()函数,以(0, 0)为中心将逻辑坐标从(-50, -50)转换到(50, 50)。

QPainter painter(this);
painter.setWindow(QRect(-50, -50, 100, 100));

现在,逻辑坐标的(-50,-50)对应绘图设备物理坐标的(0, 0)点。独立于绘图设备,你的绘制代码在指定的逻辑坐标上总能运行。

通过设置窗口或视口矩形,执行一个线性变换的坐标。注意:每个窗口的角落映射到相应的视口的角落,反之亦然。因此,这通常是一个好方法,让视口和窗口保持相同的长宽比,防止变形:

int side = qMin(width(), height())
int x = (width() - side / 2);
int y = (height() - side / 2); painter.setViewport(x, y, side, side);

如果让逻辑坐标系统是一个正方形,那么,也应该使用QPainter::setViewport()函数让视口坐标也是一个正方形。上面的示例中,我们让其和最大的正方形相同来适应绘图设备的矩形,当设置窗口或视口时,考虑到绘图设备的大小,也能够保持绘图代码独立于绘制设备。

注意:窗口-视口转换只是一个线性转换,即:它不执行剪切。这意味着,如果你绘制在当前设置的窗口外面,你的绘制依然被转换到视口,使用相同的线性代数方法。

视口、窗口和矩阵转换决定了QPainter如何将逻辑坐标映射到绘图设备的物理坐标。默认情况下,世界变换矩阵是单位矩阵,窗口和视口设置等同于绘图设备的设置,即世界、窗口和设备坐标系统是等价的。正如我们所看到的,系统可以使用转换运算和窗口-视口转换进行操作,上面的图说明了过程。

更多参考

  • Coordinate System - 助手

Qt之坐标系统的更多相关文章

  1. 每日一笔记之2:QT之坐标系统:

    以前一直多单片机开发,也没怎么使用过大的显示器,第一次学习,备忘: QT画图系统. 绘图,通过QPainter类实现. Qt的绘图系统对底层函数进行了良好的封装,使得在屏幕和设备的绘图功能可能使用相同 ...

  2. Qt 的坐标系统

    QWidget *q = , Qt::WindowStaysOnTopHint); q->setWindowTitle(QObject::tr("父窗口widget")); ...

  3. Qt 学习之路:坐标系统

    在经历过实际操作,以及前面一节中我们见到的那个translate()函数之后,我们可以详细了解下 Qt 的坐标系统了.泛泛而谈坐标系统,有时候会觉得枯燥无味,难以理解,好在现在我们已经有了基础. 坐标 ...

  4. Qt 学习之路 :坐标系统

    在经历过实际操作,以及前面一节中我们见到的那个translate()函数之后,我们可以详细了解下 Qt 的坐标系统了.泛泛而谈坐标系统,有时候会觉得枯燥无味,难以理解,好在现在我们已经有了基础. 坐标 ...

  5. qt坐标系统

    #说明:坐标系统是由 QPainter控制的QPaintDevice是那些能够让 QPainter 进行绘制的“东西”(准确的术语叫做,二维空间)# 的抽象层(其子类有QWidget. QPixmap ...

  6. Qt 学习之路 2(28):坐标系统

    Qt 学习之路 2(28):坐标系统 豆子 2012年11月25日 Qt 学习之路 2 59条评论 在经历过实际操作,以及前面一节中我们见到的那个translate()函数之后,我们可以详细了解下 Q ...

  7. Qt 2D绘图之二:抗锯齿渲染和坐标系统

    一.抗锯齿渲染 1.1 逻辑绘图 图形基元的大小(宽度和高度)始终与其数学模型相对应,下图示意了忽略其渲染时使用的画笔的宽度的样子. 1.2 物理绘图(默认情况) 在默认的情况下,绘制会产生锯齿,并且 ...

  8. 《Qt 实战一二三》

    简介 "我们来自Qt分享&&交流,我们来自Qt Quick分享&&交流",不管你是笑了,还是笑了,反正我们是认真的.我们就是要找寻一种Hold不住的 ...

  9. [Qt2D绘图]-02坐标系统&&抗锯齿渲染

    本节的内容可以在帮助中通过Coordinate System关键字查看. 或者入门可以看<Qt Creator 快速入门>这本书.强烈推荐入门使用.下面的内容为本书的阅读笔记,喜欢的可以买 ...

随机推荐

  1. BZOJ 3241: [Noi2013]书法家

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3241 题意: 思路:把每个字母分成三部分,两个字母之间还有空的列,所以我一共设了11个状态 ...

  2. 【转载】20分钟MySQL基础入门

    原文:20分钟MySQL基础入门 这里持续更新修正 开始使用 MySQL 为关系型数据库(Relational Database Management System),一个关系型数据库由一个或数个表格 ...

  3. String一点小发现

    今天面试官问了几个关于java内存方面的问题,其中有一个是关于内存重复使用的.突然想到java中String比较特殊的地方,根据自己的理解所以稍微记录一下以免遗忘. 对于下面这个小程序: public ...

  4. [YY]已知逆序列求原序列(二分,树状数组)

    在看组合数学,看到逆序列这个概念.于是YY了一道题:已知逆序列,求出原序列. 例子: 元素个数 n = 8 逆序列 a={5,3,4,0,2,1,1,0} 则有原序列 p={4,8,6,2,5,1,3 ...

  5. [SAP ABAP开发技术总结]日期函数

    声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...

  6. 数据库mysql中having 和where的区别

    having的用法 having字句可以让我们筛选成组后的各种数据,where字句在聚合前先筛选记录,也就是说作用在group by和having字句前.而 having子句在聚合后对组记录进行筛选. ...

  7. BestRW团队项目创意以及NABCD

    一.写在前面 这次的团队项目我们队选到的是自由选题,与其说是选,不如说是分配.毕竟我们组游戏排名倒数第二···其实当我第一次听说我们队排倒数第二的时候我是有点不爽的,毕竟在这后面能够抽到的题目都是剩下 ...

  8. c++ primer 的 textquery 例子。

    总共3种方法,一种是第四版书上的面向对象的教学方法.一种是实际中应该使用的简洁方法.一种是模板的方法. 1)第四版书中,面向对象的方法,基类,继承,多态 2)自己的更简洁的写法.(前提条件:如果不需要 ...

  9. Java中正则Matcher类的matches()、lookAt()和find()的区别<转>

    在Matcher类中有matches.lookingAt和find都是匹配目标的方法,但容易混淆,整理它们的区别如下: matches:整个匹配,只有整个字符序列完全匹配成功,才返回True,否则返回 ...

  10. bootstrap学习笔记<四>(table表格)

    表格 bootstrap为table表格定制多个常用样式:基本样式,隔行变色样式,带边框样式,荧光棒样式,紧凑样式,响应样式. ☑  .table:基础表格 ☑  .table-striped:斑马线 ...