看到Graphics View Framework的时候,被窗口,视口,场景坐标系,对象坐标系,世界坐标系,逻辑坐标,物理坐标等等概念彻底搞懵了。到底他们之间是什么关系呢?是怎样映射的呢?到现在小狼还没有搞懂,不过经过不断试验,有了一点点自己的理解。
QPainter的各种draw方法是基于窗口坐标系的。
窗口坐标为逻辑坐标,是基于视口坐标系的;视口坐标为物理坐标,是基于绘图设备坐标系的。没有做过改动的情况下,他们是一样的,都是以绘图设备(paint device,qwidget,qpixmap等都为绘图设备)大小为大小,左上角为原点(0,0)。
窗口:
窗口代表视口的区域,他始终以视口坐标为最终目标进行映射(这句话的意思到下面讲视口的时候会再讲),他的大小和逻辑位置可以通过QPainter::setWindow()
设置,但是无论大小和逻辑位置设置为什么数值,他始终代表着整个视口。
例如你有一个实际大小为200×200像素的窗口,那么原始状态之下窗口大小也是200×200,视口大小也是200×200,,在0,0位置画一个大小为100×100的矩形的时候,他会占视口左上角的4分之一。painter.drawRect(0,0,100,100);
如果这时候我们通过QPainter::setWindow修改了窗口位置和大小,例如setWindow(-50,-50,100,100)
函数原型:
void QPainter::setWindow(int x, int y, int width, int height)
参数:
x:窗口左上角x坐标
y:窗口左上角y坐标
width:窗口长度
height:窗口高度
窗口代表的还是整个视口,但是映射的数值有所不同,这时候窗口的逻辑坐标(-50,-50)成为了视口坐标的(0,0),而窗口的逻辑大小成为了100×100的单位长度(这里用单位长度是因为窗口大小的长度并不固定,受视口大小影响),因为用100个单位长度代表原本物理大小的200像素,所以,每一个单位长度就是实际的2像素。因为QPainter是以窗口坐标为基础的,所以这时候画一个位置为(-50,-50),大小为 50,50的矩形。painter.drawRect(-50,-50,50,50);
矩形还是占窗口的左上角的4分之一(下图左),而painter.drawRect(0,0,50,50);
矩形占窗口右下角的4分之一。
而视口对应的则是物理坐标,没有改动的情况下,视口大小与绘图区大小一样,上面的例子中,视口的属性一直没有改变过,所以视口的左上角还是在绘图区的物理位置(0,0),在窗口坐标的(-50,-50)。大小为物理大小的
200×200像素,而为窗口坐标系下的100×100单位长度。
视口:
现在我们看看设置视口对绘图的影响,为了简单起见,先把上面的setWindow()
一句注释掉,即现在窗口,视口是一样的。
现在来改变视口的属性,先用painter.setViewPort(0,0,100,100);
函数原型:void QPainter::setViewport ( int x, int y, int width, int height )
参数:
x:设置视口左上角x坐标
y::设置视口左上角y坐标
width:设置视口长度
height:设置视口宽度
所以上面语句的作业就是把视口的的原点位置设置为绘图设备(这里是QDialog)的原点,大小改变为100,100。
那么现在是个什么情况呢?
现在我们把视口的坐标设置为绘图区的左上角(0,0)位置,大小设置为绘图区的一半,因为绘图区是(200×200),而我们把视口设置为(100×100)。即现在实际的绘图区为绘图设备的左上角的4分之一。
那么这时候我们再用painter.drawRect(0,0,100,100);
画一个矩形,实际显示是怎么样的呢?看下图:
绘制出来的是dialog的16分之一了,为什么会这样呢?
前面我们讲过窗口坐标始终以视口坐标为最终目标进行映射,而原来没有经过修改的窗口的属性为以左上角为原点,大小为200×200单位长度,我们修改视口大小为100×100像素后,窗口的200单位长度就映射到100像素的视口长度上,即每一单位长度为0.5像素,所以绘制出来的结果就是100×0.5=50像素,所以长和高都是dialog的4分之一,面积就是16分之一了。
这时候如果我们拖动dialog边界改变dialog的大小会怎么样呢?小熊原本的想法是画出来的矩形应该还是占总大小的16分之一,实际上这是错的。
void Lang::paintEvent(QPaintEvent *)
{
QPainter painter(this);
qDebug()<<"after drag:"<<endl;
qDebug()<<painter.viewport().width();
qDebug()<<painter.viewport().height();
painter.setViewport(0, 0, 100 ,100 );
//painter.setWindow(-50,-50,100,100);
painter.drawRect(0,0, 100, 100);
}
先通过painter.viewport().width()和heigth()获取当前实际视口大小(paintEvent之前,视口会被重置为绘图设备实际大小)。如下图,当我们把dialog拖动为400×400大小时,矩形框变得更小了。
其实这很简单。因为在paintEvent之前窗口值也会重置为dialog(绘图设备)大小,所以这时候窗口大小为400×400单位长度,而视口我们还是再设定为100×100像素,所以这时候窗口大小的一单位长度为实际的100/400=0.25像素,所以画一个100×100单位长度的矩形时,实际大小时25×25像素,所以变得更小。
上图中我是用qq的截图功能进行测量,qq截图会给出当前截取图形的大小,正是25×25(圈的时候有所偏差).
因此最好的办法就是让视口和窗口维持相同的宽高来防止变形。
int side = qMin(width(),height()); int x = (width() - side / 2); int y = (height() - side / 2); painter.setViewport(x,y,side,side);