Qt組件中的雙緩衝無閃爍繪圖

 

 

雙緩衝繪圖
在Qt4中,全部的窗口部件默認都使用雙緩衝進行繪圖。使用雙緩衝,能夠減輕繪製的閃爍感。在有些狀況下,用戶要關閉雙緩衝,本身管理繪圖。下面的語句設置了窗口部件的Qt::WA_PaintOnScreen屬性 ,就關閉了窗口部件的雙緩衝.
mywidget->setAttribute(Qt::WA_PaintOnScreen);
函數

因爲Qt4再也不提供異或筆,組合模式QPainter::CompostionMode_Xor()並非異或筆,Qt4只提供了QRubberBand實現矩形和直線的繪圖反饋。所以要實如今繪圖中動態
反饋必須使用其餘方法。程序中使用雙環衝來解決這個問題。在繪圖過程當中,一個緩衝區繪製臨時內存,一個緩衝區保存繪製好的內容,最後進行合併。
在交互繪圖過程當中,程序將圖像緩衝區複製到臨時緩衝區,並在臨時緩衝區上繪製,繪製完畢在將結果複製到圖像緩衝區,若是沒有交互複製,則直接將圖像緩衝區繪製顯示到屏幕上。
post

 

Qt組件中的雙緩衝無閃爍繪圖
閃爍首先,要想把閃爍減弱,請設置組件的背景模式爲NoBackground. 
setBackgroundMode(NoBackground);
字體

其次,重載組件的paintEvent()函數,以下改寫: 
void MyWidget::paintEvent(QPaintEvent *e) 

QRect ur=e->rect();//獲得組件尺寸 
QPixmap pix(ur.size());//以此爲參數建立一個位圖變量 
pix.fill(this,ur.topLeft());//填充位圖 
QPainter p(&pic);//以位圖爲參數建立一個QPainter 對象
ui

p.translate(-ur.x(),-ur.y());//在QPainter 上繪畫 
//......//Drawing
this

p.End();//繪畫完畢spa

bitBlt(this,ur.topLeft().&pix);//把位圖貼到組件上.net

//注從qt4開始,bitBlt函數不在使用,取而代之的是drawPixmap。
}
orm

 

 

   // 這是能隨機繪點的關鍵,沒有設置此屬性,默認至關於每次Qt都會完整的將上一次的屏幕擦除,對象

   // 新版的Qt中已經沒有了repaint(bool)接口了。blog

   w.setAttribute(Qt::WA_OpaquePaintEvent);

 

 

老電視機雪花效果中每次都須要擦除重繪避免點的疊加因此一下語句註釋掉

//    w.setAttribute(Qt::WA_OpaquePaintEvent);

 

 

 

(

如下是私有函數的實現:

void Plotter::updateRubberBandRegion()

{

    QRect rect = rubberBandRect.normalized();

    update(rect.left(), rect.top(), rect.width(), 1);
    update(rect.left(), rect.top(), 1, rect.height());
    update(rect.left(), rect.bottom(), rect.width(), 1);
    update(rect.right(), rect.top(), 1, rect.height());
}
函數updateRubberBand()在mousePressEvent(),mouseMoveEvent()和mouseReleaseEvent()中被調用,用來刪除或者從新繪製橡皮線。函數中調用了四次update(),用四個繪製事件完成由橡皮線(兩條垂直和水平的線)組成的四個小矩形的繪製。Qt也提供了一個類QRubberBand用來繪製橡皮線,可是控件本身提供的繪製函數會更好
void Plotter::refreshPixmap()
{
    pixmap = QPixmap(size());
    pixmap.fill(this, 0, 0);
    QPainter painter(&pixmap);
    painter.initFrom(this);
    drawGrid(&painter);
    drawCurves(&painter);
    update();
}
函數refreshPixmap()把plot繪製到圖片上,而且更新顯示。首先咱們把圖片的大小調整爲和當前控件大小相同,而後用控件的背景顏色填充整個圖片。這個顏色是當前調色版的「dark」部分,由於在Plotter構造函數中調用setBackgroundRole() 。若是背景用的刷子是非實心的(solid brush,刷子的樣式,只有顏色,沒有花紋的那種最簡單的),QPixmap::fill()須要知道控件中刷子的偏移量,以便圖片對齊刷子模式。這裏圖片對應整個控件,所以偏移位置爲(0,0)。
接下來咱們建立了一個QPainter對象來繪製圖片,QPainter::initFrom()設置繪製圖片所需畫筆,背景和字體,參數this表示這些設置和Plotter控件的相應設置是一致的。而後咱們調用drawGrid(),drawCurves()繪製網格和曲線。最後,update()函數安排整個控件的繪製事件,在painteEvent()函數中把圖片拷貝到控件上。
void Plotter::drawGrid(QPainter *painter)
{
   QRect rect(Margin, Margin,
               width() - 2 * Margin, height() - 2 * Margin);
    if (!rect.isValid())
        return;
    PlotSettings settings = zoomStack[curZoom];
    QPen quiteDark = palette().dark().color().light();
    QPen light = palette().light().color();
    for (int i = 0; i <= settings.numXTicks; ++i) {
        int x = rect.left() + (i * (rect.width() - 1)
                                 / settings.numXTicks);
        double label = settings.minX + (i * settings.spanX()
                                           / settings.numXTicks);
        painter->setPen(quiteDark);
        painter->drawLine(x, rect.top(), x, rect.bottom());
        painter->setPen(light);
        painter->drawLine(x, rect.bottom(), x, rect.bottom() + 5);
       painter->drawText(x - 50, rect.bottom() + 5, 100, 15,
                          Qt::AlignHCenter | Qt::AlignTop,
                          QString::number(label));
    }
    for (int j = 0; j <= settings.numYTicks; ++j) {
        int y = rect.bottom() - (j * (rect.height() - 1)
                                   / settings.numYTicks);
        double label = settings.minY + (j * settings.spanY()
                                          / settings.numYTicks);
        painter->setPen(quiteDark);
        painter->drawLine(rect.left(), y, rect.right(), y);
        painter->setPen(light);
        painter->drawLine(rect.left() - 5, y, rect.left(), y);
        painter->drawText(rect.left() - Margin, y - 10, Margin - 5, 20,
                          Qt::AlignRight | Qt::AlignVCenter,
                          QString::number(label));
    }
    painter->drawRect(rect.adjusted(0, 0, -1, -1));
}
函數drawGrid()在座標軸和曲線的下面繪製網格。這個區域由一個矩形肯定,若是控件過小,則不繪製當即返回。第一個循環繪製網格的垂直線,及沿x座標軸的刻度。第二個循環繪製網格的水平線,及沿y座標軸的刻度。最後,沿邊界繪製一個矩形。drawText()繪製數字,對應兩個座標軸上刻度的標記。
函數painter->drawText()語法以下:
painter->drawText(x, y, width, height, alignment, text);
其中(x,y,width,height)所肯定的矩形,alignment肯定文字在矩形中的位置。
void Plotter::drawCurves(QPainter *painter)
{
    static const QColor colorForIds[6] = {
        Qt::red, Qt::green, Qt::blue, Qt::cyan, Qt::magenta, Qt::yellow
    };
    PlotSettings settings = zoomStack[curZoom];
    QRect rect(Margin, Margin,
               width() - 2 * Margin, height() - 2 * Margin);
    if (!rect.isValid())
        return;
    painter->setClipRect(rect.adjusted(+1, +1, -1, -1));
    QMapIterator<int, QVector<QPointF> > i(curveMap);
    while (i.hasNext()) {
        i.next();
        int id = i.key();
        const QVector<QPointF> &data = i.value();
        QPolygonF polyline(data.count());
        for (int j = 0; j < data.count(); ++j) {
            double dx = data[j].x() - settings.minX;
            double dy = data[j].y() - settings.minY;
            double x = rect.left() + (dx * (rect.width() - 1)
                                          / settings.spanX());
            double y = rect.bottom() - (dy * (rect.height() - 1)
                                           / settings.spanY());
            polyline[j] = QPointF(x, y);
        }
        painter->setPen(colorForIds[uint(id) % 6]);
        painter->drawPolyline(polyline);
    }
}
函數drawCurves()在網格的上層繪製曲線。調用了QPainter::setClipRect()函數設置QPainter的剪切區域爲包含曲線的矩形區域(不包括四周的間隙和圖片的外框)。QPainter會忽略這個區域外的象素。
而後咱們使用Java風格的迭代器,遍歷全部的曲線,對每一條曲線,遍歷它全部的QPointF點。函數key()獲得曲線的id,value()函數獲得曲線的QVector<QPointF>類型的數據。內層循環把每一個QPointF記錄的plotter座標轉換爲控件座標,把結果保存在polyline變量中。

轉換座標後,咱們設置畫筆的顏色(使用函數前面預約義的顏色),調用drawPolyline()繪製曲線,通過全部的曲線上的點。

 

http://blog.csdn.net/Last_Impression/archive/2008/05/20/2463647.aspx

相關文章
相關標籤/搜索