天天看點

學習QT之雙緩沖機制

所謂雙緩沖機制,是指在繪制控件時,首先将要繪制的内容繪制在一個圖檔中,再将圖檔一次性繪制到控件上。在早期的Qt版本中,若直接在控件上進行繪制工作,則在控件重繪時會産生閃爍的現象,控件重繪頻繁時,閃爍尤為明顯。雙緩沖機制可以有效地消除這種閃爍現象。自Qt 5版本之後,​

​QWidget​

​控件已經能夠自動處理閃爍的問題。是以,在控件上直接繪圖時,不用再操心顯示的閃爍問題,但雙緩沖機制在很多場合仍然有其用武之地。當所需繪制的内容較複雜并需要頻繁重新整理,或者每次隻需要重新整理整個控件的一小部分時,仍應盡量采用雙緩沖機制。

下面通過一個執行個體來示範雙緩沖機制:

一、運作結果

學習QT之雙緩沖機制

二、具體代碼

​drawwidget.h​

#ifndef DRAWWIDGET_H
#define DRAWWIDGET_H

#include <QWidget>
#include <QtGui>
#include <QMouseEvent>
#include <QPaintEvent>
#include <QResizeEvent>
#include <QColor>
#include <QPixmap>
#include <QPoint>
#include <QPainter>
#include <QPalette>

class DrawWidget : public QWidget
{
    Q_OBJECT
public:
    explicit DrawWidget(QWidget *parent = 0);
    void mousePressEvent(QMouseEvent *);  //重寫滑鼠按下事件
    void mouseMoveEvent(QMouseEvent *);   //重寫滑鼠移動事件
    void paintEvent(QPaintEvent *);       //重寫繪圖事件
    void resizeEvent(QResizeEvent *);     //重寫窗體大小變化事件

signals:

public slots:
    void setStyle(int);
    void setWidth(int);
    void setColor(QColor);
    void clear();

private:
    QPixmap *pix;
    QPoint startPos;
    QPoint endPos;
    int style;
    int weight;
    QColor color;
};

#endif // DRAWWIDGET_H      

​drawwidget.cpp​

#include "drawwidget.h"
#include <QtGui>
#include <QPen>

DrawWidget::DrawWidget(QWidget *parent) : QWidget(parent)
{
    setAutoFillBackground(true);
    setPalette(QPalette(Qt::white));
    pix = new QPixmap(size());  //擷取目前窗體的大小建立一個QPixmap對象
    pix->fill(Qt::white);       //将其填充為白色
    setMinimumSize(60,400);     //設定窗體的最小值
}

void DrawWidget::setStyle(int s)
{
    style = s;
}

void DrawWidget::setWidth(int w)
{
    weight = w;
}

void DrawWidget::setColor(QColor c)
{
    color = c;
}

void DrawWidget::mousePressEvent(QMouseEvent *event)
{
    startPos = event->pos();
}

void DrawWidget::mouseMoveEvent(QMouseEvent *event)  //注(1)
{
    QPainter *painter = new QPainter;
    QPen pen;
    pen.setStyle((Qt::PenStyle)style);
    pen.setWidth(weight);
    pen.setColor(color);
    painter->begin(pix);  //注(2)
    painter->setPen(pen);
    painter->drawLine(startPos,event->pos());
    painter->end();
    startPos = event->pos();
    update();
}

void DrawWidget::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    painter.drawPixmap(QPoint(0,0),*pix);
}

void DrawWidget::resizeEvent(QResizeEvent *evnet)  //注(3)
{
    if(height() > pix->height() || width() > pix->width())
    {
        QPixmap *newPix = new QPixmap(size());  //建立一個新的QPixmap對象
        newPix->fill(Qt::white);  //填充QPixmap對象newPix的顔色為白色背景色
        QPainter painter(newPix);
        painter.drawPixmap(QPoint(0,0),*pix);  //在newPix中繪制原pix中的内容
        pix = newPix;  //将newPix指派給pix作為新的繪制圖形接收對象
    }
    QWidget::resizeEvent(evnet);  //完成其餘的工作
}

void DrawWidget::clear()  //以目前視窗大小建立一個QPixmap對象,然後更新pix,重繪
{
    QPixmap *clearPix = new QPixmap(size());
    clearPix->fill(Qt::white);
    pix = clearPix;
    update();
}      

注:以上類是繼承至QWidget,将其設為QMainWindow的中心區域作為畫闆

注(1):重定義滑鼠移動事件​

​mouseMoveEvent()​

​​,滑鼠移動事件在預設情況下,在滑鼠按鍵被按下的同時拖拽滑鼠時被觸發。QWidget的​

​mouseTracking​

​​屬性訓示是否追蹤滑鼠,預設為false(不追蹤),即在至少有一個滑鼠按鍵被按下的前提下移動滑鼠才觸發​

​mouseMoveEvent()​

​​事件,可以通過​

​setMouseTracking(bool enable)​

​​方法對該屬性值進行設定。如果設定為追蹤,則無論滑鼠按鍵是否按下,隻要滑鼠移動,就會觸發​

​mouseMoveEvent()​

​事件。

注(2):​

​painter->begin(pix)、painter->end()​

​​:以QPixmap對象為QPaintDevice參數繪制。在構造一個QPainter對象時,就立即開始對繪畫設定進行繪制。此構造QPainter對象是短時期的,如應定義在​

​QWidget::paintEvent()​

​​中,并隻能調用一次。此構造函數調用開始與​

​begin()​

​​函數,并且在QPainter的析構函數中自動調用​

​end()​

​​函數。由于當一個QPainter對象的初始化失敗是構造函數不能提供回報資訊,是以在繪制外部裝置時應使用​

​begin()​

​​和​

​end()​

​函數,如列印機等外部裝置。

下面是使用​

​begin()​

​​和​

​end()​

​的一個例子

void MyWidget::paintEvent(QPaintEvent *)
{
    QPainter p;
    p.begin(this);
    p.drawLine(...);
    p.end();
}      

類似于下面的形式:

void MyWidget::paintEvent(QPaintEvent *)
{
    QPainter p(this);    
    p.drawLine(...);
}      

注(3):調整繪制區大小函數​

​resizeEvent()​

​,當窗體的大小發送改變是,效果看起來雖然像是繪制區大小改變了,但實際能夠進行繪制的區域仍然沒有改變。因為繪圖的大小并沒有改變,還是原來繪制區窗體的大小,是以在窗體尺寸改變時應及時調整用于繪制的QPixmap對象的大小。

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QToolButton>
#include <QLabel>
#include <QComboBox>
#include <QSpinBox>
#include "drawwidget.h"

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = 0);
    ~MainWindow();
    void createToolBar();

public slots:
    void ShowStyle();
    void ShowColor();

private:
    DrawWidget *drawWidget;
    QLabel *styleLabel;
    QComboBox *styleComboBox;
    QLabel *widthLabel;
    QSpinBox *widthSpinBox;
    QToolButton *colorBtn;
    QToolButton *clearBtn;
};

#endif // MAINWINDOW_H      
#include "mainwindow.h"
#include <QToolBar>
#include <QColorDialog>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    drawWidget = new DrawWidget;
    setCentralWidget(drawWidget);
    createToolBar();
    setMinimumSize(600,400);
    ShowStyle();
    drawWidget->setWidth(widthSpinBox->value());
    drawWidget->setColor(Qt::black);
}

MainWindow::~MainWindow()
{

}

void MainWindow::createToolBar()
{
    QToolBar *toolBar = addToolBar("Tool");
    styleLabel = new QLabel(tr("線型風格"));
    styleComboBox = new QComboBox;
    styleComboBox->addItem(tr("SolodLine"),static_cast<int>(Qt::SolidLine));
    styleComboBox->addItem(tr("DashLine"),static_cast<int>(Qt::DashLine));
    styleComboBox->addItem(tr("DotLine"),static_cast<int>(Qt::DotLine));
    styleComboBox->addItem(tr("DashDotLine"),static_cast<int>(Qt::DashDotLine));
    styleComboBox->addItem(tr("DashDotDotLine"),static_cast<int>(Qt::DashDotDotLine));
    connect(styleComboBox,SIGNAL(activated(int)),this,SLOT(ShowStyle()));
    widthLabel = new QLabel(tr("線寬"));
    widthSpinBox = new QSpinBox;
    connect(widthSpinBox,SIGNAL(valueChanged(int)),drawWidget,SLOT(setWidth(int)));
    colorBtn = new QToolButton;
    QPixmap pixmap(20,20);
    pixmap.fill(Qt::black);
    colorBtn->setIcon(QIcon(pixmap));
    connect(colorBtn,SIGNAL(clicked(bool)),this,SLOT(ShowColor()));
    clearBtn = new QToolButton();
    clearBtn->setText(tr("清除"));
    connect(clearBtn,SIGNAL(clicked(bool)),drawWidget,SLOT(clear()));
    toolBar->addWidget(styleLabel);
    toolBar->addWidget(styleComboBox);
    toolBar->addWidget(widthLabel);
    toolBar->addWidget(widthSpinBox);
    toolBar->addWidget(colorBtn);
    toolBar->addWidget(clearBtn);
}

void MainWindow::ShowStyle()
{
    drawWidget->setStyle(styleComboBox->itemData(styleComboBox->currentIndex(),Qt::UserRole).toInt());
}

void MainWindow::ShowColor()
{
    QColor color = QColorDialog::getColor(static_cast<int>(Qt::black),this);
    if(color.isValid())
    {
        drawWidget->setColor(color);
        QPixmap p(20,20);
        p.fill(color);
        colorBtn->setIcon(QIcon(p));
    }
}