qt坐標系統見解


窗口坐標為邏輯坐標,是基於視口坐標系的。

視口坐標為物理坐標,是基於繪圖設備坐標系的

窗口坐標始終以視口坐標為最終目標進行映射: 

QPainter::setWindow 修改了窗口位置和大小(左上角重新定義了一個數值和長度)

QPainter::setViewport 修改了視口位置和像素個數(左上角移動到相應位置和像素個數)

 

------------------------------------------------------------------------------------------------------------------------------------------------

 

看到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);
1

如果這時候我們通過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分之一。
2

而視口對應的則是物理坐標,沒有改動的情況下,視口大小與繪圖區大小一樣,上面的例子中,視口的屬性一直沒有改變過,所以視口的左上角還是在繪圖區的物理位置(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);畫一個矩形,實際顯示是怎么樣的呢?看下圖:
3

繪制出來的是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大小時,矩形框變得更小了。
4

其實這很簡單。因為在paintEvent之前窗口值也會重置為dialog(繪圖設備)大小,所以這時候窗口大小為400×400單位長度,而視口我們還是再設定為100×100像素,所以這時候窗口大小的一單位長度為實際的100/400=0.25像素,所以畫一個100×100單位長度的矩形時,實際大小時25×25像素,所以變得更小
5
上圖中我是用qq的截圖功能進行測量,qq截圖會給出當前截取圖形的大小,正是25×25(圈的時候有所偏差).

最后再來看一個窗口和視口一起設置的例子.
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); } 

這里設置窗口坐標(-50,-50)映射為視口的原點,把窗口100×100單位長度映射為視口的100×100像素大小
這時候窗口邏輯坐標-50,-50就是視口的坐標(0,0),也就是繪圖設備的50,50坐標,所以窗口坐標(0,0)位置即繪圖設備坐標的(100,100)
因為窗口坐標100單位長度映射到視口坐標的100像素,所以上圖的painter.drawRect(0,0, 50, 50);一句繪制出了的結果就是在繪圖設備的(100,100)位置繪制一個50×50像素的矩形。
67

總結:

要得到QPainter繪圖的真正位置,要經過兩步

第一步:
窗口坐標轉換為視口坐標,轉換公式為:
vp_width / win_width * (draw_x - win_x)
其中vp_width為視口長度,win_width為窗口長度,draw_x為實際要繪制的x左上坐標,win_x為窗口的x左上坐標,y坐標同理

窗口大小轉換為視口大小,轉換公式為:
draw_width * vp_width / win_width
draw_width為要繪制的窗口長度,vp_width為視口長度,win_width為窗口長度

height同理

第二步:

視口坐標轉換為繪圖設備坐標,這一步就簡單的進行相加就好了
實際坐標x=上一步轉換來的視口坐標+vp_x
vp_x為視口的左上x坐標,實際坐標y同理

 

轉載地址:http://blog.csdn.net/syzobelix/article/details/9377863


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM