天天看点

Qt坐标系统之窗口/视口变换原理

Qt坐标系统之窗口/视口变换原理

本文为原创文章,转载请注明出处,或注明转载自“黄邦勇帅(原名:黄勇)

本文出自本人原创著作《Qt5.10 GUI完全参考手册》网盘地址:

https://pan.baidu.com/s/1iqagt4SEC8PUYx6t3ku39Q

《C++语法详解》网盘地址:https://pan.baidu.com/s/1dIxLMN5b91zpJN2sZv1MNg

若对C++语法不熟悉,建议参阅本人所著《C++语法详解》一书,电子工业出版社出版,该书语法示例短小精悍,对查阅C++知识点相当方便,并对语法原理进行了透彻、深入详细的讲解,可确保读者彻底弄懂C++的原理,彻底解惑C++,使其知其然更知其所以然。此书是一本全面了解C++不可多得的案头必备图书。

需要用到的QPainter类中的函数如下

Qt坐标系统之窗口/视口变换原理

1、理解“逻辑”

“逻辑”一词具有“理论上的”意思,说简单一点,就是“假想的”

2、从图形绘制的方向理解窗口和视口(原理见图12-60)

Qt坐标系统之窗口/视口变换原理

窗口就是一个逻辑上的(假想的)矩形,图形在这个假想的矩形上绘制,绘制完之后再把图形映射到视口上,视口通常是与需要显示的设备相关联的(即绘制设备,比如QWidget部件,显示屏等),然后图形才能显示出来。

3、从图形显示方向理解窗口和视口(见图12-61)

Qt坐标系统之窗口/视口变换原理

假设需要显示图12-61中阴影部分的图形,则,首先把图形映射到窗口,此时可对图形进行旋转、缩放、平移等操作,然后把超出窗口之外的图形裁剪掉(这就是为什么窗口全称为裁剪窗口的原因),然后把图形映射到视口显示出来,若把视口理解为我们的眼睛,则视口和窗口的概念就很容易理解了。窗口的所有内容只有完全映射到了视口的范围内,才能被完全显示,一个窗口的内容可以同时映射到多个视口,即可以从不同的视口去观察窗口中的内容,在窗口到视口的映射过程中,还可对图形进行缩放、平移等简单变换。

注意:此处讲解的只是一种理论模型,实际实现时是否会裁剪图形,以及坐标变换是在窗口进行还是在视口进行,则视实际使用的绘图软件而不同。

4、窗口/视口主要作用

绘制设备有以像素为单位的(比如显示器),也有以点为单位的(比如打印机),使用窗口/视口机制,可使绘图与绘制设备相互独立,我们只需在窗口绘图,而不必关心底层绘制设备到底是什么(即,不需关心底层设备究竟使用的什么单位等)。因为图形在窗口中绘制,因此窗口是来自真实世界的。

5、窗口/视口变换公式的推导

因为计算机中图形的绘制和显示通常都是位于矩形区域之内的,因此窗口/视口变换就是矩形到矩形的变换。

窗口/视口变换原理(见图12-62):在窗口中被映射的点(x,y)与原点所围成的矩形区域,与被映射后在视口中的点(x’,y’)与原点所围成的矩形区域,其所对应的长度之比,应与窗口和视口所形成的矩形的长度之比相等,对于矩形区域的宽度,其原理相同。

Qt坐标系统之窗口/视口变换原理

为便于讲解引入表12-20所示变量及概念(意义见图12-62)

Qt坐标系统之窗口/视口变换原理

根据变换原理,变换后的点(x’,y’)可由如下公式计算得出(参见图12-62):

Qt坐标系统之窗口/视口变换原理

6、理解窗口/视口变换公式

视口中的设备就是指的实际的绘制设备,设备坐标和设备点是不会变的,其位置通常被设置为绘制设备的左上角,方向是x轴从左向右,y轴从上到下增长。在视口中的坐标值x’,y’,Vox,Voy都是相对于设备坐标而言的,因此使用公式计算出来的坐标值x’,y’是相对于设备坐标而言的,因为窗口通常来自于现实世界,因此窗口中的坐标值是相对于世界坐标而言的。

映射公式使用的数值是没有单位的,不存在某个数表示多少毫米,多少像素这一说法,计算时只需把相应的数值代入公式就能计算出正确的结果。

7、窗口/视口变换可实现的坐标变换

注意:以下的窗口和视口范围使用的数值并不是真实的数值,因为在坐标转换公式中,使用的是缩放系数(即他们的比值),因此使用真实数值并没有多大意义。

Qt坐标系统之窗口/视口变换原理

8、窗口/视口数据的单位(逻辑单位与设备单位)

窗口和视口的数据在数学上来讲是没有什么实际意义的,数据就是一个数值,但在现实世界中绘制图形时,需要知道图形在绘图区域的具体位置,因此需要知道给定的数据具体表示多少个单位的距离,这样就需要给数据指定一个单位。比如,视口中的数据以“像素”为单位,窗口中的数据以“毫米”为单位等。

视口数据的单位:来自视口的数据,一般都以实际的物理单位为单位,比如若视口为显示屏,若显示屏以像素为单位,则数据100表示位于显示屏100个像素处的实际位置,若显示屏以毫米为单位,则数据100表示位于显示屏100毫米处的实际位置。若视口中数据的意义未确定,则使用“设备单位”表示视口中数据的单位,比如100,表示100设备单位。

窗口数据的单位:窗口的数据是实际绘制时希望使用的数据,因实际绘制时可能会使用多种不同的单位,因此在未明确窗口中数据的单位时,使用“逻辑单位”表示窗口数据的单位。比如若想以毫米为单位绘制图形,则1逻辑单位就是1毫米,若想以0.1毫米的精度绘制图形,则1逻辑单位就是0.1毫米。

由以上可知,若想让绘制设备以实际窗口绘制时使用的逻辑单位为单位来显示图形,则在设备单位和逻辑单位之间需要一个单位转换,而这种转换可以使用窗口/视口变换来实现(详见下文)。

数据的来源:由公式可知,x’,y’,Vex,Vey,Vox,Voy是来自视口的数据,因此这些数据是以设备单位为单位的,x,y,Wex,Wey,Wox,Woy是来自窗口的数据,这些数据是以逻辑单位为单位的。

9、视口/窗口范围比Vex/Wex(缩放系数S)与单位转换

对于缩放系数Vey/Wey=K的原理是相同的,仅以S为例讲解。

实际绘制图形时,我们只需指定绘制时的单位,而不需关心物理设备究竟使用什么单位,比如想绘制100毫米长的直线,只需在绘制时直接输入100就能在物理设备上显示100毫米这么长的直线,而不是显示100像素或100点大小这么长的直线,这就是单位转换的问题,使用窗口/视口变换就能实现单位转换。

虽然使用窗口/视口变换也可实现一些简单的坐标变换,但通常使用窗口/视口进行单位转换以实现在窗口中绘制的图形与现实世界相同,当然单位转换也可使用QTransform变换矩阵来完成。

视口/窗口范围比在数据无任何单位时,就仅仅表示缩放系数,即Vex/Wex = S。

视口/窗口范围比S的意义一般根据视口所表示的实际意义而定,下面以窗口的数据为未确定的逻辑单位,然后再依视口数据的单位不同,而分别对S的单位进行推导

Qt坐标系统之窗口/视口变换原理

10、单位转换示例

例1:假设想以1逻辑单位表示0.1毫米,屏幕的分辨率为1024像素,屏幕实际长度为376mm,若客户区的长度为500像素,试确定窗口范围的尺寸,注意:屏幕是物理设备,是以像素为单位来绘制图形的。

解:主要应注意单位间的转换,本示例只计算了X方向的情形,Y方向的情形未予计算。本示例的实际应用示例见示例12.24

方法一:把单位转换为像素

Qt坐标系统之窗口/视口变换原理

方法二:把单位转换为毫米

Qt坐标系统之窗口/视口变换原理
示例12.24:使用窗口/视口变换实现以0.1毫米为单位在显示屏上绘制图形(结果见图12-63),本示例的显示屏尺寸及计算方法见例1的讲解
void paintEvent(QPaintEvent *e){
    	QPainter pr(this);
    	QBrush bs(QColor(255,255,1));
//绘制一个与视口一样大小的矩形,以便于观察
    	pr.drawRect(100,100,500,500);  
    	QPen pn;
    	pn.setColor(QColor(111,1,1));
    	pn.setWidth(10);
    	pn.setCapStyle(Qt::FlatCap);
    	pr.setPen(pn);
    	pr.setViewport(100,100,500,500);	//设置视口
    	pr.setWindow(0,0,1836,1836);	//设置窗口
//注意:在设置窗口/视口之后,使用drawXXX绘制图形时使用的坐标,都需要使用公式26进行计算。
    	pr.drawLine(0,100,200,100);	}   //绘制一条长200*0.1mm(即20mm)长的直线
           
Qt坐标系统之窗口/视口变换原理

11、使用窗口/视口变换移动绘制设备的坐标

由以上讲解可知,通过计算窗口/视口变换可实现绘制图形时以毫米、厘米等现实世界的单位为单位,下面再讲解一个使用窗口/视口变换实现移动绘制设备坐标的方法

绘制设备的坐标默认位于左上角,但可通过窗口/视口变换在逻辑上来移动该坐标系到我们想要的位置。具体方法如下:

使S = K = 1,即Vex = Wex; Vey =Wey; 也就是窗口和视口具有相同的宽度和长度,这意味着窗口和视口拥有相同的长度单位。此时,公式26变为

x’=x+Vox-Wox; y’=y+Voy-Woy;

以上公式是一个坐标平移公式,该公式不但是一个平移点的公式,也是平移坐标系的公式。因此该公式相当于把坐标系向x方向平移了Vox - Wox的距离,向y方向平移了Voy - Woy的距离。在之后绘制图形时可以以该坐标系为参考进行绘制。若绘图软件不裁剪图形,则可简单的把窗口/视口范围设置为1,即Vex = Wex = Vey = Wey =1。详见示例12.25

示例12.25:移动绘制设备的坐标(结果见图12-64)
void paintEvent(QPaintEvent *e){
    	QPainter pr(this);
QBrush bs(QColor(255,255,1));
//以下绘制的交叉直线用于标示平移后的
//坐标位置,以方便观察
    	pr.drawLine(0,100,555,100);    
pr.drawLine(100,0,100,555);
//设置画笔
    	QPen pn;    pn.setColor(QColor(111,1,1));
   	pn.setWidth(10);pn.setCapStyle(Qt::FlatCap);
    	pr.setPen(pn);
//设置窗口/视口,以下代码相当于把设备坐标移至(100,100)处,本示例也可把窗口/视口范围设置为1。
    		pr.setViewport(50,50,200,200);   pr.setWindow(-50,-50,200,200);
    	pr.drawLine(0,100,100,100);		pr.drawRect(-50,-50,100,100);}
           
Qt坐标系统之窗口/视口变换原理

本文作者:黄邦勇帅(原名:黄勇)

Qt坐标系统之窗口/视口变换原理

继续阅读