找回密码
 立即注册
收起左侧

Qt 2D绘图部分窗口、视口的研究

3
回复
3209
查看
[复制链接]
累计签到:825 天
连续签到:3 天
来源: 2017-5-7 19:03:56 显示全部楼层 |阅读模式
开始前创建一个Widget窗口应用(其默认宽400像素,高300像素,左上角为原点)。

首先正常绘制一个正方形:

  1. void Widget::paintEvent(QPaintEvent *)
  2. {
  3.     QPainter p(this);
  4.     p.setPen(Qt::blue);
  5.     p.drawRect(0, 0, 100, 100);
  6. }
复制代码


效果如下图:


这个是没有问题的,因为现在的绘图设备就是Widget,其左上角就是原点(0, 0)点。现在我们使用setWindow来设置逻辑坐标矩形:

  1. void Widget::paintEvent(QPaintEvent *)
  2. {
  3.     QPainter p(this);
  4.     p.setPen(Qt::blue);
  5.     p.drawRect(0, 0, 100, 100);

  6.     p.setWindow(-50, -50, 100, 100);
  7.     p.setPen(Qt::red);
  8.     p.drawRect(0, 0, 100, 100);
  9. }
复制代码

这时,效果如下图所示。


现在来说p.setWindow(-50, -50, 100, 100)的作用,它将逻辑坐标矩形(后面提到的术语window窗口)与我们现在的设备物理坐标矩形(后面提到的术语viewport视口)进行了线性映射,这里所说的设备物理坐标矩形就是我们可见的Widget的坐标,就是左上角为(0, 0)点,宽400,高300这样的矩形,线性映射的示意图如下:




也就是说,调用p.setWindow(-50, -50, 100, 100)之后,再次使用p进行绘制,那么坐标原点就不再是Widget的左上角了,而是到了其中心,以前绘制的宽100、高100的正方形,现在也会按比例变为宽400, 高300,也就是我们看到的这个红色矩形。


再来修改代码:


  1. void Widget::paintEvent(QPaintEvent *)

  2. {
  3.     QPainter p(this);
  4.     p.setPen(Qt::blue);
  5.     p.drawRect(0, 0, 100, 100);

  6.     p.setWindow(-50, -50, 100, 100);
  7.     p.setPen(Qt::red);
  8.     p.drawRect(0, 0, 20, 20);
  9. }
复制代码


运行效果如下图所示:




我们将绘制的红色矩形变小,可以明显看到,本应该是个正方形,现在却变成了长方形。就是因为上面说的比例变换造成的,那么怎么才能让它显示应有的形状呢,我们来设置视口:


  1. void Widget::paintEvent(QPaintEvent *)
  2. {
  3.     QPainter p(this);
  4.     p.setPen(Qt::blue);
  5.     p.drawRect(0, 0, 100, 100);

  6.     int side = qMin(width(), height());
  7.     p.setViewport((width() - side)/2, (height() - side) /2, side, side);
  8.     p.setWindow(-50, -50, 100, 100);

  9.     p.setPen(Qt::red);
  10.     p.drawRect(0, 0, 20, 20);
  11. }
复制代码



现在使用setViewport设置视口为一个正方形,就是Widget可是区域上最大的正方形,这样逻辑坐标和物理坐标进行比例变换的时候,红色矩形的宽和高就不会因为缩放的比例不同而发生变形了,如下图所示。





问题提出:那么为什么要修改这个逻辑坐标矩形?

这是为了便于我们绘图,因为我们一般绘图时只是想在标准的坐标系中应该绘制成什么样子,不会考虑不同绘图设备的具体坐标系(比如有的设备坐标原点在其左上角,有的在中心等等),也不会考虑窗口的大小不同而使用不同的代码(比如我们只想在一个宽100、高100的绘图区域的中心绘制一个高20、宽20的正方形,到底实际绘图设备的单位是像素、还是英寸、还是厘米,我们不用考虑)。




引申术语:窗口、视口

这里说的我们想象中的宽100、高100、原点在中心的绘图区域,就是逻辑坐标下的矩形,也就是使用setWindow设置的所谓的窗口(英文为Window,注意与窗口部件的窗口二字意义不同);而实际的绘图设备,比如这里的Widget部件,其可视化的区域上设置的一个矩形被称为视口(英文为viewport),默认就是可视化区域的大小,但是可以通过setViewport来设置。窗口与视口相对应,可以进行线性变换,这样,我们就可以通过先设置视口,再设置对应的窗口的方法,来确保我们的代码在标准的想象中的坐标系中绘制的图形,可以准确地显示在不同的绘图设备界面上。





本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
参与人数 3人气 +5 收起 理由
bai9988 + 2
Since + 1
xsqxsy + 2 很详细!

查看全部评分总评分 : 人气 +5

回复

使用道具 举报

累计签到:3 天
连续签到:1 天
2017-5-7 21:01:41 显示全部楼层
先占个位置。看完回复。
回复 支持 反对

使用道具 举报

累计签到:3 天
连续签到:1 天
2017-5-8 22:14:41 显示全部楼层
今天实验了一下,发现其实可以简单的理解。
由一个画布 例如一个QPixmap,大小400,300
默认的时候 viewport 和 window 大小都是400,300
当用setviewport改变viewport的时候相当于在这个画布上画出来一个区域例如setviewport(100,100,200,200),用来与window来映射,如果此时没有修改window,那么就相当于画布上面的100,100到300,300这个区域W,H分别是200,200与window的400,300进行了映射,window的原点0,0与QPixmap的100,100相对应。
如果window也做了设置例如setwindow(-20,-20,200,200),那么此时window的原点位置变成了window的20,20,此时原点坐标对应QPixmap的是100+(20*200/200),100+(20*200/200),括号中的表示( window坐标原点*viewport长度/window长度)。
在进行绘制之前不论先设置viewport还是先设置window,最后结果一样,绘制之前Qt会完成坐标转换,绘制的时候直接进行线性变换。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册