Qt 双缓冲绘图

From:http://hi.baidu.com/lvlook/item/972abd3f50c91280b611db1e

在Qt4中,所有的窗口部件默认都使用双缓冲进行绘图。使用双缓冲,可以减轻绘制的闪烁感。在有些情况下,用户要关闭双缓冲,自己管理绘图。下面的语句设置了窗口部件的Qt::WA_PaintOnScreen属性 ,就关闭了窗口部件的双缓冲.
mywidget->setAttribute(Qt::WA_PaintOnScreen);

由于Qt4不再提供异或笔,组合模式QPainter::CompostionMode_Xor()并不是异或笔,Qt4只提供了QRubberBand实现矩形和直线的绘图反馈。因此要实现在绘图中动态
反馈必须使用其他方法。程序中使用双环冲来解决这个问题。在绘图过程中,一个缓冲区绘制临时内存,一个缓冲区保存绘制好的内容,最后进行合并。
在交互绘图过程中,程序将图像缓冲区复制到临时缓冲区,并在临时缓冲区上绘制,绘制完毕在将结果复制到图像缓冲区,如果没有交互复制,则直接将图像缓冲区绘制显示到屏幕上。

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 对象

p.translate(-ur.x(),-ur.y());//在QPainter 上绘画
//......//Drawing

p.End();//绘画完毕

bitBlt(this,ur.topLeft().&pix);//把位图贴到组件上

//注从qt4开始,bitBlt函数不在使用,取而代之的是drawPixmap。
}

   // 这是能随机绘点的关键,没有设置此属性,默认相当于每次Qt都会完整的将上一次的屏幕擦除,

   // 新版的Qt中已经没有了repaint(bool)接口了。

   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()绘制曲线,经过所有的曲线上的点。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在 Qt C++ 中使用双缓冲技术进行绘图,可以按照以下步骤进行操作: 1. 创建一个自定义的 QWidget 子类,例如 MyPaintWidget,用于绘制图形。 ```cpp class MyPaintWidget : public QWidget { Q_OBJECT public: explicit MyPaintWidget(QWidget *parent = nullptr); protected: void paintEvent(QPaintEvent *event) override; private: QImage buffer; // 双缓冲区 }; MyPaintWidget::MyPaintWidget(QWidget *parent) : QWidget(parent) { // 设置窗口属性,启用双缓冲绘制 setAttribute(Qt::WA_PaintOnScreen); setAttribute(Qt::WA_NoSystemBackground); } void MyPaintWidget::paintEvent(QPaintEvent *event) { // 创建绘图对象,并将其绑定到双缓冲区 QPainter painter(&buffer); // 在双缓冲区上进行绘制 painter.fillRect(rect(), Qt::white); // 绘制白色背景 painter.setPen(Qt::black); // 设置画笔颜色为黑色 painter.drawLine(0, 0, width(), height()); // 绘制一条线段 // 将双缓冲区的内容绘制到窗口上 painter.begin(this); painter.drawImage(0, 0, buffer); painter.end(); } ``` 2. 在主窗口的构造函数中创建 MyPaintWidget 对象,并将其添加到布局中。 ```cpp MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { MyPaintWidget *paintWidget = new MyPaintWidget(this); setCentralWidget(paintWidget); } ``` 通过以上步骤,我们创建了一个自定义的 QWidget 子类,并在其中实现双缓冲绘图的功能。在 paintEvent 函数中,首先将绘图操作绘制在双缓冲区(buffer)上,然后再将双缓冲区的内容绘制到窗口上。这样可以避免图形闪烁的问题,并提高绘图的效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值