[Qt教程] 第16篇 2D绘图(六)坐标系统
生活随笔
收集整理的這篇文章主要介紹了
[Qt教程] 第16篇 2D绘图(六)坐标系统
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
[Qt教程]?第16篇 2D繪圖(六)坐標系統
??|?查看: 738|?回復: 0| 坐標系統 版權聲明 該文章原創于Qter開源社區(www.qter.org),作者yafeilinux,轉載請注明出處! 導語 前面一節我們講解了圖片的顯示,其中很多地方都用到了坐標的變化。這一節我們將講解Qt的坐標系統,分為兩部分來講解:第一部分主要講解前面一節的那幾個函數,它們分別是translate()平移變換、scale()比例變換、rotate()旋轉變換、shear()扭曲變換。最后還會介紹兩個有用的函數save()和restore(),利用它們來保存和彈出坐標系的狀態,從而實現快速利用幾個變換函數來繪圖。 ? ?? ??第二部分會和大家一起來研究一下Qt的坐標系統,其中可能會涉及到多個坐標,大家一定要親自動手操作感悟一下,不然很難理解的! 環境:Windows Xp + Qt 4.8.4+Qt Creator2.6.2 目錄 第一部分?Qt坐標系統應用 一、坐標系統簡介 二、坐標系統變換 三、坐標系統的保存 第二部分?坐標系統深入研究 一、獲得坐標信息 二、研究變換后的坐標系統 三、研究繪圖設備的坐標系統 正文 第一部分?Qt坐標系統應用 一、坐標系統簡介 ? ? Qt中每一個窗口都有一個坐標系統,默認的,窗口左上角為坐標原點,水平向右依次增大,水平向左依次減小,垂直向下依次增大,垂直向上依次減小。原點即為(0,0)點,以像素為單位增減。 下面仍然在上一節的程序中進行代碼演示,更改paintEvent()的內容如下: void?Dialog::paintEvent(QPaintEvent?*) { ? ??QPainter?painter(this); ? ??painter.setBrush(Qt::red); ? ??painter.drawRect(0,?0,?100,?100); ? ??painter.setBrush(Qt::yellow); ? ??painter.drawRect(-50,?-50,?100,?100); } 我們先在原點(0,0)繪制了一個長寬都是100像素的紅色矩形,又在(-50,-50)點繪制了一個同樣大小的**矩形。可以看到,我們只能看到**矩形的四分之一部分。運行程序,效果如下圖所示。 二、坐標系統變換 默認的,QPainter在相關設備的坐標系統上進行繪制,在進行繪圖時,可以使用QPainter::scale()函數縮放坐標系統;使用QPainter::rotate()函數順時針旋轉坐標系統;使用QPainter::translate()函數平移坐標系統;還可以使用QPainter::shear()圍繞原點來扭曲坐標系統。如下圖所示。 坐標系統的2D變換由QTransform類實現,我們可以使用前面提到的那些便捷函數進行坐標系統變換,當然也可以通過QTransform類實現。 1.平移變換。將paintEvent()函數內容更改如下: void?Dialog::paintEvent(QPaintEvent?*) { ? ??//?平移變換 ? ??QPainter?painter(this); ? ??painter.setBrush(Qt::yellow); ? ??painter.drawRect(0,?0,?50,?50); ? ??painter.translate(100,?100);??//將點(100,100)設為原點 ? ??painter.setBrush(Qt::red); ? ??painter.drawRect(0,?0,?50,?50); ? ??painter.translate(-100,?-100); ? ??painter.drawLine(0,?0,?20,?20); } 運行程序,效果如下圖所示。 這里先在原點(0, 0)繪制了一個寬、高均為50的正方形,然后使用translate()函數將坐標系統進行了平移,使(100, 100)點成為了新原點,所以我們再次進行繪制的時候,雖然drawRect()中的邏輯坐標還是(0, 0)點,但實際顯示出來的卻是在(100, 100)點的紅色正方形。可以再次使用translate()函數進行反向平移,使原點重新回到窗口左上角。 2.縮放變換。將paintEvent()函數中的內容更改如下: void?Dialog::paintEvent(QPaintEvent?*) { ? ??//?縮放 ? ??QPainter?painter(this); ? ??painter.setBrush(Qt::yellow); ? ??painter.drawRect(0,?0,?100,?100); ? ??painter.scale(2,?2);?//放大兩倍 ? ??painter.setBrush(Qt::red); ? ??painter.drawRect(50,?50,?50,?50); } 運行程序,效果如下圖所示。 可以看到,當我們使用scale()函數將坐標系統的橫、縱坐標都放大兩倍以后,邏輯上的(50,?50)點變成了窗口上的(100, 100)點,而邏輯上的長度50,繪制到窗口上的長度卻是100。 3.扭曲變換。將paintEvent()函數更改如下: void?Dialog::paintEvent(QPaintEvent?*) {? ?? ? ?? ?? ???//?扭曲? ?? ? ?? ?? ???QPainter?painter(this);? ?? ? ?? ?? ???painter.setBrush(Qt::yellow);? ? ? ?? ?? ???painter.drawRect(0,?0,?50,?50);? ?? ? ?? ?? ???painter.shear(0,?1);?//縱向扭曲變形? ?? ? ?? ?? ???painter.setBrush(Qt::red);? ?? ? ?? ?? ???painter.drawRect(50,?0,?50,?50); } 運行程序,效果如下圖所示。 shear()有兩個參數,第一個是對橫向進行扭曲,第二個是對縱向進行扭曲,而取值就是扭曲的程度。比如程序中對縱向扭曲值為1,那么就是紅色正方形左邊的邊下移一個單位,右邊的邊下移兩個單位,值為1就表明右邊的邊比左邊的邊多下移一個單位。大家可以更改取值,測試效果。 4.旋轉變換。將paintEvent()函數更改如下: void?Dialog::paintEvent(QPaintEvent?*) { ? ??//?旋轉 ? ??QPainter?painter(this); ? ??painter.drawLine(0,?0,?100,?0); ? ??painter.rotate(30);?//以原點為中心,順時針旋轉30度 ? ??painter.drawLine(0,?0,?100,?0); ? ??painter.translate(100,?100); ? ??painter.rotate(30); ? ??painter.drawLine(0,?0,?100,?0); } 運行程序,效果如下圖所示。 這里先繪制了一條水平的直線,然后將坐標系統旋轉了30度,又繪制了一條直線。可以看到,默認是以原點(0, 0)為中心旋轉的。如果想改變旋轉中心,可以使用translate()函數,比如這里將中心移動到了(100, 100)點,然后旋轉了30度,又繪制了一條直線。我們的本意是想在新的原點從水平方向旋轉30度進行繪制,可是實際效果卻超過了30度。這是由于前面已經使用rotate()函數旋轉過坐標系統了,后面的旋轉會在前面的基礎上進行。 下面我們再次更改paintEvent()函數: void?Dialog::paintEvent(QPaintEvent?*) {? ??//?旋轉? ?? ? ?? ?QPainter?painter(this);? ? ? ?? ?painter.drawLine(0,?0,?100,?0);? ? ? ?? ?painter.rotate(30);?//以原點為中心,順時針旋轉30度? ?? ? ?? ?painter.drawLine(0,?0,?100,?0);?? ?? ? ?? ?painter.rotate(-30);?//?反向旋轉?? ?? ? ?? ?painter.translate(100,?100);? ?? ? ?? ?painter.rotate(30);? ?? ? ?? ?painter.drawLine(0,?0,?100,?0); } 運行程序,效果如下圖所示。 這次我們在移動原點以前先將坐標系統反向旋轉,可以看到,第二次旋轉也是從水平方向開始的。 其實,前面講到的這幾個變換函數都是如此,他們改變了坐標系統以后,如果不進行逆向操作,坐標系統是無法自動復原的。針對這個問題,下面我們將講解兩個非常實用的函數來實現坐標系統的保存和還原。 三、坐標系統的保存 我們可以先利用save()函數來保存坐標系現在的狀態,然后進行變換操作,操作完之后,再用restore()函數將以前的坐標系狀態恢復,其實就是一個入棧和出棧的操作。下面來看一個具體的例子,更改paintEvent()函數如下: void?Dialog::paintEvent(QPaintEvent?*) { ? ??QPainter?painter(this); ? ??painter.save();?//保存坐標系狀態 ? ??painter.translate(100,100); ? ??painter.drawLine(0,?0,?50,?50); ? ??painter.restore();?//恢復以前的坐標系狀態 ? ??painter.drawLine(0,?0,?50,?50); } 運行程序,效果如下圖所示。利用好這兩個函數,可以實現坐標系快速切換,繪制出不同的圖形。 第二部分?坐標系統深入研究 在第一部分,我們主要學習了常用的一些坐標變換,雖然在編程中,這些變換已經可以滿足大部分的應用需求。不過,大家是否也感覺到現在對坐標的變換依然很模糊,沒有一個透徹的認識。下面咱們就一點一點來研究一下坐標系統的變換。 一、獲得坐標信息 前面圖形的變換都是我們眼睛看到的,為了更具有說服力,下面將獲取具體的坐標數據,通過參考數據來進一步了解坐標變換。 1.首先在dialog.h文件中添加頭文件包含: #include?<QMouseEvent> 然后添加一個protected鼠標事件處理函數聲明: void?mousePressEvent(QMouseEvent?*); 2.到dialog.cpp文件中,先添加頭文件包含: #include?<QDebug> 然后添加函數定義: void?Dialog::mousePressEvent(QMouseEvent?*event) {? ?? ? ?? ?? ?qDebug()?<<?event->pos(); } 這里應用了qDebug()函數,該函數可以在程序運行時將程序中的一些信息輸出到控制面板,在QtCreator中會將信息輸出到其下面的“應用程序輸出”窗口。這個函數很有用,在進行簡單的程序調試時,都可以利用該函數進行。我們這里利用它將鼠標指針的坐標值輸出出來。 3.將paintEvent()函數更改如下: void?Dialog::paintEvent(QPaintEvent?*) { ? ??QPainter?painter(this); ? ??painter.drawRect(0,?0,?50,?50); } 現在運行程序,然后將鼠標在繪制的正方形右下角頂點處點擊,在QtCreator的應用程序輸出窗口就會輸出相應點的坐標信息。如下圖所示。大家也可以點擊一下其他的地方,查看輸出信息。 二、研究變換后的坐標系統 1.首先研究放大后的坐標系統,將paintEvent()函數更改如下: void?Dialog::paintEvent(QPaintEvent?*) { ? ??//?放大 ? ??QPainter?painter(this); ? ??painter.drawRect(0,?0,?50,?50); ? ??painter.scale(1,?2); ? ??painter.drawRect(50,?50,?50,?50); } 這里,我們將縱坐標放大了兩倍,而橫坐標沒有改變。運行程序,效果如下圖所示。 大家可以查看一下第二個矩形的各個頂點的坐標,左上角是(50, 100)也就是說縱坐標擴大了兩倍,查看其它點,會發現左右兩條邊長都變成了100。 2.研究旋轉后的坐標系統。修改paintEvent()函數如下: void?Dialog::paintEvent(QPaintEvent?*) { ? ??QPainter?painter(this); ? ??painter.drawLine(0,?0,?100,?0); ? ??painter.rotate(45); ? ??painter.setPen(Qt::red); ? ??painter.drawLine(0,?0,?100,?0); } 這里我們先繪制了一條水平的直線,然后將坐標系統旋轉45度,再次繪制了一條相同的紅色直線。運行程序,效果如下圖所示。 大家可以查看一下各處的坐標,雖然旋轉后直線位置發生了變化,但是坐標其實是沒有變化的。 我們也可以利用這種方法來測試一下應用其他變換函數后坐標的變化,這里就不再敖述。 三、研究繪圖設備的坐標系統 除了可以在QWidget等窗口部件上進行繪制以外,還可以在QPixmap、QImage等上面進行繪制,這些均稱為繪圖設備。下面我們就以QPixmap為例,來研究一下它的坐標系統。 1.首先更改paintEvent()函數如下: void?Dialog::paintEvent(QPaintEvent?*) { ? ??QPainter?painter(this); ? ??QPixmap?pix(200,?200); ? ??pix.fill(Qt::red);? ?//背景填充為紅色 ? ??painter.drawPixmap(0,?0,?pix); } ? ??在前面我們已經講過,QPixmap可以用來顯示圖片。其實QPixmap本身就是一個繪圖設備,可以在它上面直接繪圖。這里先生成了一個寬和高都是200像素的QPixmap類對象(注意,必須在構建時指定其大小),然后為其填充了紅色,最后在窗口的原點進行了繪制。為了表述方面,下面將QPixmap對象稱為畫布,這里就是先繪制了一個紅色畫布。 ? ??我們運行程序,并在紅色畫布的左上角和右下角分別點擊,查看輸出的坐標。如下圖所示。因為點擊位置的誤差,所以兩個點可能不是頂點。 2.下面我們接著更改paintEvent()的代碼: void?Dialog::paintEvent(QPaintEvent?*) {? ?? ? ?? ?QPainter?painter(this);? ?? ? ?? ?QPixmap?pix(200,?200);? ?? ? ?? ?pix.fill(Qt::red);? ?//背景填充為紅色? ?? ? ?? ?painter.drawPixmap(100,?100,?pix); } 這次我們在(100, 100)點重新繪制了畫布,現在運行程序,發現畫布左上角坐標確實為(100,100),這個就是我們窗口中的坐標,是沒有什么疑問的。效果如下圖所示。 窗口和畫布都是繪圖設備,那么畫布本身有沒有自己的坐標系統呢?我們接著研究! 3.我們繼續更改paintEvent()函數: void?Dialog::paintEvent(QPaintEvent?*) { ? ??QPainter?painter(this); ? ??QPixmap?pix(200,?200); ? ??pix.fill(Qt::red); ? ??//新建QPainter類對象,在pix上進行繪圖 ? ??QPainter?pp(&pix); ? ??//在pix上的(0,0)點和(50,50)點之間繪制直線 ? ??pp.drawLine(0,?0,?50,?50); ? ??painter.drawPixmap(100,?100,?pix); } 這里我們為畫布pix創建了一個QPainter對象pp,注意這個pp只能在畫布上繪畫,然后我們在畫布上繪制了一條從原點(0, 0)開始的直線。運行程序,效果如下圖所示。 可以看到,直線是從畫布的左上角開始繪制的,也就是說,畫布也有自己的坐標系統,坐標原點在畫布的左上角。 ? ??下面補充說明一下:QPainter painter(this) ,this就表明了是在窗口上進行繪圖,所以利用painter進行的繪圖都是在窗口部件上的,painter進行的坐標變換,是變化的窗口的坐標系;而利用pp進行的繪圖都是在畫布上進行的,如果它進行坐標變化,就是變化的畫布的坐標系。 而通過坐標數值,我們可以得出下面兩條結論: 第一,QWidget和QPixmap各有一套坐標系統,它們互不影響。可以看到,無論畫布在窗口的什么位置,它的坐標原點依然在左上角,為(0,0)點,沒有變。 第二,我們所得到的鼠標指針的坐標值是窗口坐標系統的,不是畫布的坐標。 4.下面這個例子將對比分析擴大窗口坐標或畫布坐標的異同。 首先將paintEvent()函數更改如下: void?Dialog::paintEvent(QPaintEvent?*) { ? ??QPainter?painter(this); ? ??QPixmap?pix(200,200); ? ??//放大前輸出畫布的大小 ? ??qDebug()?<<?pix.size(); ? ??pix.fill(Qt::red); ? ??QPainter?pp(&pix); ? ??//畫布的坐標擴大2倍 ? ??pp.scale(2,?2); ? ??//在畫布上的(0,0)點和(50,50)點之間繪制直線 ? ??pp.drawLine(0,?0,?50,?50); ? ??//放大后輸出畫布的大小 ? ??qDebug()?<<?pix.size(); ? ??painter.drawPixmap(0,?0,?pix); } 這里我們將畫布坐標系統放大了兩倍,然后從原點開始繪制了一條直線,并分別輸出了畫布放大前后的大小。運行程序,效果如下圖所示。 下面再次更改paintEvent()函數: void?Dialog::paintEvent(QPaintEvent?*) { ? ??QPainter?painter(this); ? ??QPixmap?pix(200,200); ? ??qDebug()?<<?pix.size(); ? ??//窗口坐標擴大2倍 ? ??painter.scale(2,2); ? ??pix.fill(Qt::red); ? ??QPainter?pp(&pix); ? ??pp.drawLine(0,?0,?50,?50); ? ??qDebug()?<<?pix.size(); ? ??painter.drawPixmap(0,?0,?pix); } 這里與前面唯一的不同是:這里放大了窗口的坐標系統,而前面放大的是畫布的坐標系統。運行程序,效果如下圖所示。 可以看到,整個畫布的可見面積變大了。直線雖然長度依然是100,但是這次的效果跟前面明顯不同,因為是窗口坐標變大,所以在上面繪出的線條有了明顯的顆粒感。 上面兩個程序雖然最終輸出的數據是一樣的,但實際效果還是有很大不同的。大家可以根據需要進行選擇性應用。 結語 ? ?? ??在這一節中我們講述了坐標相關的多個知識點,經過本節的學習,大家應該已經對Qt的2D繪圖有了一個淺顯的認識,下一節我們將做一個比較實用的涂鴉板例子。 ? ?? ? Qt的坐標系統是很有必要好好研究的,它對深入學習應用Qt繪圖很有幫助。如果大家想更系統的學習Qt坐標系統,可以參考《Qt Creator快速入門》的第10章相關內容。 涉及到的源碼: ?painter_2_1.zip?? ?painter_2_2.zip?? ?painter_2_3.zip?? |
總結
以上是生活随笔為你收集整理的[Qt教程] 第16篇 2D绘图(六)坐标系统的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [Qt教程] 第15篇 2D绘图(五)绘
- 下一篇: [Qt教程] 第47篇 进阶(七) 定制