Przeglądaj źródła

support mouse interaction

xuqiang 1 rok temu
rodzic
commit
ca271a4b33

+ 6 - 0
CMakeLists.txt

@@ -26,6 +26,12 @@ set(PROJECT_SOURCES
     qquickitem/audiowaveform.h
     qquickitem/spectrum.cpp
     qquickitem/spectrum.h
+    qquickitem/qabstractplot.h
+    qquickitem/qabstractplot.cpp
+    qquickitem/spectrumplot.h
+    qquickitem/spectrumplot.cpp
+    qquickitem/waveformplot.h
+    qquickitem/waveformplot.cpp
 )
 
 set(PROJECT_RESOURCES

+ 5 - 0
main.cpp

@@ -5,6 +5,8 @@
 #include "qquickitem/timedomainplot.h"
 #include "qquickitem/audiowaveform.h"
 #include "qquickitem/spectrum.h"
+#include "qquickitem/spectrumplot.h"
+#include "qquickitem/waveformplot.h"
 
 int main(int argc, char *argv[])
 {
@@ -17,6 +19,9 @@ int main(int argc, char *argv[])
     qmlRegisterType<AudioWaveform>("myqml",1,0,"AudioWaveform");
     qmlRegisterType<Spectrum>("myqml",1,0,"Spectrum");
 
+    qmlRegisterType<SpectrumPlot>("myqml",1,0,"SpectrumPlot");
+    qmlRegisterType<WaveformPlot>("myqml",1,0,"WaveformPlot");
+
     QQmlApplicationEngine engine;
 
     const QUrl url(QStringLiteral("qrc:/qml/main.qml"));

+ 121 - 0
qquickitem/qabstractplot.cpp

@@ -0,0 +1,121 @@
+#include "qabstractplot.h"
+
+QAbstractPlot::QAbstractPlot(QQuickItem *parent)
+    : QQuickPaintedItem(parent)
+    , m_pCustomPlot(new QCustomPlot())
+{
+    setFlag(QQuickItem::ItemHasContents, true);
+    setAcceptedMouseButtons(Qt::AllButtons);
+
+    connect(this, &QQuickPaintedItem::widthChanged, this, &QAbstractPlot::onPlotSizeChanged);
+    connect(this, &QQuickPaintedItem::heightChanged, this, &QAbstractPlot::onPlotSizeChanged);
+    connect(m_pCustomPlot, &QCustomPlot::afterReplot,
+            this, &QAbstractPlot::onPlotUpdate, Qt::UniqueConnection);
+}
+
+QAbstractPlot::~QAbstractPlot()
+{
+    delete m_pCustomPlot;
+}
+
+QCustomPlot *QAbstractPlot::customPlot()
+{
+    return m_pCustomPlot;
+}
+
+
+void QAbstractPlot::setOpenGL(bool enabled)
+{
+    if(m_pCustomPlot)
+    {
+        m_pCustomPlot->setOpenGl(true, 4);
+    }
+}
+
+void QAbstractPlot::paint(QPainter *painter)
+{
+    if (!painter || !painter->isActive())
+        return;
+
+    // m_pCustomPlot->setGeometry(0, 0, this->width(), this->height());
+    // painter->drawPixmap(0, 0, this->width(), this->height(), m_pCustomPlot->toPixmap());
+    QPixmap pixmap(boundingRect().size().toSize());
+    QCPPainter qcpPainter(&pixmap);
+    m_pCustomPlot->toPainter(&qcpPainter);
+    painter->drawPixmap(QPoint(), pixmap);
+}
+
+void QAbstractPlot::postMouseEvents(QMouseEvent *event)
+{
+    if(m_pCustomPlot)
+    {
+        QMouseEvent* newEvent = new QMouseEvent(event->type(), event->localPos(),
+                                                event->button(), event->buttons(),
+                                                event->modifiers());
+        QCoreApplication::postEvent(m_pCustomPlot, newEvent);
+    }
+}
+
+void QAbstractPlot::postWheelEvents(QWheelEvent *event)
+{
+    if(m_pCustomPlot)
+    {
+        // QWheelEvent* newEvent = new QWheelEvent(event->pos(),
+        //                                         event->delta(),
+        //                                         event->buttons(),
+        //                                         event->modifiers(),
+        //                                         event->orientation());
+
+        QWheelEvent* newEvent = new QWheelEvent(event->position(), event->globalPosition(),
+                                                event->pixelDelta(), event->angleDelta(),
+                                                event->buttons(), event->modifiers(),
+                                                event->phase(), event->inverted());
+        QCoreApplication::postEvent(m_pCustomPlot, newEvent);
+    }
+}
+
+void QAbstractPlot::mousePressEvent(QMouseEvent *event)
+{
+    this->postMouseEvents(event);
+}
+
+void QAbstractPlot::mouseReleaseEvent(QMouseEvent *event)
+{
+    this->postMouseEvents(event);
+}
+
+void QAbstractPlot::mouseMoveEvent(QMouseEvent *event)
+{
+    this->postMouseEvents(event);
+}
+
+void QAbstractPlot::mouseDoubleClickEvent(QMouseEvent *event)
+{
+    this->postMouseEvents(event);
+}
+
+void QAbstractPlot::wheelEvent(QWheelEvent *event)
+{
+    this->postWheelEvents(event);
+}
+
+void QAbstractPlot::onPlotSizeChanged()
+{
+    m_pCustomPlot->setGeometry(0, 0, (int)width(), (int)height());
+#if 0
+    QCPMarginGroup *marginGroup = new QCPMarginGroup(m_pCustomPlot);
+    m_pCustomPlot->axisRect()->setMarginGroup(QCP::msBottom|QCP::msTop, marginGroup);
+    m_pCustomPlot->axisRect()->setAutoMargins(QCP::msNone);
+    m_pCustomPlot->axisRect()->setMargins(QMargins(0, 0, 0, 0));
+    m_pCustomPlot->plotLayout()->setMargins(QMargins(0, 0, 0, 0));
+#endif
+    m_pCustomPlot->setViewport(QRect(0, 0, (int)width(), (int)height()));
+    m_pCustomPlot->axisRect()->setOuterRect(QRect(0, 0, (int)width(), (int)height()));
+    m_pCustomPlot->axisRect()->setMinimumMargins (QMargins(0, 0, 0, 0));
+    m_pCustomPlot->axisRect()->setMargins(QMargins(0, 0, 0, 0));
+}
+
+void QAbstractPlot::onPlotUpdate()
+{
+    this->update();
+}

+ 37 - 0
qquickitem/qabstractplot.h

@@ -0,0 +1,37 @@
+#ifndef QABSTRACTPLOT_H
+#define QABSTRACTPLOT_H
+
+#include <QQuickPaintedItem>
+#include "thirdparty/qcustomplot/qcustomplot.h"
+
+class QAbstractPlot : public QQuickPaintedItem
+{
+    Q_OBJECT
+public:
+    QAbstractPlot(QQuickItem *parent = nullptr);
+    ~QAbstractPlot();
+
+    QCustomPlot* customPlot();
+
+    void setOpenGL(bool enabled);
+
+protected:
+    virtual void paint(QPainter *painter);
+    virtual void postMouseEvents(QMouseEvent* event);
+    virtual void postWheelEvents(QWheelEvent* event);
+
+    virtual void mousePressEvent(QMouseEvent* event);
+    virtual void mouseReleaseEvent(QMouseEvent* event);
+    virtual void mouseMoveEvent(QMouseEvent* event);
+    virtual void mouseDoubleClickEvent(QMouseEvent* event);
+    virtual void wheelEvent(QWheelEvent *event);
+
+protected:
+    void onPlotSizeChanged();
+    void onPlotUpdate();
+
+private:
+    QCustomPlot* m_pCustomPlot;
+};
+
+#endif // QABSTRACTPLOT_H

+ 90 - 0
qquickitem/spectrumplot.cpp

@@ -0,0 +1,90 @@
+#include "spectrumplot.h"
+
+SpectrumPlot::SpectrumPlot()
+{
+    QCustomPlot* pCustomPlot = this->customPlot();
+    // 创建QCPColorMap
+    m_pColorMap = new QCPColorMap(pCustomPlot->xAxis, pCustomPlot->yAxis);
+    // pCustomPlot->xAxis->setVisible(false);
+    // pCustomPlot->yAxis->setVisible(false);
+    // pCustomPlot->xAxis2->setVisible(false);
+    // pCustomPlot->yAxis2->setVisible(false);
+
+    pCustomPlot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom);
+
+    int nx = 1232;
+    int ny = 128;
+
+    m_pColorMap->data()->setSize(nx, ny); // we want the color map to have nx * ny data points
+    m_pColorMap->data()->setRange(QCPRange(0, 10), QCPRange(0, 8000)); // and span the coordinate range -4..4 in both key (x) and value
+
+    QFile file(":/other/spectrumdata.txt");
+    file.open(QIODevice::ReadOnly);
+    quint64 num = 0;
+
+    for (int xIndex=0; xIndex<nx; ++xIndex)
+    {
+        for (int yIndex=0; yIndex<ny; ++yIndex)
+        {
+            if(file.atEnd())
+                goto end;
+            QByteArray byteArray = file.readLine();
+            QString str = QString(byteArray);
+            str.remove("\n");
+            QJsonDocument doc = QJsonDocument::fromJson(str.toUtf8());
+            QJsonObject obj = doc.object();
+            m_pColorMap->data()->setCell(xIndex, yIndex, obj["s"].toDouble());
+            ++num;
+        }
+    }
+end:
+    qDebug() << "num" << num;
+    file.close();
+
+    this->setGradient();
+    m_pColorMap->rescaleDataRange();
+
+    pCustomPlot->replot();
+    pCustomPlot->rescaleAxes();
+}
+
+SpectrumPlot::~SpectrumPlot()
+{
+    customPlot()->removePlottable(m_pColorMap);
+}
+
+void SpectrumPlot::setGradient()
+{
+    QFile file(":/conf/gradient.json");
+
+    if(file.open(QIODevice::ReadOnly))
+    {
+        QByteArray byteArray = file.readAll();
+        file.close();
+        QJsonParseError jsonError;
+        QJsonDocument doc = QJsonDocument::fromJson(byteArray, &jsonError);
+        if(jsonError.error == QJsonParseError::NoError)
+        {
+            QJsonObject obj = doc.object();
+            QJsonArray array = obj["gradient"].toArray();
+            QJsonObject temp;
+
+            QCPColorGradient customGradient;
+            customGradient.clearColorStops();
+            customGradient.setColorInterpolation(QCPColorGradient::ciRGB);
+            customGradient.setLevelCount(256); // 设置颜色级别的数量
+            for(int i=0;i<array.size();++i)
+            {
+                temp = array.at(i).toObject();
+                customGradient.setColorStopAt(temp["position"].toDouble(), temp["color"].toString());
+            }
+            m_pColorMap->setGradient(customGradient);
+        }
+        else
+            qDebug() << "load gradient conf error";
+
+    }
+    else
+        qDebug() << file.errorString();
+}
+

+ 19 - 0
qquickitem/spectrumplot.h

@@ -0,0 +1,19 @@
+#ifndef SPECTRUMPLOT_H
+#define SPECTRUMPLOT_H
+
+#include "qabstractplot.h"
+
+class SpectrumPlot : public QAbstractPlot
+{
+    Q_OBJECT
+public:
+    SpectrumPlot();
+    ~SpectrumPlot();
+
+private:
+    QCPColorMap* m_pColorMap;
+    void setGradient();
+
+};
+
+#endif // SPECTRUMPLOT_H

+ 157 - 0
qquickitem/waveformplot.cpp

@@ -0,0 +1,157 @@
+#include "waveformplot.h"
+
+#define BITS_DEPTH_16
+
+WaveformPlot::WaveformPlot(QAbstractPlot* parent)
+    : QAbstractPlot(parent)
+{
+    QCustomPlot* pCustomPlot = this->customPlot();
+    m_pAudioWaveformGraph = pCustomPlot->addGraph();
+    m_pAudioWaveformGraph->setPen(QPen(Qt::red));
+
+    pCustomPlot->yAxis->setNumberFormat("gbc");
+    pCustomPlot->yAxis->setNumberPrecision(1);
+    pCustomPlot->yAxis->setRange(-1, 1);
+
+    pCustomPlot->setBackground(QBrush(QColor("#cde8ff")));
+
+    pCustomPlot->xAxis->setVisible(true);
+    pCustomPlot->yAxis->setVisible(true);
+
+    pCustomPlot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom);
+    // pCustomPlot->axisRect()->setRangeZoomFactor(1.1, 1); // 设置缩放系数
+    pCustomPlot->axisRect()->setRangeZoom(Qt::Horizontal);  // 只有水平方向可以缩放
+    pCustomPlot->axisRect()->setRangeDrag(Qt::Horizontal);  // 只有水平方向可以拖动
+
+    this->setOpenGL(true);
+}
+
+void WaveformPlot::paintWaveForm()
+{
+    QCustomPlot* pCustomPlot = this->customPlot();
+    QVector<double> xValue;
+    QVector<double> yValue;
+    QFile file(":/audio/audio1.pcm");
+
+    if(!file.open(QIODevice::ReadOnly))
+    {
+        qDebug() << file.errorString();
+        return;
+    }
+
+    int maxSize = file.size() / 2;
+    // 除以2是多绘制一些点,使波形看起来更加连续
+    int step = maxSize / this->width() / 2;
+
+    if (step < 1)
+        step = 1;
+    else if (step > maxSize)
+        step = maxSize;
+
+    short cur_max = 0;
+    short cur_min = 0;
+    int index_max = 0;
+    int index_min = 0;
+    quint64 index = 0;
+
+#ifdef BITS_DEPTH_8
+    char* buffer = new char[step];
+    while(qint64 size = file.read(buffer, step))
+    {
+
+        cur_max = buffer[0];
+        cur_min = buffer[0];
+        index_max = 0;
+        index_min = 0;
+        for(int i=0;i<size;i++)
+        {
+            //遍历找这一段的最大最小值
+            if (cur_max < buffer[i])
+            {
+                cur_max = buffer[i];
+                index_max = i;
+            }
+            if (cur_min > buffer[i])
+            {
+                cur_min = buffer[i];
+                index_min = i;
+            }
+        }
+        //根据先后顺序存最大最小,相等就存一个
+        if (index_max < index_min)
+        {
+            xValue.push_back(index);
+            yValue.push_back(qreal(uchar(cur_max) - 128) / qreal(128));
+            // m_vector.push_back(QPointF(index, qreal(uchar(cur_max) - 128) / qreal(128)));
+            index++;
+        }
+
+        xValue.push_back(index);
+        yValue.push_back(qreal(uchar(cur_min) - 128) / qreal(128));
+        // m_vector.push_back(QPointF(index, qreal(uchar(cur_min) - 128) / qreal(128)));
+        index++;
+        if (index_max > index_min)
+        {
+            xValue.push_back(index);
+            yValue.push_back(qreal(uchar(cur_max) - 128) / qreal(128));
+            // m_vector.push_back(QPointF(index, qreal(uchar(cur_max) - 128) / qreal(128)));
+            index++;
+        }
+    }
+    delete[] buffer;
+#endif
+
+#ifdef BITS_DEPTH_16
+    char* buffer = new char[step * 2];
+    while(qint64 size = file.read(buffer, step * 2))
+    {
+        short* data = (short*)buffer;
+
+        cur_max = data[0];
+        cur_min = data[0];
+        index_max = 0;
+        index_min = 0;
+
+        for(int i=0;i<size / 2;i++)
+        {
+            //遍历找这一段的最大最小值
+            if (cur_max < data[i])
+            {
+                cur_max = data[i];
+                index_max = i;
+            }
+            if (cur_min > data[i])
+            {
+                cur_min = data[i];
+                index_min = i;
+            }
+        }
+        //根据先后顺序存最大最小,相等就存一个
+        if (index_max < index_min)
+        {
+            xValue.push_back(index);
+            yValue.push_back(cur_max / 32768.0);
+            // m_vector.push_back(QPointF(index, cur_max / 32768.0));
+            index++;
+        }
+        xValue.push_back(index);
+        yValue.push_back(cur_min / 32768.0);
+        // m_vector.push_back(QPointF(index, cur_min / 32768.0));
+        index++;
+        if (index_max > index_min)
+        {
+            xValue.push_back(index);
+            yValue.push_back(cur_max / 32768.0);
+            // m_vector.push_back(QPointF(index, cur_max / 32768.0));
+            index++;
+        }
+
+    }
+    delete[] buffer;
+#endif
+    file.close();
+    m_pAudioWaveformGraph->data().clear();
+    pCustomPlot->xAxis->setRange(0, xValue.size());
+    m_pAudioWaveformGraph->setData(xValue, yValue);
+    this->update();
+}

+ 18 - 0
qquickitem/waveformplot.h

@@ -0,0 +1,18 @@
+#ifndef WAVEFORMPLOT_H
+#define WAVEFORMPLOT_H
+
+#include "qabstractplot.h"
+
+class WaveformPlot : public QAbstractPlot
+{
+    Q_OBJECT
+public:
+    WaveformPlot(QAbstractPlot* parent = nullptr);
+
+    Q_INVOKABLE void paintWaveForm();
+
+private:
+    QCPGraph* m_pAudioWaveformGraph;
+};
+
+#endif // WAVEFORMPLOT_H

+ 16 - 3
resources/qml/main.qml

@@ -5,8 +5,8 @@ import myqml 1.0
 
 Window {
     id: window
-    width: 640
-    height: 480
+    width: 800
+    height: 600
     visible: true
     title: qsTr("Hello World")
 
@@ -42,7 +42,20 @@ Window {
 
         TimeDomainPlot {
             width: window.width
-            height: 200
+            height: 100
+        }
+
+        SpectrumPlot {
+            width: window.width
+            height: 100
+        }
+        WaveformPlot {
+            width: window.width
+            height: 100
+
+            onWidthChanged: {
+                paintWaveForm()
+            }
         }
     }