Qt的坐标系统是由QPainter类控制的,而QPainter是在绘图设备上绘制的。绘图设备类QPaintDevice是所有可以绘制的对象的基类,它的子类主要有QWidget、QPixmap、QPicture、QImage和QPrinter等。
一个绘图设备的默认坐标系统中原点(0, 0)在其左上角,x坐标向右增长,y坐标向下增长。在基于像素的设备上,默认的单位是一个像素,而在打印机上默认的单位是一个点(1/72英寸)。
QPainter的逻辑坐标与绘图设备的物理坐标之间的映射由QPainter的变换矩阵、视口和窗口处理。逻辑坐标和物理坐标默认是一致的。
抗锯齿渲染
参考:https://blog.csdn.net/qq_40732350/article/details/86994305
坐标系统基本转换
QPainter的逻辑坐标与绘图设备的物理坐标之间的映射由QPainter 的变换矩阵、视口和窗口处理。逻辑坐标和物理坐标默认是一致的。
默认情况下,QPainter在关联的设备自己的坐标系上运行,但它也完全支持坐标转换。您可以使用QPainter::scale()函数按给定的偏移量缩放坐标系,使用QPainter::rotate()函数顺时针旋转它,使用QPainter::translate()对坐标系进行平移。
还可以使用QPainter::shear()函数在原点周围扭转坐标系。所有转换操作都在QPainter的转换矩阵上运行,您可以使用QPainter::worldTransform()函数进行检索。矩阵将平面中的点转换为另一点。
如果你反复需要相同的转换,你也可以使用Qtransform类对象和QPainter::worldTransform和QPainter::setWorldTransform()函数。您可以随时通过调用QPainter::save()函数来保存QPainter的变换矩阵,该函数将矩阵保存在内部堆栈中。使用QPainter::restore()函数弹出回来。
坐标系统”窗口-视口”转换
在使用QPainter进行绘制时,会使用逻辑坐标进行绘制,然后再转换为绘图设备的物理坐标。逻辑坐标到物理坐标的映射由QPainter的worldTransform()函数和QPainter的viewport()以及window()函数进行处理。其中视口(viewport)表示物理坐标下指定的一个任意矩形,而窗口(window,与以前讲的窗口部件的概念不同)表示逻辑坐标下的相同的矩形。默认的,逻辑坐标和物理坐标是重合的,它们都相当于绘图设备上的矩形。
使用窗口—视口转换可以使逻辑坐标系统适合应用的要求,这个机制也可以用来让绘图代码独立于绘图设备。
例如可以使用下面的代码来使逻辑坐标以(-50, -50)为原点,宽为100,高为100,(0, 0)点为中心:
QPainter painter(this);
painter.setWindow(QRect(-50, -50, 100, 100));
现在逻辑坐标的(-50, -50)对应绘图设备的物理坐标的(0, 0)点。这样就可以独立于绘图设备,使绘图代码在指定的逻辑坐标上进行操作了。当设置窗口或者视口矩形时,实际上是执行了坐标的一个线性变换,窗口的四个角会映射到视口对应的四个角,反之亦然。出于这个原因,一个很好的办法是让视口和窗口维持相同的宽高比来防止变形:
int side = qMin(width(), height());
int x = (width() - side / 2);
int y = (height() - side / 2);
painter.setViewport(x, y, side, side);
实例:
void Widget::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
// 填充界面背景为白色
painter.fillRect(rect(), Qt::white);
painter.setPen(QPen(Qt::red, 11));
// 绘制一条线段
painter.drawLine(QPoint(5, 6), QPoint(100, 99));
// 将坐标系统进行平移,使(200, 150)点作为原点
painter.translate(200, 150);
// 开启抗锯齿
painter.setRenderHint(QPainter::Antialiasing);
// 重新绘制相同的线段
painter.drawLine(QPoint(5, 6), QPoint(100, 99));
}
运行结果:
实例:
void Widget::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
painter.setWindow(-50, -50, 100, 100);
painter.setBrush(Qt::green);
painter.drawRect(0, 0, 20, 20);
}
这里先使用setWindow()函数将逻辑坐标矩形设置为以(-50, -50)为起点,宽100,高100。这样逻辑坐标的(-50, -50)点就会对应物理坐标的(0, 0)点,因为这里是在this即Widget部件上进行绘图,所以Widget就是绘图设备,也就是说,现在逻辑坐标的(-50, -50)点对应界面上的左上角的(0, 0)点。而且,因为逻辑坐标矩形宽为100,高为100,所以界面的宽度和高度都会被100等分。
为什么绘制的矩形不是我们想要的效果呢?
更改逻辑坐标或者物理坐标的矩形,就是进行坐标的一个线性变换,逻辑坐标矩形的四个角会映射到对应物理坐标矩形的四个角。而现在Widget部件的大小为宽400,高300,所以物理坐标对应的矩形就是(0, 0, 400, 300)。这样按比例对应,就是在水平方向,逻辑坐标的1个单位对应物理坐标的4个单位;在垂直方向,逻辑坐标的1个单位,对应物理坐标的3个单位。所以逻辑坐标中的宽20高20的矩形在物理坐标中就是宽80高60的矩形。