1. 程式人生 > >Qt之在控制元件上繪圖

Qt之在控制元件上繪圖

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 }
//mainwindow.cpp
#include "mainwindow.h" #include "ui_mainwindow.h" #include<QPushButton> #include<QPaintEvent> #include<QPainter> #include<QPen> #include<QColor> #include<QString> #include<QDebug> #include<QFont> #include<QPixmap> #include<QVector> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); labels.push_back(ui->label); labels.push_back(ui->label_2); //ui->label->installEventFilter(this);//在label上安裝事件過濾器,this指標指定當事件發生時呼叫當前類中的事件過濾器進行處理 //ui->label_2->installEventFilter(this);//在label_2上安裝事件過濾器 connect(ui->pushButton,&QPushButton::clicked,this,[&]() { flag =1; update();//產生繪圖事件 }); connect(ui->pushButton_2,&QPushButton::clicked,this,[&]() { flag =0; update();//產生繪圖事件 }); } MainWindow::~MainWindow() { delete ui; } void MainWindow::myDraw(QLabel * label) { QPainter painter(label); painter.setPen(Qt::gray); painter.setBrush(Qt::green); painter.drawRect(10,10,20,20); } void MainWindow::paintEvent(QPaintEvent *) { myDraw(ui->label); myDraw(ui->label_2); } bool MainWindow::eventFilter(QObject *watched, QEvent *event) { if(event->type() == QEvent::Paint)//繪圖事件 { if(flag == 1)//標誌位為1才在label上繪圖,否者不繪圖 { for(QVector<QLabel *>::iterator it=labels.begin();it!=labels.end();it++) { if(watched == *it)//限制條件,只處理label上的繪圖事件 { myDraw(*it); return true;//返回true表示處理完成該事件,否者該事件還會繼續向下轉發 } } return QMainWindow::eventFilter(watched,event);//其它繪圖事件交給父類處理 } else return QMainWindow::eventFilter(watched,event);//其它繪圖事件交給父類處理 }else return QMainWindow::eventFilter(watched,event); }