1.總述
Qt的要在當前類對應的窗口上繪圖一般需要重寫paintEvent函數,但是Qt的事件過濾器默認是把父窗口下子控件的繪圖事件給過濾了的,因此重寫父窗口的paintEvent函數是無法在子控件上進行繪圖的,下面舉一個例子。
1 void MainWindow::myDraw(QLabel * label) 2 { 3 QPainter painter(label); 4 painter.setPen(Qt::gray); 5 painter.setBrush(Qt::green); 6 painter.drawRect(10,10,20,20); 7 } 8 9 void MainWindow::paintEvent(QPaintEvent *) 10 { 11 myDraw(ui->label); 12 myDraw(ui->label_2); 13 }
如上所示,重寫MainWindow的paintEvent(QPaintEvent *)函數,然后在里面對子控件繪圖是沒有用的。
2.解決方案
還是以上面的例子為例。
法一
自己定義一個Mylabel類繼承於QLabel,然后在這個類中重寫paintEvent(QPaintEvent *)函數,並在里面繪圖。然后在ui界面中把對應的QLabel提升為Mylabel。這種方式不是很靈活,因此不多介紹,詳見https://blog.csdn.net/seanwang_25/article/details/18667871。
法二
在介紹法二之前,先補充一下qt中的事件機制,qt程序需要在main()函數創建一個QApplication對象,然后調用它的exec()函數。這個函數就是開始 Qt 的事件循環。在執行exec()函數之后,程序將進入事件循環來監聽應用程序的事件(鼠標事件,鍵盤事件,繪圖事件等)。當事件發生時,Qt 將創建一個事件對象。Qt 中所有事件類都繼承於QEvent。在事件對象創建完畢后,Qt 將這個事件先傳給事件過濾器:
virtual bool QObject::eventFilter ( QObject * watched, QEvent * event );
在事件過濾器中可以對感興趣的事件進行處理或屏蔽,令函數返回 true,不感興趣的事件繼續轉發,令函數返回 false或者交給父類處理。
通過事件過濾器的事件將交給事件分發器:
virtual bool QObject::event(QEvent *e)
event()函數並不直接處理事件,而是按照事件對象的類型分派給特定的事件處理函數(event handler),比如paintEvent(QPaintEvent *ev),mouseMoveEvent(QMouseEvent *ev).....
法二的實現思想:
使用事件過濾器,在子控件的繪圖事件被過濾前對子控件的繪圖事件進行處理。下面的示例代碼實現了點擊界面上的畫圖按鈕進行畫圖,點擊清除按鈕不進行畫圖。
1 //mainwindow.cpp 2 #include "mainwindow.h" 3 #include "ui_mainwindow.h" 4 #include<QPushButton> 5 #include<QPaintEvent> 6 #include<QPainter> 7 #include<QPen> 8 #include<QColor> 9 #include<QString> 10 #include<QDebug> 11 #include<QFont> 12 #include<QPixmap> 13 #include<QVector> 14 MainWindow::MainWindow(QWidget *parent) : 15 QMainWindow(parent), 16 ui(new Ui::MainWindow) 17 { 18 ui->setupUi(this); 19 labels.push_back(ui->label); 20 labels.push_back(ui->label_2); 21 ui->label->installEventFilter(this);//在label上安裝事件過濾器,this指針指定當事件發生時調用當前類中的事件過濾器進行處理 22 ui->label_2->installEventFilter(this);//在label_2上安裝事件過濾器 23 connect(ui->pushButton,&QPushButton::clicked,this,[&]() 24 { 25 flag =1; 26 update();//手動產生繪圖事件 27 }); 28 connect(ui->pushButton_2,&QPushButton::clicked,this,[&]() 29 { 30 flag =0; 31 update();//手動產生繪圖事件 32 }); 33 } 34 35 MainWindow::~MainWindow() 36 { 37 delete ui; 38 } 39 40 void MainWindow::myDraw(QLabel * label) 41 { 42 QPainter painter(label); 43 painter.setPen(Qt::gray); 44 painter.setBrush(Qt::green); 45 painter.drawRect(10,10,20,20); 46 } 47 48 //void MainWindow::paintEvent(QPaintEvent *) 49 //{ 50 // myDraw(ui->label); 51 // myDraw(ui->label_2); 52 //} 53 54 bool MainWindow::eventFilter(QObject *watched, QEvent *event) 55 { 56 if(watched == ui->label && event->type() == QEvent::Paint)//發生繪圖事件,且是在label上發生的 57 { 58 if(flag == 1)//標志位為1才在label上繪圖,否者不繪圖 59 { 60 myDraw(ui->label); 61 return true; 62 } 63 else 64 return false; 65 } 66 else if(watched == ui->label_2 && event->type() == QEvent::Paint) 67 { 68 if(flag == 1) 69 { 70 myDraw(ui->label_2); 71 return true; 72 } 73 else 74 return false; 75 } 76 else 77 return QMainWindow::eventFilter(watched,event);//其它繪圖事件交給父類處理 78 }
上述寫法還是有一個不方便的地方,就是當控件很多的時候,要對每一個控件都單獨的像第21行和第22行那樣單獨的安裝事件過濾器。因此可以向QApplication或者QCoreApplication添加事件過濾器,這樣就相當於當前應用程序下所有的控件都安裝了事件過濾器。
1 //main.cpp 2 #include "mainwindow.h" 3 #include <QApplication> 4 5 int main(int argc, char *argv[]) 6 { 7 QApplication a(argc, argv); 8 MainWindow w; 9 w.show(); 10 a.installEventFilter(&w);//給整個應用程序安裝事件過濾器 11 return a.exec(); 12 }
1 //mainwindow.cpp 2 #include "mainwindow.h" 3 #include "ui_mainwindow.h" 4 #include<QPushButton> 5 #include<QPaintEvent> 6 #include<QPainter> 7 #include<QPen> 8 #include<QColor> 9 #include<QString> 10 #include<QDebug> 11 #include<QFont> 12 #include<QPixmap> 13 #include<QVector> 14 MainWindow::MainWindow(QWidget *parent) : 15 QMainWindow(parent), 16 ui(new Ui::MainWindow) 17 { 18 ui->setupUi(this); 19 labels.push_back(ui->label); 20 labels.push_back(ui->label_2); 21 //ui->label->installEventFilter(this);//在label上安裝事件過濾器,this指針指定當事件發生時調用當前類中的事件過濾器進行處理 22 //ui->label_2->installEventFilter(this);//在label_2上安裝事件過濾器 23 connect(ui->pushButton,&QPushButton::clicked,this,[&]() 24 { 25 flag =1; 26 update();//產生繪圖事件 27 }); 28 connect(ui->pushButton_2,&QPushButton::clicked,this,[&]() 29 { 30 flag =0; 31 update();//產生繪圖事件 32 }); 33 } 34 35 MainWindow::~MainWindow() 36 { 37 delete ui; 38 } 39 40 void MainWindow::myDraw(QLabel * label) 41 { 42 QPainter painter(label); 43 painter.setPen(Qt::gray); 44 painter.setBrush(Qt::green); 45 painter.drawRect(10,10,20,20); 46 } 47 48 //void MainWindow::paintEvent(QPaintEvent *) 49 //{ 50 // myDraw(ui->label); 51 // myDraw(ui->label_2); 52 //} 53 54 bool MainWindow::eventFilter(QObject *watched, QEvent *event) 55 { 56 if(event->type() == QEvent::Paint)//繪圖事件 57 { 58 if(flag == 1)//標志位為1才在label上繪圖,否者不繪圖 59 { 60 for(QVector<QLabel *>::iterator it=labels.begin();it!=labels.end();it++) 61 { 62 if(watched == *it)//限制條件,只處理label上的繪圖事件 63 { 64 myDraw(*it); 65 return true;//返回true表示處理完成該事件,否者該事件還會繼續向下轉發 66 } 67 } 68 return QMainWindow::eventFilter(watched,event);//其它繪圖事件交給父類處理 69 } 70 else 71 return QMainWindow::eventFilter(watched,event);//其它繪圖事件交給父類處理 72 73 }else 74 return QMainWindow::eventFilter(watched,event); 75 76 }