Qt/C++ 波形绘制双缓冲下改善PaintEvent连续绘制卡顿问题(完整代码解析)

发布于:2024-04-28 ⋅ 阅读:(31) ⋅ 点赞:(0)

  1. 音频波形可视化:该控件用于将音频样本数据可视化为波形,常用于音频处理软件中以展示音频信号的时间域特性。

  2. 动态数据绘制:控件能够响应外部数据的变化并重新绘制波形,适用于实时或动态的音频数据流。

  3. 自定义绘制逻辑:通过Qt的绘图API,特别是QPainterQPainterPath,实现了波形的自定义绘制,包括线条的平滑、颜色渐变以及路径的描绘。

  4. 性能优化:通过双缓冲技术(使用QPixmap)和适当的数据处理,减少了绘制过程中的计算量,提高了渲染效率。

  5. 颜色编码:使用颜色渐变来区分波形的不同振幅区域,比如使用红色到蓝色的渐变表示波形的高低振幅,绿色表示零点线,使得波形的阅读更直观。

  6. 可扩展性:控件为Qt框架下的自定义控件,可以很容易地集成到更大的Qt应用程序中,并根据需求进行定制和扩展。

    
    #ifndef WAVEFORMWIDGET_H
    #define WAVEFORMWIDGET_H
    
    #include <QWidget>
    #include <QPixmap>
    
    class WaveformWidget : public QWidget {
        Q_OBJECT
    
    public:
        explicit WaveformWidget(QWidget *parent = nullptr);
        ~WaveformWidget();
        void setSamples(const QList<float> &newSamples);
    
    protected:
        void paintEvent(QPaintEvent *event) override;
        void resizeEvent(QResizeEvent *event) override;
    
    private:
        QList<float> samples; // 用于存储音频样本的列表
        QPixmap buffer; // 双缓冲的画布
        void redrawBuffer(); // 在画布上重新绘制波形
    };
    
    #endif // WAVEFORMWIDGET_H
    #include "WaveformWidget.h"
    #include <QPainter>
    #include <QResizeEvent>
    
    WaveformWidget::WaveformWidget(QWidget *parent)
        : QWidget(parent) {
        // 初始化双缓冲画布
        buffer = QPixmap(size());
        buffer.fill(Qt::black);
    }
    
    WaveformWidget::~WaveformWidget() {
    }
    
    void WaveformWidget::setSamples(const QList<float> &newSamples) {
        samples = newSamples;
        redrawBuffer(); // 更新画布
    }
    
    void WaveformWidget::paintEvent(QPaintEvent *event) {
        QPainter painter(this);
        painter.drawPixmap(0, 0, buffer);
    }
    
    void WaveformWidget::resizeEvent(QResizeEvent *event) {
        // 调整画布大小并重新绘制
        buffer = QPixmap(event->size());
        buffer.fill(Qt::black);
        redrawBuffer();
    }
    
    void WaveformWidget::redrawBuffer() {
        // 确保画布准备好
        if (buffer.size() != size()) {
            buffer = QPixmap(size());
        }
    
        QPainter painter(&buffer);
        painter.setRenderHint(QPainter::Antialiasing);
    
        // 重置画布背景
        buffer.fill(Qt::black);
    
        if (samples.isEmpty()) return;
    
        const int middleY = buffer.height() / 2;
        const int upperLimit = middleY / 2;  // -50 to 50 对应的 Y 坐标
        const int lowerLimit = middleY + upperLimit;
    
        // 创建路径和描边路径
        QPainterPath path, strokePath;
        path.moveTo(0, middleY);
    
        qreal xStep = static_cast<qreal>(buffer.width()) / (samples.size() - 1);
    
        // 根据样本数据构造波形路径
        for (int i = 0; i < samples.size(); ++i) {
            qreal x = i * xStep;
            qreal y = middleY - (samples.at(i) * middleY / 100.0);
            path.lineTo(x, y);
            strokePath.lineTo(x, y);
        }
    
        // 绘制渐变
        QLinearGradient gradient(0, 0, 0, buffer.height());
        gradient.setColorAt(0.0, QColor(255, 0, 0));    // 顶部为红色
        gradient.setColorAt(0.25, QColor(0, 0, 255));   // 中部为蓝色
        gradient.setColorAt(0.5, Qt::transparent);      // 中间透明
        gradient.setColorAt(0.75, QColor(0, 0, 255));   // 中部为蓝色
        gradient.setColorAt(1.0, QColor(255, 0, 0));    // 底部为红色
    
        QPen pen(gradient, 2);
        painter.setPen(pen);
        painter.drawPath(strokePath);
    
        // 绘制中间的零点线
        QPen centerLinePen(Qt::green);
        centerLinePen.setWidth(1);
        painter.setPen(centerLinePen);
        painter.drawLine(0, middleY, buffer.width(), middleY);
    
        // 请求更新控件
        update();
    }


网站公告

今日签到

点亮在社区的每一天
去签到