小谈Qt的坐标系系统 - 3个坐标系,2个变换

发布时间 2023-09-04 15:41:06作者: imxiangzi

小谈Qt的坐标系系统

Qt中有三个坐标系

设备坐标系

窗口坐标系

逻辑坐标系

设备坐标系: 即Device坐标系。也是物理坐标系。即真实的的物理坐标系。

逻辑坐标系: 即用户坐标系。也就是说日常大家使用Qt的时候的坐标系。

窗口坐标系: 这个坐标系是QPainter设置setWindow以后的一个坐标系。

这三个坐标系,基本上就代表了常规的绘制引擎的三个坐标系。

那么三个坐标系,就代表可以用两次变换来实现了。

这个就是QPainter中的setWindow() & setViewport()的两个概念了

直接看下图

 

<img src="https://pic2.zhimg.com/v2-48e08b23b4413e7059b814bc90f29b71_b.jpg" data-caption="" data-size="normal" data-rawwidth="1080" data-rawheight="437" class="origin_image zh-lightbox-thumb" width="1080" data-original="https://pic2.zhimg.com/v2-48e08b23b4413e7059b814bc90f29b71_r.jpg"/>

 

 

The viewport represents the physical coordinates specifying an arbitrary rectangle. The "window" describes the same rectangle in logical coordinates. By default the logical and physical coordinate systems coincide, and are equivalent to the paint device's rectangle.

 

视口表示指定任意矩形的物理坐标。“窗口”在逻辑坐标中描述了相同的矩形。默认情况下,逻辑坐标系和物理坐标系重合,并且等同于绘制设备的矩形。

via https://doc.qt.io/qt-6/coordsys.html#window-viewport-conversion

OK,既然有了这些概念,那么我们就开始讲一下,坐标系的转换流程。

其实,一开始我准备从网上copy一份教程来讲的,但是发现大家讲的全是tm不明不白的。所以这里我决定自己来讲了

废话不多说

还是按照我的方式 & Qt的官方文档来讲吧

我自己的例子

画个图,描述下这三个坐标系

 

<img src="https://pic1.zhimg.com/v2-bddaf11528d32a9e4a24dc0d0ac58fd4_b.jpg" data-caption="" data-size="normal" data-rawwidth="1080" data-rawheight="608" class="origin_image zh-lightbox-thumb" width="1080" data-original="https://pic1.zhimg.com/v2-bddaf11528d32a9e4a24dc0d0ac58fd4_r.jpg"/>

 

 

平时我们的三个坐标系都是相同的,都是水平方向是X轴,垂直方向是Y轴,坐标原点是左上角(0, 0)。

 

X轴从左到右增加,Y轴从上到下增加

即如图所示

 

<img src="https://pic4.zhimg.com/v2-b01f30d2835ca3e44807b5612859bd67_b.jpg" data-caption="" data-size="normal" data-rawwidth="1080" data-rawheight="608" class="origin_image zh-lightbox-thumb" width="1080" data-original="https://pic4.zhimg.com/v2-b01f30d2835ca3e44807b5612859bd67_r.jpg"/>

 

 

那么三个坐标系的变化流程就是

 

逻辑坐标系->setWindow()->窗口坐标系->setViewport()->设备坐标系

还是先上个Qt的Demo

Qt的Demo

设定,屏幕的DPI是96

三个坐标系重叠

先创建一个QWidget,然后设置其大小是(500, 500)

resize(500,500);

然后再重写他的PaintEvent事件

void QtWidgetsApplication1::paintEvent(QPaintEvent*e)

{

QPainter painter(this);

painter.drawLine(0,0,500,500);

}

那么表现

 

<img src="https://pic1.zhimg.com/v2-c0fe3fea97c20cdd725598b40462ab48_b.jpg" data-caption="" data-size="normal" data-rawwidth="503" data-rawheight="531" class="origin_image zh-lightbox-thumb" width="503" data-original="https://pic1.zhimg.com/v2-c0fe3fea97c20cdd725598b40462ab48_r.jpg"/>

 

 

 

这里可以先重点说下

我们以普通用户(程序员)的角度来理解这个代码。因为有三个坐标系,所以Qt的代码全都是以逻辑坐标系为基础的。对于这个代码来讲

painter.drawLine(0,0,500,500);

drawLine的时候实际上就是以逻辑坐标系为准。这时候,三个坐标系是相等的,没有任何比例变化。大家可以理解为这三个坐标系是重叠的。

setWindow()

控制变量,其他的代码不变,我们只单独设置setWindow这个代码setWindow的作用,就是在物理坐标系不变的情况下,将逻辑坐标系进行缩放。

假定,我们的代码是这样的。将逻辑坐标扩大一倍,那么代码就是这样的

void QtWidgetsApplication1::paintEvent(QPaintEvent*e)

{

QPainter painter(this);

painter.setWindow(0,0,1000,1000);

painter.drawLine(0,0,500,500);

}

这时候我们的窗口就变成了这个样子。

 

<img src="https://pic4.zhimg.com/v2-7c75f310b08bb8d21905ae2dab4ab0af_b.jpg" data-caption="" data-size="normal" data-rawwidth="500" data-rawheight="532" class="origin_image zh-lightbox-thumb" width="500" data-original="https://pic4.zhimg.com/v2-7c75f310b08bb8d21905ae2dab4ab0af_r.jpg"/>

 

 

也就是说,我的物理大小,并没有变,我通过更改逻辑坐标,(放大两倍的效果) 看起来将我的整个坐标系变大了。比如原来我可以通过

painter.drawLine(0,0,500,500);

来绘制对角。

经过

painter.setWindow(0,0,1000,1000)\\放大逻辑坐标系两倍

最后

painter.drawLine(0,0,500,500);\\只能绘制一半的效果。

 

 

<img src="https://pic4.zhimg.com/v2-7c75f310b08bb8d21905ae2dab4ab0af_b.jpg" data-caption="" data-size="normal" data-rawwidth="500" data-rawheight="532" class="origin_image zh-lightbox-thumb" width="500" data-original="https://pic4.zhimg.com/v2-7c75f310b08bb8d21905ae2dab4ab0af_r.jpg"/>

 

 

如果还想绘制对角,那么就得

painter.drawLine(0,0,1000,1000);

 

 

<img src="https://pic2.zhimg.com/v2-7bdda7da0a7b5cf8b7e62dc0a8e625a1_b.jpg" data-caption="" data-size="normal" data-rawwidth="895" data-rawheight="544" class="origin_image zh-lightbox-thumb" width="895" data-original="https://pic2.zhimg.com/v2-7bdda7da0a7b5cf8b7e62dc0a8e625a1_r.jpg"/>

 

 

也就是,我们现在将用户的坐标系放大了两倍。这个就是setWindow的作用。

setViewport()

大家可以理解setViewport的作用就是控制物理坐标系的。

实际上,不管是Qt的官方文档,还是网上的例子,实际上setViewport的设置都是变化的,这样就很难给新手理解了。

比如官方代码

int side=qMin(width(),height());

int x=(width()-side/2);

int y=(height()-side/2);

 

painter.setViewport(x,y,side,side); 

还要控制窗口的宽高,这时候就更难理解了。我的例子还是控制变量,假定Window不变。我们只更改ViewPort

还是那个例子,窗口大小(500, 500)。我们还是画对角线。这时候我们把代码改成

void QtWidgetsApplication1::paintEvent(QPaintEvent*e)

{

QPainter painter(this);

painter.setViewport(0,0,1000,1000)

painter.drawLine(0,0,250,250);

}

大家可以看现象,画线也变粗了。这是为啥呢?

 

<img src="https://pic3.zhimg.com/v2-f46c7384b0871a3f5cca74276b1feb3e_b.jpg" data-caption="" data-size="normal" data-rawwidth="500" data-rawheight="533" class="origin_image zh-lightbox-thumb" width="500" data-original="https://pic3.zhimg.com/v2-f46c7384b0871a3f5cca74276b1feb3e_r.jpg"/>

 

 

很简单,painter.setViewport(0, 0, 1000, 1000)这个代码相当于把整个物理坐标系翻倍了。也就是单位逻辑坐标变大了。

 

原来逻辑坐标画1个像素点,现在相当于画2个。

所以会有两个现象

画线变粗

drawLine现在只需要一半就可以撑满整个窗口。

小结:

逻辑坐标系->setWindow()->窗口坐标系->setViewport()->设备坐标系

还是要好好观察下这个顺序。

Qt通过三个坐标系,来做到了窗口之间的变换 & 放大缩小。

那么工程上是如何使用的呢。一般来说viewport这个跟当前物理DPI保持一致。

然后通过setWindow放大坐标系

 

如果你想把整个逻辑坐标系放大。那么就用setWindow()还是看你当前的逻辑需求- 即逻辑坐标系的需求。

以这个为出发点,那么再去理解setWindow & setViewport就很好理解了。

 

 

from:https://zhuanlan.zhihu.com/p/584048811