【Qt】2D绘图之涂鸦板
00. 目錄
文章目錄
- 00. 目錄
- 01. 概述
- 02. 開發(fā)環(huán)境
- 03. 程序設(shè)計(jì)(基本功能)
- 04. 程序設(shè)計(jì)(放大功能)
- 05. 程序設(shè)計(jì)(放大功能)
- 06. 附錄
01. 概述
結(jié)合前面所學(xué)內(nèi)容,編寫一個(gè)簡單的涂鴉板程序。
02. 開發(fā)環(huán)境
Windows系統(tǒng):Windows10
Qt版本:Qt5.15或者Qt6
03. 程序設(shè)計(jì)(基本功能)
3.1 新建Qt Widgets應(yīng)用,項(xiàng)目名稱為12Draw,基類這次還用QDialog,類名保持Dialog不變即可。
3.2 到dialog.h文件中,先添加頭文件包含:#include ,然后添加幾個(gè)事件處理函數(shù)的聲明:
protected:void paintEvent(QPaintEvent *);void mousePressEvent(QMouseEvent *);void mouseMoveEvent(QMouseEvent *);void mouseReleaseEvent(QMouseEvent *);3.3 再添加幾個(gè)private私有變量定義:
private:QPixmap pix;QPoint lastPoint;QPoint endPoint;因?yàn)樵谇懊娼坛讨泻瘮?shù)里定義的QPixmap類對(duì)象是臨時(shí)的,不能存儲(chǔ)以前的值,為了實(shí)現(xiàn)保留上次的繪畫結(jié)果,我們需要將其設(shè)為全局變量。后面兩個(gè)QPoint變量存儲(chǔ)鼠標(biāo)指針的兩個(gè)坐標(biāo)值,我們需要用這兩個(gè)坐標(biāo)值完成繪圖。
3.4 在dialog.cpp文件中,先添加頭文件包含:#include ,然后在構(gòu)造函數(shù)中添加如下初始代碼:
//設(shè)置窗口大小resize(600, 500);//設(shè)置畫布大小和背景pix = QPixmap(200, 200);pix.fill(Qt::white);3.5 在paintEvent添加如下代碼
//繪圖事件 void Dialog::paintEvent(QPaintEvent *) {QPainter p(&pix);//根據(jù)鼠標(biāo)指針前后兩個(gè)位置繪制直線p.drawLine(lastPoint, endPoint);//前一個(gè)坐標(biāo)值等于后一個(gè)坐標(biāo)值lastPoint = endPoint;QPainter painter(this);painter.drawPixmap(0, 0, pix); }3.6 在mousePressEvent添加如下代碼
void Dialog::mousePressEvent(QMouseEvent *e) {if (e->button() == Qt::LeftButton){lastPoint = e->pos();}endPoint = lastPoint; }當(dāng)鼠標(biāo)左鍵按下時(shí)獲得開始點(diǎn),每次開始繪制都讓結(jié)束點(diǎn)和開始點(diǎn)重合,這樣確保這兩個(gè)點(diǎn)的值都是預(yù)期的值。
3.7 在mouseMoveEvent添加如下代碼
//鼠標(biāo)移動(dòng)事件 void Dialog::mouseMoveEvent(QMouseEvent *e) {if (e->button() & Qt::LeftButton){endPoint = e->pos();update();} }當(dāng)鼠標(biāo)移動(dòng)時(shí)獲得結(jié)束點(diǎn),并更新繪制,注意這里的buttons()函數(shù)可以獲取鼠標(biāo)移動(dòng)過程中按下的所有按鍵,然后用&Qt::LeftButton來判斷是否按下了左鍵,在mouseMoveEvent()中必須使用該方法來判斷按下的鼠標(biāo)按鍵。最后調(diào)用update()函數(shù)會(huì)執(zhí)行paintEvent()函數(shù)進(jìn)行重新繪制。
3.8 在mouseReleaseEvent添加如下代碼
//鼠標(biāo)釋放事件 void Dialog::mouseReleaseEvent(QMouseEvent *e) {if (e->button() == Qt::LeftButton){endPoint = e->pos();update();}}當(dāng)鼠標(biāo)按鍵釋放時(shí)也進(jìn)行重繪。現(xiàn)在運(yùn)行程序,使用鼠標(biāo)在白色畫布上進(jìn)行繪制,發(fā)現(xiàn)已經(jīng)實(shí)現(xiàn)了簡單的涂鴉板功能,效果如下圖所示。
04. 程序設(shè)計(jì)(放大功能)
4.1 添加放大按鈕。到dialog.h文件中,先添加頭文件:
#include <QDialog> #include <QMouseEvent> #include <QPushButton>QT_BEGIN_NAMESPACE namespace Ui { class Dialog; } QT_END_NAMESPACEclass Dialog : public QDialog {Q_OBJECTpublic:Dialog(QWidget *parent = nullptr);~Dialog();protected:void paintEvent(QPaintEvent *);void mousePressEvent(QMouseEvent *);void mouseMoveEvent(QMouseEvent *);void mouseReleaseEvent(QMouseEvent *);private slots:void zoomIn();private:Ui::Dialog *ui;QPixmap pix;QPoint lastPoint;QPoint endPoint;qreal scale;QPushButton *button; }; #endif // DIALOG_H4.2 到dialog.cpp文件中,先在構(gòu)造函數(shù)中添加如下代碼:
Dialog::Dialog(QWidget *parent): QDialog(parent), ui(new Ui::Dialog) {ui->setupUi(this);//設(shè)置窗口大小resize(600, 500);//設(shè)置畫布大小和背景pix = QPixmap(300, 250);pix.fill(Qt::white);//設(shè)置初始放大倍數(shù)為1scale = 1;//新建按鈕對(duì)象button = new QPushButton(this);//設(shè)置按鈕顯示文本button->setText(tr("放大"));//設(shè)置按鈕的位置button->move(500, 450);//信號(hào)與槽關(guān)聯(lián)connect(button, &QPushButton::clicked, this, &Dialog::zoomIn);}這里使用代碼創(chuàng)建了一個(gè)按鈕對(duì)象,并將其單擊信號(hào)關(guān)聯(lián)到了放大槽上,也就是說按下這個(gè)按鈕,就會(huì)執(zhí)行zoomIn()槽。
4.3 添加zoomIn函數(shù)
//槽函數(shù) 放大 void Dialog::zoomIn() {scale *= 2;update(); }這里實(shí)現(xiàn)每按下這個(gè)按鈕,放大值都擴(kuò)大兩倍。后面調(diào)用update()函數(shù)來更新顯示。
4.4 讓畫布的內(nèi)容放大有兩個(gè)辦法,一個(gè)是直接放大畫布的坐標(biāo)系統(tǒng),一個(gè)是放大窗口的坐標(biāo)系統(tǒng)。下面我們先來放大窗口的坐標(biāo)系統(tǒng)。更改paintEvent()函數(shù)如下:
//繪圖事件 void Dialog::paintEvent(QPaintEvent *) {QPainter p(&pix);//根據(jù)鼠標(biāo)指針前后兩個(gè)位置繪制直線p.drawLine(lastPoint, endPoint);//前一個(gè)坐標(biāo)值等于后一個(gè)坐標(biāo)值lastPoint = endPoint;QPainter painter(this);painter.scale(scale, scale);painter.drawPixmap(0, 0, pix); }現(xiàn)在運(yùn)行程序,效果如下圖所示。
然后按下zoomIn按鈕,效果如下圖所示。
現(xiàn)在再用鼠標(biāo)進(jìn)行繪制,發(fā)現(xiàn)圖形已經(jīng)不能和鼠標(biāo)軌跡重合了,效果如下圖所示。
窗口的坐標(biāo)擴(kuò)大了,但是畫布的坐標(biāo)并沒有擴(kuò)大,而我們畫圖用的坐標(biāo)值是鼠標(biāo)指針的,鼠標(biāo)指針又是獲取的窗口的坐標(biāo)值。現(xiàn)在窗口和畫布的同一點(diǎn)的坐標(biāo)并不相等,所以就出現(xiàn)了這樣的問題。
其實(shí)解決辦法很簡單,窗口放大了多少倍,就將獲得的鼠標(biāo)指針的坐標(biāo)值縮小多少倍就行了。我們將paintEvent()函數(shù)更改如下:
//繪圖事件 void Dialog::paintEvent(QPaintEvent *) {QPainter p(&pix);//根據(jù)鼠標(biāo)指針前后兩個(gè)位置繪制直線p.drawLine(lastPoint / scale, endPoint / scale);//前一個(gè)坐標(biāo)值等于后一個(gè)坐標(biāo)值lastPoint = endPoint;QPainter painter(this);painter.scale(scale, scale);painter.drawPixmap(0, 0, pix); }這種用改變窗口坐標(biāo)大小來改變畫布面積的方法,實(shí)際上是有損圖片質(zhì)量的。就像將一張位圖放大一樣,越放大越不清晰。原因就是,它的像素的個(gè)數(shù)沒有變,如果將可視面積放大,那么單位面積里的像素個(gè)數(shù)就變少了,所以畫質(zhì)就差了。
05. 程序設(shè)計(jì)(放大功能)
方法二
5.1 擴(kuò)大畫布坐標(biāo)系統(tǒng)。先將paintEvent()更改如下:
//繪圖事件 void Dialog::paintEvent(QPaintEvent *) {QPainter p(&pix);p.scale(scale, scale);//根據(jù)鼠標(biāo)指針前后兩個(gè)位置繪制直線p.drawLine(lastPoint, endPoint);//前一個(gè)坐標(biāo)值等于后一個(gè)坐標(biāo)值lastPoint = endPoint;QPainter painter(this);painter.drawPixmap(0, 0, pix); }這時(shí)運(yùn)行程序,先進(jìn)行繪制,然后點(diǎn)擊zoomIn按鈕,發(fā)現(xiàn)以前的內(nèi)容并沒有放大,而當(dāng)我們?cè)俅卫L畫時(shí),發(fā)現(xiàn)鼠標(biāo)指針和繪制的線條又不重合了。效果如下圖所示。
這并不是我們想要的結(jié)果,為了實(shí)現(xiàn)按下放大按鈕,畫布和圖形都進(jìn)行放大,我們可以使用緩沖畫布(就是一個(gè)輔助畫布)來實(shí)現(xiàn)。將paintEvent()函數(shù)內(nèi)容更改如下。
//繪圖事件 void Dialog::paintEvent(QPaintEvent *) {if (1 != scale){//臨時(shí)畫布QPixmap copyPix(pix.size() * scale);QPainter pp(©Pix);pp.scale(scale, scale);//將以前畫布上的內(nèi)容復(fù)制到現(xiàn)在的畫布上pp.drawPixmap(0, 0, pix);//將放大后的內(nèi)容在復(fù)制到原來的畫布上pix = copyPix;//讓scale重新置1scale = 1;}QPainter p(&pix);p.scale(scale, scale);//根據(jù)鼠標(biāo)指針前后兩個(gè)位置繪制直線p.drawLine(lastPoint / scale, endPoint / scale);//前一個(gè)坐標(biāo)值等于后一個(gè)坐標(biāo)值lastPoint = endPoint;QPainter painter(this);painter.drawPixmap(0, 0, pix); }運(yùn)行結(jié)果
06. 附錄
源碼下載:【Qt】2D繪圖之涂鴉板.rar
總結(jié)
以上是生活随笔為你收集整理的【Qt】2D绘图之涂鸦板的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Qt】2D绘图之绘图中其它问题
- 下一篇: 【Qt】2D绘图之双缓冲绘图