零基礎開始QT繪圖(3)
在上兩篇基礎上,我們瞭解利用Painter的四個物件進行繪圖的基本用法,接下來,我們來實戰一次,接下來的兩篇教程我們來做一個很簡單的繪圖板小專案。
我們要實現的功能主要兩點:一、可隨意塗鴉,二、可以儲存我們塗鴉的作品。在這前,我們要解決一個問題,那就是如何使用滑鼠事件來繪製圖形。
一、掌握滑鼠事件的實現
利用滑鼠繪圖,我們必須首先了解三個滑鼠事件,mousePressEvent、mouseReleaseEvent和mouseMoveEvent。其中,我麼在mouseMove中必須實時獲得滑鼠的位置資訊,這樣才可以將實時繪圖的效果顯示出來。mousePress則是負責結束當前繪圖以及將已經繪製好的圖形儲存在圖形元素佇列中。
我們首先看看mousePressEvent怎麼實現?
在第一篇教程中,我們已經知道了QPainterEvent是需要在Widget中實現的虛擬函式,mousePressEvent以及mouseMoveEvent其實和QPainterEvent一樣,必須手動實現,而不能像QPushButton那樣通過右鍵選擇資訊槽即可。我們來看看mousePressEvnet的實現:
首先在widget標頭檔案中增加宣告:
void mousePressEvent(QMouseEvent *event);
然後,在cpp檔案中,增加對應的實現方法體:
void Widget::mousePressEvent(QMouseEvent *event) { //利用QMessageBox輸出hello的對話方塊內容 QMessageBox::about(this,"title","hello"); }
我們來測試一下執行效果,看看滑鼠是否有反應了,效果如下:
緊接下來,我們同樣的方法實現mouseMoveEvent、mouseReleaseEvent,我們這次在讓我們的滑鼠移動式顯示出滑鼠的實時座標值。程式碼如下:
Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) { setMouseTracking(true); //開啟滑鼠追蹤實時更新 ui->setupUi(this); } void Widget::paintEvent(QPaintEvent *event) { QPainter painters(this); QRect rect(this->width()/3,this->height()/2,200,40); painters.drawText(rect,"mousePosition:"+QString::number(coursePoint.x())+","+QString::number(coursePoint.y())); } void Widget::mousePressEvent(QMouseEvent *event) { QMessageBox::about(this,"title","hello"); } void Widget::mouseMoveEvent(QMouseEvent *event) { coursePoint = QCursor::pos(); //coursePoint 變數在標頭檔案中定義,獲得滑鼠位置 this->update(); //強制更新繪圖 }
執行效果如下:
到這裡滑鼠事件的準備工作已經完成。我們這裡的滑鼠位置的顯示是通過update不斷的重新整理來實現的,也就是說老的座標值不會遺留在上面,始終顯示的都是最新的座標。但如果是這樣的話,給我們的塗鴉帶來了麻煩,如果我們每次繪製的圖形都被重新整理了,只剩下最後的一筆,而且一旦滑鼠有移動就會因呼叫了update而重新整理消失。
二、開始塗鴉
為了解決上面的重新整理繪圖和保留前面的繪圖的矛盾,我們引入一種機制,這個機制很簡單,那就是將前面的繪圖先儲存起來,等更新的時候全部重繪一次。那麼儲存起來的方法,一般有三種,一種是儲存在一個數組裡面,一種是儲存在一個容器裡面。
我們知道陣列的大小是固定的,這個很麻煩,比方說定義1000就只能畫1000個圖形元素。為了解決這個問題,我們使用的是容器,這裡我們使用QList來儲存,每次在滑鼠移動的時候就將最後的座標位置儲存到QList中去。
我們來看看程式碼是如何實現的,首先,宣告一個QList用於儲存座標點:
QList<QPoint> plist;
接下來,我們在mouseEvent中不停的儲存點位置:
plist.append(event->pos());
this->update();
最後,我們在PainterEvent中實施繪製
QPainter painters(this);
QRect rect(this->width()-140,this->height()-20,200,40);
painters.drawText(rect,"mousePosition:"+QString::number(coursePoint.x())+","+QString::number(coursePoint.y()));
if(plist.count()>2)
for(int i=0;i<plist.count()-1;i++)
{
qDebug()<<i;
painters.drawLine(plist[i],plist[1+i]);
}
我們一開始要實現的就是塗鴉,所以不必考慮其他的圖形繪製,這種塗鴉實現的思路就是繪製直線,無數的小直線段共同組成了這些不規則的塗鴉(毛線團)軌跡。我們來看看執行效果:
隨手塗鴉了一條似蛇非蛇的東東,這個圖形是一條連貫的線組成,僅僅一條線,因為我們的QList儲存的是點。那麼我們如何實現能夠繪製多條線的塗鴉呢?
三、能繪製多條線多個圖形的繪圖板
這裡,我們來改進一下我們的儲存容器,是指能夠判斷我們繪製多條線的意圖。思路很簡單,每次儲存的時候將點分為兩類,一類是起點,一類是終點,當然QList無法辦到儲存兩個值,所以我們這裡另外再配合plist啟用另一個QList來儲存點的型別(採用bool型別作為內容),變數名為isEndPoint,當點的型別是不是終點型別的時候則繪製線條,程式碼如下:
void Widget::paintEvent(QPaintEvent *event)
{
QPainter painters(this);
QRect rect(this->width()-140,this->height()-20,200,40);
painters.drawText(rect,"mousePosition:"+QString::number(coursePoint.x())+","+QString::number(coursePoint.y()));
if(plist.count()>2)
for(int i=0;i<plist.count()-1;i++)
{
qDebug()<<i;
if(isEndPoint[i]>0)
painters.drawLine(plist[i],plist[1+i]);
}
}
void Widget::mousePressEvent(QMouseEvent *event)
{
startPos = event->pos();
start=true;
}
void Widget::mouseMoveEvent(QMouseEvent *event)
{
if(start)
{
plist.append(startPos);
isEndPoint.append(true);
startPos =event->pos();
}
coursePoint =event->pos();// QCursor::pos();
this->update();
}
void Widget::mouseReleaseEvent(QMouseEvent *event)
{
plist.append(startPos);
isEndPoint.append(false);
start=false;
}
執行效果如下,我們依然是繪製那條蛇:
從這裡我們可以看到,我們的思路是成功的。雖然實現了,但是貌似有些麻煩,麻煩就麻煩再每次繪製的時候要用一個for迴圈來逐個將線條繪製出來。
現在繪製的還是線條,那如果有更多的更復雜的圖形,一定會讓人抓狂的,因為我們的QList儲存的型別是單一的,不能說所有的圖形都用點來完成儲存。
下面我們說說第三種思路,利用QPixmap來儲存各種塗鴉元素。
四、利用QPixmap完成圖形的臨時儲存
我們首先來看程式碼的實現,首先在標頭檔案中增加一個QPixmap的物件宣告:
QPixmap *pix;
QPoint startPos; //記錄滑鼠的當前位置
QPoint endPos;
然後,在mouseMoveEvent中,直接將線條繪製在pix中,然後再PainterEvent中更新即可,沒有見到儲存的過程,意思是這個QPixmap是真正意義上的畫板,它會自己記錄儲存曾經畫過的內容。
void Widget::mouseMoveEvent(QMouseEvent *event)
{
QPainter *painter = new QPainter;
if(start)
{
painter->begin(pix);
painter->drawLine(startPos, event->pos());
painter->end();
// plist.append(startPos);
//isEndPoint.append(true);
startPos =event->pos();
}
coursePoint =event->pos();// QCursor::pos();
this->update();
}
執行效果如下:
圖形雖然畫得有點醜,隨手塗鴉嘛,應該有點塗鴉感。好了,這次我們就到這裡結束。
下一篇,我麼繼續介紹標準圖形(如線條,圓,矩形,填充,漸變等功能)的繪製以及工具欄的製作,讓它更像我們的畫筆工具,請有需要的小夥伴們關注部落格更新。