Qt中的一种异步绘制方案

一 设计思路

Qt绘制控件时,会在主线程调用paintEvent方法,将图像渲染到屏幕上。但是我们有时需要绘制复杂的图形,此时再在主线程中进行绘制,就会造成界面卡顿。有一种优化思路是,在其他线程上面做离屏渲染,主线程将离屏渲染的结果渲染到屏幕上面。

二 示例

meter.h

#pragma once

#include <QtGui>
#include <QFrame>
#include <QImage>

class CMeter : public QFrame
{
    Q_OBJECT

public:
    explicit CMeter(QWidget *parent = 0);
    explicit CMeter(float min_level = -100.0, float max_level = 10.0,
                    QWidget *parent = 0);
    ~CMeter();

    QSize minimumSizeHint() const;
    QSize sizeHint() const;

    void setMin(float min_level);
    void setMax(float max_level);
    void setRange(float min_level, float max_level);

    void draw();
    void UpdateOverlay(){DrawOverlay();}

public slots:
    void setLevel(float dbfs);
    void setSqlLevel(float dbfs);

protected:
    void paintEvent(QPaintEvent *event);
    void resizeEvent(QResizeEvent* event);

private:
    void DrawOverlay();

    QFont   m_Font;
    QPixmap m_2DPixmap;
    QPixmap m_OverlayPixmap;
    QSize   m_Size;
    QString m_Str;
    qreal   m_pixperdb;     // pixels / dB
    qreal   m_Siglevel;
    float   m_dBFS;
    qreal   m_Sql;
    qreal   m_SqlLevel;
};

meter.cpp

#include <cmath>
#include <QDebug>
#include "meter.h"

// ratio to total control width or height
#define CTRL_MARGIN 0.07		// left/right margin
#define CTRL_MAJOR_START 0.3	// top of major tic line
#define CTRL_MINOR_START 0.3	// top of minor tic line
#define CTRL_XAXIS_HEGHT 0.4	// vertical position of horizontal axis
#define CTRL_NEEDLE_TOP 0.4		// vertical position of top of needle triangle

#define MIN_DB -100.0f
#define MAX_DB +0.0f

#define ALPHA_DECAY     0.25f
#define ALPHA_RISE      0.70f

CMeter::CMeter(QWidget *parent) : QFrame(parent)
{
    setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    setFocusPolicy(Qt::StrongFocus);
    setAttribute(Qt::WA_PaintOnScreen,false);
    setAutoFillBackground(false);
    setAttribute(Qt::WA_OpaquePaintEvent, false);
    setAttribute(Qt::WA_NoSystemBackground, true);
    setMouseTracking(true);

    m_Font = QFont("Arial");
    m_Font.setWeight(QFont::Normal);
    m_2DPixmap = QPixmap(0,0);
    m_OverlayPixmap = QPixmap(0,0);
    m_Size = QSize(0,0);
    m_pixperdb = 0.0;
    m_Siglevel = 0;
    m_dBFS = MIN_DB;
    m_Sql = -150.0;
    m_SqlLevel = 0.0;
}

CMeter::~CMeter()
{
}

QSize CMeter::minimumSizeHint() const
{
    return QSize(20, 10);
}

QSize CMeter::sizeHint() const
{
    return QSize(100, 30);
}

void CMeter::resizeEvent(QResizeEvent *)
{
    if (!size().isValid())
        return;

    if (m_Size != size())
    {
        // if size changed, resize pixmaps to new screensize
        m_Size = size();
        qreal dpr = devicePixelRatioF();
        m_OverlayPixmap = QPixmap(m_Size.width() * dpr, m_Size.height() * dpr);
        m_OverlayPixmap.setDevicePixelRatio(dpr);
        m_OverlayPixmap.fill(Qt::black);
        m_2DPixmap = QPixmap(m_Size.width() * dpr, m_Size.height() * dpr);
        m_2DPixmap.setDevicePixelRatio(dpr);
        m_2DPixmap.fill(Qt::black);

        qreal w = (m_2DPixmap.width() / dpr) - 2 * CTRL_MARGIN * (m_2DPixmap.width() / dpr);
        m_pixperdb = w / fabs((double)(MAX_DB - MIN_DB));
        setSqlLevel(m_Sql);
    }

    DrawOverlay();
    draw();
}

void CMeter::setLevel(float dbfs)
{
    if (dbfs < MIN_DB)
        dbfs = MIN_DB;
    else if (dbfs > MAX_DB)
        dbfs = MAX_DB;

    float level = m_dBFS;
    float alpha  = dbfs < level ? ALPHA_DECAY : ALPHA_RISE;
    m_dBFS -= alpha * (level - dbfs);
    m_Siglevel = (qreal)(level - MIN_DB) * m_pixperdb;

    draw();
}

void CMeter::setSqlLevel(float dbfs)
{
    if (dbfs >= 0.f)
        m_SqlLevel = 0.0;
    else
        m_SqlLevel = (qreal)(dbfs - MIN_DB) * m_pixperdb;

    if (m_SqlLevel < 0.0)
        m_SqlLevel = 0.0;

    m_Sql = (qreal)dbfs;
}

// Called by QT when screen needs to be redrawn
void CMeter::paintEvent(QPaintEvent *)
{
    QPainter painter(this);

    painter.drawPixmap(0, 0, m_2DPixmap);
    return;
}

// Called to update s-meter data for displaying on the screen
void CMeter::draw()
{
    int w;
    int h;

    if (m_2DPixmap.isNull())
        return;

    // get/draw the 2D spectrum
    w = m_2DPixmap.width() / m_2DPixmap.devicePixelRatioF();
    h = m_2DPixmap.height() / m_2DPixmap.devicePixelRatioF();

    // first copy into 2Dbitmap the overlay bitmap.
    m_2DPixmap = m_OverlayPixmap.copy(0, 0, m_OverlayPixmap.width(), m_OverlayPixmap.height());
    QPainter painter(&m_2DPixmap);

    // DrawCurrent position indicator
    qreal hline = (qreal) h * CTRL_XAXIS_HEGHT;
    qreal marg = (qreal) w * CTRL_MARGIN;
    qreal ht = (qreal) h * CTRL_NEEDLE_TOP;
    qreal x = marg + m_Siglevel;

    if (m_Siglevel > 0.0)
    {
        QColor color(0, 190, 0, 255);
        QPen pen(color);
        pen.setJoinStyle(Qt::MiterJoin);
        painter.setPen(pen);
        painter.setBrush(QBrush(color));
        painter.setOpacity(1.0);

        painter.drawRect(QRectF(marg, ht + 2, x - marg, 4));
    }

    if (m_SqlLevel > 0.0)
    {
        x = marg + m_SqlLevel;
        painter.setPen(QPen(Qt::yellow, 1, Qt::SolidLine));
        painter.drawLine(QLineF(x, hline, x, hline + 8));
    }

    int y = (h) / 4;
    m_Font.setPixelSize(y);
    painter.setFont(m_Font);

    painter.setPen(QColor(0xDA, 0xDA, 0xDA, 0xFF));
    painter.setOpacity(1.0);
    m_Str.setNum(m_dBFS, 'f', 1);
    painter.drawText(marg, h - 2, m_Str + " dBFS" );

    update();
}

// Called to draw an overlay bitmap containing items that
// does not need to be recreated every fft data update.
void CMeter::DrawOverlay()
{
    if (m_OverlayPixmap.isNull())
        return;

    int w = m_OverlayPixmap.width() / m_OverlayPixmap.devicePixelRatioF();
    int h = m_OverlayPixmap.height() / m_OverlayPixmap.devicePixelRatioF();
    int x,y;
    QRect rect;
    QPainter painter(&m_OverlayPixmap);

    m_OverlayPixmap.fill(QColor(0x1F, 0x1D, 0x1D, 0xFF));

    // Draw scale lines
    qreal marg = (qreal) w * CTRL_MARGIN;
    qreal hline = (qreal)h * CTRL_XAXIS_HEGHT;
    qreal magstart = (qreal) h * CTRL_MAJOR_START;
    qreal minstart = (qreal) h * CTRL_MINOR_START;
    qreal hstop = (qreal) w - marg;
    painter.setPen(QPen(Qt::white, 1, Qt::SolidLine));
    painter.drawLine(QLineF(marg, hline, hstop, hline));        // top line
    painter.drawLine(QLineF(marg, hline+8, hstop, hline+8));    // bottom line
    qreal xpos = marg;
    for (x = 0; x < 11; x++) {
        if (x & 1)
            //minor tics
            painter.drawLine(QLineF(xpos, minstart, xpos, hline));
        else
            painter.drawLine(QLineF(xpos, magstart, xpos, hline));
        xpos += (hstop-marg) / 10.0;
    }

    // draw scale text
    y = h / 4;
    m_Font.setPixelSize(y);
    painter.setFont(m_Font);
    int rwidth = (int)((hstop - marg) / 5.0);
    m_Str = "-100";
    rect.setRect(marg / 2 - 5, 0, rwidth, magstart);

    for (x = MIN_DB; x <= MAX_DB; x += 20)
    {
        m_Str.setNum(x);
        painter.drawText(rect, Qt::AlignHCenter|Qt::AlignVCenter, m_Str);
        rect.translate(rwidth, 0);
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值