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

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

转载于:https://www.cnblogs.com/lvdongjie/p/5393411.html

Qt组件中的双缓冲无闪烁绘图相关推荐

  1. C++零食:WTL中使用双缓冲避免闪烁

    双缓冲的原理可以这样形象的理解:把电脑屏幕看作一块黑板.首先我们在内存环境中建立一个"虚拟"的黑板,然后在这块黑板上绘制复杂的图形,等图形全部绘制完毕的时候,再一次性的把内存中绘制 ...

  2. VC绘图中的双缓冲技术

    VC绘图中的双缓冲技术 转自:VC 绘图,使用双缓冲技术实现 ********************所有的GDI绘图函数使用的都是逻辑坐标(逻辑范围)******************* **** ...

  3. VC中的双缓冲绘图技术

    之前在做图形绘制的时候,发现在拖动图形时候,会出现闪烁的情况,后来就上网找了一下双缓冲绘图,本文非原创,只是想保存下来,以后要用的时候不用再到处去搜,也希望能帮助有这方面困惑的朋友 原文来自http: ...

  4. java 坦克重叠_【Java】我所知道坦克大战(单机版)之画出坦克的实心圆、让圆动起来、双缓冲解决闪烁问题...

    28640发布于 今天 02:47 本篇文章目的 画出坦克的实心圆 让坦克圆动起来 双缓冲解决闪烁问题 一.画出代表坦克的实心圆 我们需要画出一个圆,那么可以使用fillOval方法 fillOval ...

  5. Winform中的双缓冲

    (1)在应用程序中使用双缓冲的最简便的方法是使用 .NET Framework 为窗体和控件提供的默认双缓冲.通过将 DoubleBuffered 属性设置为 true. this.DoubleBuf ...

  6. Qt之旅---10 双缓冲绘图

    1 基本知识 但我们使用painter进行快速绘图时会看到类似图下 的情况 双缓冲绘图技术 的原理 用两个画布进行绘图,一个用于显示,一个用于绘制,也就是将图画之后再显示 就可以避免上面的情况. 2 ...

  7. C语言编程闪屏,双缓冲解决VC++绘图时屏幕闪烁

    通常来说程序根据需要调用Invalidate(FALSE)使窗口客户区无效引起重绘,然后在窗口OnPaint函数(基于文档视图的程序则是OnDraw)中进行稳定绘图就行了.但是,我们在OnPaint中 ...

  8. Android中实现双缓冲(画板应用)和XML文件定义菜单

    1.什么是双缓冲技术?双缓冲技术就是当用户操作界面完成后,会有一个缓冲区保存用户操作的结果. 为什么要使用双缓冲技术?拿Android 游戏开发来说,界面贞每次都是全部重画的,也就说画了新的,旧的就没 ...

  9. 控制台双缓冲防闪烁--功能封装

    使用时,将包括#define DATALEN 10000的语句以及整个类.全局变量scrbuf的定义都拷贝到你的代码文件中. 默认不开启双缓冲. 调用scrbuf.on()开始双缓冲模式 此模式下,输 ...

最新文章

  1. Python组织文件 实践:查找大文件、 用Mb、kb显示文件尺寸 、计算程序运行时间...
  2. webform登录操作中正则表达式运用
  3. jl1.如何设置元素的宽高包含元素的边框和内边距
  4. linux 启动,停止mysql
  5. 鲲鹏支持java_#化鲲为鹏,我有话说# 鲲鹏弹性云服务器配置 JAVA 环境
  6. C++11线程库模板
  7. GitHub Actions 持续集成
  8. Zookeeper的Leader选举
  9. Java编程:中缀表达式转后缀表达式
  10. 玩大了,开源协议修改引发MongoDB“大动荡”?
  11. 【风速预测】基于matlab EMD+模拟退火算法优化DBN风速预测【含Matlab源码 JQ003期】
  12. Android 基础控件与线性布局
  13. 程序员的自我修养(雾)
  14. Win7怎么设置工作组?Win7电脑设置工作组的方法
  15. 华为“鸿蒙”所涉及的微内核到底是什么?一文带你认识微内核
  16. 【沧海拾昧】微机原理:8086/8088中断系统
  17. mysql numeric 区别_Mysql中NUMERIC和DECIMAL类型区别比较
  18. python_模块_包
  19. java有substr方法_java substring和substr
  20. Flutter中PlatformView组件无法刷新的问题

热门文章

  1. TensorFlow中Variable()和get_variable()
  2. python判断列表为空的三种方法
  3. 【人工智能中“预测”的知识点】
  4. 拉氏变换法求解线性常微分方程(系统的零状态响应)
  5. Matlab--Figure界面工具栏使用简要说明
  6. 使你的C/C++代码支持Unicode
  7. Delphi格式化函数Format FormatDateTime和FormatFloat
  8. 滑动窗口的最小值问题
  9. 贪心法——区间覆盖问题
  10. 3D 机器视觉 01 - 格雷码在3D视觉中的运用和码位选择原则