Qt窗口和视口解析
坐标变换流程
QPainter.drawRect(QRectF)绘制图形传入的是世界坐标,而后经过变换矩形变为窗口坐标,最后经过窗口-视口变换变为设备坐标。其中世界坐标系和窗口坐标系都属于逻辑坐标系,设备坐标系属于物理坐标。世界坐标、窗口坐标和设备坐标
-
世界坐标
世界坐标也叫逻辑坐标,使用的单位叫做逻辑单位,在世界坐标系中的大小与显示设备大小无关,类似于数学中的单位长度。 -
窗口坐标
窗口坐标与世界坐标差在坐标变换上,如translate、rotate、scale、shear等。 -
设备坐标
设备坐标是物理坐标,设备的左上角为原点(QGraphicsView同),向右为x正方向,向下为y轴正方向。物理坐标系的第二象限为有效绘图区域,没有负坐标轴。显示器中,坐标单位通常为像素,打印机中,坐标单位通常为点。
综上,三个坐标系中,世界坐标系和窗口坐标系是逻辑坐标系,与具体的设备大小没有关系,两者之间相差的是坐标变换,而设备坐标系也就是物理坐标系,第二象限是绘图的有效果区域,没有负坐标轴。
窗口和视口
-
窗口
窗口是窗口坐标系中的矩形框,窗口存在的意义是为了确定显示窗口坐标系中的哪部分区域。
使用painter.setWindow(x,y,w,h)
即可设置窗口的大小以及坐标系原点在窗口中的位置。默认情况下窗口与视口大小相同,并且原点都位于左上角。 -
视口
视口是设备环境中的一个矩形框,使用物理坐标,和设备大小密切相关,超出设备外的视口区域不予显示。视口存在的意义是为了将显示的内容显示在物理坐标系中的哪个矩形区域。
使用painter.setViewPort(x,y,w,h)
即可设置视口的大小以及视口在物理坐标系中的位置。
- 关于绘图设备
绘图设备常常有QWidget、QPixmap、QImage等。(另外感觉QGraphicsItem也可以作为绘图设备,使用paint函数绘图。)- 关于QGraphicsView和QGraphicsScene
使用QWidget::paintEvent只能绘制简单的图形,Qt提供了可与用户交互的QGraphics绘图体系,图形项可移动、选中。view类似于绘图设备,但是view不能设置视口,scene的坐标系类似于逻辑坐标系,可以通过view.setSceneRect(QRectF)来选定scene中的部分区域显示在view中。
世界变换和窗口视口变换
-
世界变换
世界变换的目的是直接对逻辑坐标进行矩阵变换,常见的有translate、scale、rotate、shear。 -
窗口视口变换
用户输入的坐标一定是逻辑坐标,最终用来绘制的一定是经过窗口-视口变换后的视口坐标,也就是设备坐标(物理坐标)。
如果不显示使用setViewPort
和setWindow
指定视口和窗口大小,默认的窗口和视口坐标原点都是(0,0),窗口和视口大小都是设备的长和宽。若设置了一个,那么未设置的仍然使用默认设置。
窗口和视口变换关系实际上就是一对线性函数:
QWidget、QGraphicsItem、QGraphicsView绘图
在QWidget::paintEvet()中绘图,QWidget是绘图设备,左上角为物理坐标系的坐标原点。通过设置窗口setWindow()和设置视口setViewPort()来分别设置逻辑坐标系的绘制区域和物理坐标系的显示区域,超出物理坐标系的内容不予显示。在逻辑坐标系中通过坐标系变换实现绘制。
在QGraphicsItem::paint()中可以绘制,Item的左上角默认是逻辑坐标系的原点,可通过painter的坐标系变换(如translate、scale、shear、rotate)实现与QWidget体系中相同的绘制。与QWidget不同的是,超出Item仍然能够显示。
QGraphicsView间接继承于QWidget,因此可作为控件摆放在QWidget中,直接继承于QAbstractScrollArea,因此作为显示的窗口时,可以出现滚动条。View与QGraphicsScene搭配使用,Scene用于存放QGraphicsItem,默认逻辑坐标系的原点位于其中心,Item通过item::setPos()设置其在(场景)逻辑坐标系中的位置,通过item::setRect()设置其在自身局部坐标系中的位置,因此Scene场景的不同位置皆可放置Item。而View类似于视口,用于显示场景中的内容,通过view::setSceneRect()可设置显示哪部分场景区域,类似于设置窗口。但是View并不能设置显示在哪部分区域。
窗口与视口绘图测试
painter.save();
painter.setPen(Qt::red);
painter.drawRect(0,0, 200, 200);//可视化视口(红色)
painter.setViewport(0,0, 200, 200);
QPen pen = painter.pen();
pen.setStyle(Qt::DashLine);
pen.setWidth(3);
pen.setColor(Qt::blue);
painter.setPen(pen);
painter.setWindow(-100, -100, 200, 200);
painter.drawRect(-100, -100, 200, 200);//可视化窗口(蓝色)
painter.translate(50, 50);//平移(50,50)
painter.setPen(Qt::black);//绘制两个图形
painter.drawRect(-100, -100, 100, 100);
painter.drawRect(50, 50, 100, 100);
painter.restore();
总结:
- 视口
setViewport(QRectF)
传入的是QWidget的物理坐标,窗口setWindow(QRectF)
传入的是逻辑坐标。视口和窗口是线性映射关系,因此蓝框和红色框会重合。 - 平移translate(50,50)操作,移动的是逻辑坐标系,原来的坐标系原点在蓝色窗口中央,平移后位于图示的红色坐标系。
- save和restore用于保存和恢复QPainter的变换。