參考:
- https://zhuanlan.zhihu.com/p/584048811
- https://www.zhihu.com/tardis/zm/art/634951149?source_id=1005
小談Qt的坐標系系統
Qt中有三個坐標系
- 設備坐標系
- 窗口坐標系
- 邏輯坐標系
設備坐標系: 即Device坐標系。也是物理坐標系。即真實的的物理坐標系。
邏輯坐標系: 即用戶坐標系。也就是說日常大家使用Qt的時候的坐標系。
窗口坐標系: 這個坐標系是QPainter
設置setWindow
以后的一個坐標系。
這三個坐標系,基本上就代表了常規的繪制引擎的三個坐標系。
那么三個坐標系,就代表可以用兩次變換來實現了。
這個就是QPainter
中的setWindow() & setViewport()
的兩個概念了
直接看下圖
via https://doc.qt.io/qt-6/coordsys.html#window-viewport-conversion
OK,既然有了這些概念,那么我們就開始講一下,坐標系的轉換流程。
廢話不多說
還是按照我的方式 & Qt的官方文檔來講吧
我自己的例子
畫個圖,描述下這三個坐標系
平時我們的三個坐標系都是相同的,都是水平方向是X軸,垂直方向是Y軸,坐標原點是左上角(0, 0)。
X軸從左到右增加,Y軸從上到下增加
即如圖所示
那么三個坐標系的變化流程就是
還是先上個Qt的Demo
Qt的Demo
設定,屏幕的DPI是96
三個坐標系重疊
先創建一個QWidget,然后設置其大小是(500, 500)
resize(500,500);
然后再重寫他的PaintEvent事件
void QtWidgetsApplication1::paintEvent(QPaintEvent*e)
{
QPainter painter(this);
painter.drawLine(0,0,500,500);
}
那么表現
這里可以先重點說下
我們以普通用戶(程序員)的角度來理解這個代碼。因為有三個坐標系,所以Qt的代碼全都是以邏輯坐標系為基礎的。對于這個代碼來講
painter.drawLine(0,0,500,500);
drawLine的時候實際上就是以邏輯坐標系為準。這時候,三個坐標系是相等的,沒有任何比例變化。大家可以理解為這三個坐標系是重疊的。
setWindow()
控制變量,其他的代碼不變,我們只單獨設置setWindow這個代碼setWindow的作用,就是在物理坐標系不變的情況下,將邏輯坐標系進行縮放。
假定,我們的代碼是這樣的。將邏輯坐標擴大一倍,那么代碼就是這樣的
void QtWidgetsApplication1::paintEvent(QPaintEvent*e)
{
QPainter painter(this);
painter.setWindow(0,0,1000,1000);
painter.drawLine(0,0,500,500);
}
這時候我們的窗口就變成了這個樣子。
也就是說,我的物理大小,并沒有變,我通過更改邏輯坐標,(放大兩倍的效果) 看起來將我的整個坐標系變大了。比如原來我可以通過
painter.drawLine(0,0,500,500);
來繪制對角。
經過
painter.setWindow(0,0,1000,1000)\\放大邏輯坐標系兩倍
最后
painter.drawLine(0,0,500,500);\\只能繪制一半的效果。
如果還想繪制對角,那么就得
painter.drawLine(0,0,1000,1000);
也就是,我們現在將用戶的坐標系放大了兩倍。這個就是setWindow的作用。
setViewport()
大家可以理解setViewport
的作用就是控制物理坐標系的。
比如官方代碼
int side=qMin(width(),height());
int x=(width()-side/2);
int y=(height()-side/2);painter.setViewport(x,y,side,side);
還要控制窗口的寬高,這時候就更難理解了。我的例子還是控制變量,假定Window不變。我們只更改ViewPort
還是那個例子,窗口大小(500, 500)。我們還是畫對角線。這時候我們把代碼改成
void QtWidgetsApplication1::paintEvent(QPaintEvent*e)
{
QPainter painter(this);
painter.setViewport(0,0,1000,1000)
painter.drawLine(0,0,250,250);
}
大家可以看現象,畫線也變粗了。這是為啥呢?
很簡單,
painter.setViewport(0, 0, 1000, 1000)
這個代碼相當于把整個物理坐標系翻倍了。也就是單位邏輯坐標變大了。
原來邏輯坐標畫1個像素點,現在相當于畫2個。
所以會有兩個現象
- 畫線變粗
- drawLine現在只需要一半就可以撐滿整個窗口。
小結:
還是要好好觀察下這個順序。
Qt通過三個坐標系,來做到了窗口之間的變換 & 放大縮小。
那么工程上是如何使用的呢。一般來說viewport
這個跟當前物理DPI保持一致。
然后通過setWindow放大坐標系
如果你想把整個邏輯坐標系放大。那么就用setWindow()
還是看你當前的邏輯需求- 即邏輯坐標系的需求。
以這個為出發點,那么再去理解setWindow & setViewport
就很好理解了。