原文链接:Qt学习之路(28): 坐标变换

 
同很多坐标系统一样,QPainter的默认坐标的原点(0, 0)位于屏幕的左上角,X轴正方向是水平向右,Y轴正方向是竖直向下。在这个坐标系统中,每个像素占据1 x 1的空间。你可以把它想象成是一张坐标值,其中的每个小格都是1个像素。这么说来,一个像素的中心实际上是一个“半像素坐标系”,也就是说,像素(x, y)的中心位置其实是在(x + 0.5, y + 0.5)的位置上。因此,如果我们使用QPainter在(100, 100)处绘制一个像素,那么,这个像素的中心坐标是(100.5, 100.5)。
 
这种细微的差别在实际应用中,特别是对坐标要求精确的系统中是很重要的。首先,只有在禁止反走样,也就是默认状态下,才会有这0.5像素的偏移;如果使用了反走样,那么,我们画(100, 100)位置的像素时,QPainter会在(99.5, 99.5),(99.5, 100.5),(100.5, 99.5)和(100.5, 100.5)四个位置绘制一个亮色的像素,这么产生的效果就是在这四个像素的焦点处(100, 100)产生了一个像素。如果不需要这个特性,就需要将QPainter的坐标系平移(0.5, 0.5)。
 
这一特性在绘制直线、矩形等图形的时候都会用到。下图给出了在没有反走样技术时,使用drawRect(2, 2, 6, 5)绘制一个矩形的示例。在No Pen的情况下,请注意矩形左上角的像素是在(2, 2),其中心位置是在(2.5, 2.5)的位置。然后注意下有不同的Pen的值的绘制样式,在Pen宽为1时,实际画出的矩形的面积是7 x 6的(图出自C++ GUI Programming with Qt4, 2nd Edition):
在具有反走样时,使用drawRect(2, 2, 6, 5)的效果如下(图出自C++ GUI Programming with Qt4, 2nd Edition):
注意我们前面说过,通过平移QPainter的坐标系来消除着0.5像素的差异。下面给出了使用drawRect(2.5, 2.5, 6, 5)在反走样情况下绘制的矩形(图出自C++ GUI Programming with Qt4, 2nd Edition):
请对比与上图的区别。
 
在上述的QPainter的默认坐标系下,QPainter提供了视口(viewport)窗口(window)机制,用于绘制与绘制设备的大小和分辨率无关的图形。视口和窗口是紧密的联系在一起的,它们一般都是矩形。视口是由物理坐标确定其大小,而窗口则是由逻辑坐标决定。我们在使用QPainter进行绘制时,传给QPainter的是逻辑坐标,然后,Qt的绘图机制会使用坐标变换将逻辑坐标转换成物理坐标后进行绘制。
 
通常,视口和窗口的坐标是一致的。比如一个600 x 800的widget(这是一个widget,或许是一个对话框,或许是一个面板等等),默认情况下,视口和窗口都是一个320 x 200的矩形,原点都在(0, 0),此时,视口和窗口的坐标是相同的。
 
注意到QPainter提供了setWindow()和setViewport()函数,用来设置视口和窗口的矩形大小。比如,在上面所述的320 x 200的widget中,我们要设置一个从(-50, -50)到(+50, +50),原点在中心的矩形窗口,就可以使用
 
painter.setWindow(-50, -50, 100, 100);
 
其中,(-50, -50)指明了原点,100, 100指明了窗口的长和宽。这里的“指明原点”意思是,逻辑坐标的(-50, -50)对应着物理坐标的(0, 0);“长和宽”说明,逻辑坐标系下的长100,宽100实际上对应物理坐标系的长320,宽200。
 
或许你已经发现这么一个好处,我们可以随时改变window的范围,而不改变底层物理坐标系。这就是前面所说的,视口与窗口的作用:“绘制与绘制设备的大小和分辨率无关的图形”,如下图所示(图出自C++ GUI Programming with Qt4, 2nd Edition):
 
 
除了视口与窗口的变化,QPainter还提供了一个“世界坐标系”,同样也可以变换图形。所不同的是,视口与窗口实际上是统一图形在两个坐标系下的表达,而世界坐标系的变换是通过改变坐标系来平移、缩放、旋转、剪切图形。为了清楚起见,我们来看下面一个例子:
 
void PaintedWidget::paintEvent(QPaintEvent *event)

{

        QPainter
painter(this);

        QFont
font("Courier",
24);

        painter.setFont(font);

        painter.drawText(50,
50, "Hello,
world!");

        QTransform
transform;

        transform.rotate(+45.0);

        painter.setWorldTransform(transform);

        painter.drawText(60,
60, "Hello,
world!");

}

 
为了显示方便,我在这里使用了QFont改变了字体。QPainter的drawText()函数提供了绘制文本的功能。它有几种重载形式,我们使用了其中的一种,即制定文本的坐标然后绘制。需要注意的是,这里的坐标是文字左下角的坐标(特别提醒这一点,因为很多绘图系统,比如Java2D都是把左上角作为坐标点的)!下面是运行结果:
 
我们使用QTransform做了一个rotate变换。这个变换就是旋转,而且是顺时针旋转45度。然后我们使用这个变换设置了QPainter的世界坐标系,注意到QPainter是一个状态机,所以这种变换并不会改变之前的状态,因此只有第二个Hello,
world!被旋转了。确切的说,被旋转的是坐标系而不是这个文字!请注意体会这两种说法的不同。

qt 坐标变换的更多相关文章

  1. Qt学习之路(28): 坐标变换

    经过前面的章节,我们已经能够画出一些东西来,主要就是使用QPainter的相关函数.今天,我们要看的是QPainter的坐标系统.   同很多坐标系统一样,QPainter的默认坐标的原点(0, 0) ...

  2. 基于C/S架构的3D对战网络游戏C++框架 _04客户端详细设计与OpenGL、Qt基础

    本系列博客主要是以对战游戏为背景介绍3D对战网络游戏常用的开发技术以及C++高级编程技巧,有了这些知识,就可以开发出中小型游戏项目或3D工业仿真项目. 笔者将分为以下三个部分向大家介绍(每日更新): ...

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

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

  4. 《Linux与Qt程序设计》知识框架

    本文主要是通过一本书来大致了解Qt开发的框架,不对具体内容做详细分析. 1.首先弄清楚概念:定义->以自己的话理解是什么-> 实现的是什么功能->用在哪些地方 2.前面认识到的知识点 ...

  5. QT的Paint 系统

    下面对于QT的绘制系统做一个简要说明, 这个系统主要由三部分组成,  QPainter, QPaintDevice, QPaintEngine. QPainter 是一个绘制接口类,提供绘制各种面向用 ...

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

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

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

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

  8. qt坐标系统

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

  9. QT 二维图形 原理、发展及应用

    转载自 网易博客:sun的博客 http://zhouyang340.blog.163.com/blog/static/3024095920126710504178/ 2D绘图 Qt4中的2D绘图部分 ...

随机推荐

  1. python打包工具 cx_Freeze介绍

    原理 Python 脚本在装有 Python 的系统中可以直接双击运行,但绝大多数普通用户并没有配置此类环境,而编译为可执行二进制文件后,用户无需预先安装 Python 及依赖库即可像运行普通程序一样 ...

  2. Spring 1 控制反转、依赖注入

    1.1 Spring的核心是控制反转(IoC)和面向切面(AOP) 学习spring之前的开发中通过new创建一个对象,有了spring之后,spring创建对象实例-IoC控制反转,之后需要实例对象 ...

  3. 谷歌浏览器F12基本用法

    第一步:打开你想进行调试的页面,并按F12进入到调试模式 此处以百度页面为例进行功能展示 这是关于最右侧“元素选择器”的功能展示 关于第二个功能的使用,这个功能是将页面适应成手机屏幕大小, eleme ...

  4. notes for lxf(四)

    类名首字母通常大写 创建实例 类名 +() __init__方法 创建实例时把一些属性绑上去 __init__方法第一参数永远是self 表示船舰的实例本身 类是实例的模板 实例是一个一个具体的对象 ...

  5. ORA-01455

    Oracle 用exp 导出数据库的时候,可能会遇到这个错误: Encountering errors in Export logfileEXP-00008: Oracle error # encou ...

  6. PCB Mark点相关

    1)Mark点用于锡膏印刷和元件贴片时的光学定位.根据Mark点在PCB上的作用,可分为拼板Mark点.单板Mark点.局部Mark点(也称器件级MARK点) 2)拼板的工艺边上和不需拼板的单板上应至 ...

  7. python多进程学习笔记

    # coding=utf-8 from multiprocessing import Process,Pool import os import time def printf(args): # ti ...

  8. 英语词汇—V01

    今日词汇 1, wash [wɒʃ] n. 洗涤:洗的衣服:化妆水:冲积物 vt. 洗涤:洗刷:冲走:拍打 vi. 洗澡:被冲蚀 2, dust [dʌst] n. 灰尘:尘埃:尘土 vt. 撒:拂去 ...

  9. 使用mongo获取文章

    先在命令行中输入mongo,进入mongo,然后 show dbs # 从结果中发现有cmb_demo_23_hacker use cmb_demo_23_hacker db.all_in_one.f ...

  10. CommonsChunkPlugin

    CommonsChunk 插件的作用就是提取代码中的公共代码,然后将公共模块打包到一个独立的文件中,以便在其它的入口和模块中使用,原理就是把多个入口共同的依赖都给定义成一个新入口 多种打包情况: 单一 ...