yafeilinux 发表于 2017-5-7 19:03:56

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

开始前创建一个Widget窗口应用(其默认宽400像素,高300像素,左上角为原点)。
首先正常绘制一个正方形:
void Widget::paintEvent(QPaintEvent *)
{
    QPainter p(this);
    p.setPen(Qt::blue);
    p.drawRect(0, 0, 100, 100);
}

效果如下图:

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

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

    p.setWindow(-50, -50, 100, 100);
    p.setPen(Qt::red);
    p.drawRect(0, 0, 100, 100);
}
这时,效果如下图所示。


现在来说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,也就是我们看到的这个红色矩形。

再来修改代码:

void Widget::paintEvent(QPaintEvent *)

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

    p.setWindow(-50, -50, 100, 100);
    p.setPen(Qt::red);
    p.drawRect(0, 0, 20, 20);
}

运行效果如下图所示:



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

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

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

    p.setPen(Qt::red);
    p.drawRect(0, 0, 20, 20);
}


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




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



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





xsqxsy 发表于 2017-5-7 21:01:41

先占个位置。看完回复。

Since 发表于 2017-5-8 09:22:18

眼~~~~~~~~~~~~~~~~~~~~~~~~~~~~

lovepig200 发表于 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会完成坐标转换,绘制的时候直接进行线性变换。
页: [1]
查看完整版本: Qt 2D绘图部分窗口、视口的研究