理解Qt中的setViewport和setWindow

发布时间 2023-09-04 15:47:13作者: imxiangzi

概念相关
Qt的2D变换中有 逻辑坐标(窗口) 和 物理坐标(视口)

划重点: 窗口(逻辑坐标)与视口(物理坐标)的关系:窗口的四个角会映射到视口的四个角,它们四个角之间一一对应,两者的比例是 1:1。
它们默认的状态下,逻辑坐标和物理坐标都是一一对应的,起始点都是(0,0),长度高度也一致即两者重合,对应上方两者关系的说明。

注意了,我们编程的时候用的都是逻辑坐标。
还有拥有逻辑坐标这个窗口,不是我们常说的windows窗口,逻辑坐标这个窗口是一个看不见的矩形。

绘图的时候,是在窗口上绘图的哦。

单独setWindow或setViewport会造成等比例缩小/放大的问题。

是不是找到什么感觉了,嘻嘻

这些都是我的个人理解,如果有大佬阅读觉得有错误的,请务必要指正哦~~

案例测试
直接做测试,我拿来做测试的widget大小为300*300。

当单独setWindow时
void QPainter::setWindow (int x, int y, int width, int height)
我用的setwindow函数是第二个重载,将(x,y)作为原点,并设置该窗口大小为(width*height)。
直接贴上代码:

void Widget::paintEvent(QPaintEvent *event)
{
QPainter painter(this);

//黑色为正常
painter.drawRect(10,10,100,100);

//绿色为setWindow
painter.setPen(Qt::green);

//将逻辑坐标的(10,10)作为逻辑坐标的原点
//(0,0) --> (10,10)
painter.setWindow(10,10,300,300);

painter.drawRect(10,10,100,100);

int Lx,Ly,Rx,Ry;
//输出窗口高度宽度等
qDebug() << "window width" << painter.window().width();
qDebug() << "window height" << painter.window().height();
Lx = painter.window().topLeft().x();
Ly = painter.window().topLeft().y();
Rx = painter.window().bottomRight().x();
Ry = painter.window().bottomRight().y();
qDebug() << QString("window TopLeft And BottomRight (%1,%2) , (%3,%4)" )
.arg(Lx).arg(Ly).arg(Rx).arg(Ry) << endl;

//输出视口高度宽度等
qDebug() << "viewport width" << painter.viewport().width();
qDebug() << "viewport height" << painter.viewport().height();
Lx = painter.viewport().topLeft().x();
Ly = painter.viewport().topLeft().y();
Rx = painter.viewport().bottomRight().x();
Ry = painter.viewport().bottomRight().y();
qDebug() << QString("viewport TopLeft And BottomRight (%1,%2) , (%3,%4)" )
.arg(Lx).arg(Ly).arg(Rx).arg(Ry);
qDebug() << "--------------------------";
}

void Widget::on_pushButton_clicked()
{
if(width() < 600 || height() < 600)
{
resize(width()+100,height()+100);
ui->label->setText(QString("当前大小为:%1 * %2").arg(width()).arg(height()));
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
运行效果图:


发现问题:

当改变窗体(widget)大小的时候绿色的矩形会正比例放大或缩小?
当放大的宽高不一致时绿色矩形原本的形状会变形?
放大窗体会使视口大小变大,窗口大小不变?
输出窗口和视口的height()和width()与输出的左上角、右下角计算出来的高度和宽度有1的误差?
发现规律:当我等比例放大窗体的时候,它们有以下规律。

窗口:300*300 --> 300*300
视口:300*300 --> 400*400 (放大widget相当于放大设备坐标)
矩形:(10,10)(100,100)-->(0,0)(130,130)
窗口的1约等于视口的‭1.3像素,将逻辑坐标的矩形放大1.3倍

视口:300*300 --> 500*500
矩形:(10,10)(100,100)-->(0,0)(165,165)
//计算器算的是‭1.666666666666667‬,不知道为啥取1.65
窗口的1约等于视口的‭1.65像素,将逻辑坐标的矩形放大1.65倍

视口:300*300 --> 600*600
矩形:(10,10)(100,100)-->(0,0)(200,200)
窗口的1约等于视口的2像素,将逻辑坐标的矩形放大2倍
1
2
3
4
5
6
7
8
9
10
11
12
13
1.当改变窗体(widget)大小的时候绿色的矩形会正比例放大或缩小。

答:当窗口和视图都没有被人为改变的时候,它们的比例默认都是1:1 。当我们修改了窗口后会根据窗口大小与视图大小进行计算和转换坐标,上面的规律也差不多能证明它们之间的关系了。
公式是: (矩形宽度 * (视口宽度/窗口宽度),矩形高度 * (视口高度/窗口高度)
这个公式只能计算右下角这个点的位置,而且精确度会有偏差,其实这个东西没个鸟用。
2.当放大的宽高不一致时绿色矩形原本的形状会变形。

答:解决这个问题就是让窗口和视口维持相同的宽高来防止变形。即让它们回到默认状态 1:1
//解决这个问题的方案,那个书里面也有:不变形矩形等比例缩放。

//qMin():比较参数1和参数2,将最小的值返回。
int side = qMin(width(),height());
int x = (width() - side()/2);
int y = (height() - side()/2);
painter.setViewport(x,y,side,side)
1
2
3
4
5
6
7
3.放大窗体会使视口大小变大,窗口大小不变。

答:
视口对应的是物理坐标,物理坐标也俗称设备坐标。设备就是大家都应该知道是什么吧,咱们的屏幕。
但是软件中的视口就是显示内容部分的窗口(widget),当我放大这个widget的时候显示区域变大了,视口那肯定也变大了呀。
窗口大小不变是因为我们在代码中已经固定好了这个窗口的大小,所以它就不会根据我们的窗体大小变化而变化了呀。
4.输出窗口和视口的height()和width()与输出的左上角、右下角计算出来的高度和宽度有1的误差。

答:参考3:QRect和QRectF
由于历史原因bottom()和right()函数返回的值偏离了矩形的真正的右下角:
right()函数返回left() + width() - 1, bottom()函数返回top() + height() - 1。
topRight()函数和bottomLeft()函数的x坐标和y坐标分别包含与真正的右下角和右下角相同的偏差。
单独setViewport(),正好与setWindow()相反
void QPainter::setViewport(int x, int y, int width, int height)
将视口的原点布置在(x,y)处,视口大小改为width*height。
void Widget::paintEvent(QPaintEvent *event)
{

QPainter painter(this);

//左上角
//painter.setWindow(0,0,100,100);
//将视口的原点设置为(10,10),大小为300*300
painter.setViewport(10,10,300,300);
painter.drawRect(0,0,100,100);
painter.setPen(Qt::red);
painter.drawRect(10,10,100,100);

int Lx,Ly,Rx,Ry;

qDebug() << "window width" << painter.window().width();
qDebug() << "window height" << painter.window().height();
Lx = painter.window().topLeft().x();
Ly = painter.window().topLeft().y();
Rx = painter.window().bottomRight().x();
Ry = painter.window().bottomRight().y();
qDebug() << QString("window TopLeft And BottomRight (%1,%2) , (%3,%4)" )
.arg(Lx).arg(Ly).arg(Rx).arg(Ry) << endl;

qDebug() << "viewport width" << painter.viewport().width();
qDebug() << "viewport height" << painter.viewport().height();
Lx = painter.viewport().topLeft().x();
Ly = painter.viewport().topLeft().y();
Rx = painter.viewport().bottomRight().x();
Ry = painter.viewport().bottomRight().y();
qDebug() << QString("viewport TopLeft And BottomRight (%1,%2) , (%3,%4)" )
.arg(Lx).arg(Ly).arg(Rx).arg(Ry);
qDebug() << "--------------------------";
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
当我们单独使用setViewPort()的时候视口的大小已经被我们给固定住了,当放大窗体的时候窗口将会变大,故而引起两者宽高不一致,导致矩形缩小,成反比例。

通过两个实例得出以下规律:

单独使用setWindow()宽高给常数后会固定窗口的大小,放大窗体的时候只能放大视口,放大缩小成正比。单独使用setViewPort()反之。
同时使用setViewport和setWindow
同时使用setViewport和setWindow才是正确的操作,这两者配合才能打出一套配合拳。

提出问题:

先设置了setWindow()改变了窗口,那逻辑坐标也会改变,那么我们接下来使用逻辑坐标setViewport()会不会受到影响?
矩形超出了窗口和视口的范围,那超出的还会显示出来吗?
同时设置之后,放大或缩小窗体还会有矩形放大或缩小么?
1. 先设置了setWindow()改变了窗口,那逻辑坐标也会改变,那么我们接下来使用逻辑坐标setViewport()会不会受到影响?

直接贴代码:

void Widget::paintEvent(QPaintEvent *event)
{
QPainter painter(this);

painter.setWindow(-10,-10,100,100);
painter.setViewport(0,0,100,100);
painter.drawRect(0,0,90,90);
}
1
2
3
4
5
6
7
8
效果图:


答:很明显,是有影响的。
2. 矩形超出了窗口和视口的范围,那超出的还会显示出来吗?

将绘制矩形的代码修改如下:

painter.drawRect(-10,-10,110,110);
1
答:当你运行之后会发现,还是会显示出来的,它们并不能抑制显示,这也是一个坑坑坑。
3. 同时设置之后,放大或缩小窗体还会有矩形放大或缩小么?

painter.setWindow(-10,-10,100,100);
painter.setViewport(0,0,100,100);
painter.drawRect(-10,-10,110,110);
1
2
3
答:
上面的代码都是用了常量,而且窗口和视口的大小一致,所以并不会产生放大或缩小。
窗口和视口的映射关系知道了吧,为什么单独设置setViewport或setWindow会产生放大缩小比例的问题,这里我还要提一个点,就是因为窗口和视口的宽高不一致,比例也会不一致,才会导致那样的问题出现。
但是我们有时候并不希望它们一直都是这么死板,矩形随窗体大小变化而等比例变化,这时参数中就不能使用常量了。解决方法已经在单独设置setWindow第二问里面了哦。
本文有参考1:三步理解Qt中的setViewport和setWindow

本文由参考2:QT 坐标系统理解


————————————————
版权声明:本文为CSDN博主「焕小谢」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_38832450/article/details/103478571

 

 

 

 

from:https://blog.csdn.net/qq_38832450/article/details/103478571