Qt中利用Qwt画动态正弦曲线,并可动态调整振幅和周期

本文介绍如何使用Qt和Qwt库实现实时动态显示正弦曲线并调整其振幅和周期的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言:

这里主要是做出了一个动态的正弦曲线,并可以调整正弦曲线的振幅和周期。网上有很多画动态正弦曲线的,有一些是通过绘制一整个正弦图像,然后每一次显示都是刷新一整个正弦曲线来达到动态的效果,但是有时候需要的效果可能不是这样。

这里的正弦曲线主要是通过在数据容器中每次追加一个曲线点来达到动态的效果。当当前的坐标轴已经画满了曲线,那么就更新当前的坐标系,将当前坐标轴往前移动一个单位( x 轴 坐标 +1),这样整个坐标系就会看到往前移动了,显示出动态的效果。而动态地改变周期和振幅,则是通过按钮来改变,当点击一下对应的按钮时,就根据当前的振幅和周期,重新生成正弦曲线点,然后把改变前的数据擦除,重新追加新的数据进数据容器中,再重新绘图显示,这样就能改变图像的周期和振幅了。


下面是效果图:

这里写图片描述

一、准备好调整Sin曲线的接口

首先给出产生正弦曲线数据点的代码,这里的代码我在linux上用纯C写,把它编译成库,供Qt调用

/* SinData.h */
#ifndef SINDATA_H
#define SINDATA_H

#include <stdio.h>
#include <math.h>
#include <pthread.h>
#include <unistd.h>

void StartSin();             //启动线程
int  GetSinData(int *AxisX,int *AxisY);   //获取Sin坐标点
void SetAmplitude(int Amplitude);   // 设置振幅
void SetCycle(int Cycle);           // 设置周期
void SetSinOffset( int SinOffset);  // 设置Y轴偏移量
void UpdateSinData(int AxisX[],int AxisY[]); // 更新Sin数据

#endif

/* SinData.c */
#include "SinData.h"
#define PI 3.1415926
#define BUFFER_SIZE 1024

int g_Buffer[BUFFER_SIZE];
int g_BufferIndex;
int g_AxisX;       
int g_AxisY;
int g_AxisIndex;
int g_Amplitude = 25;
int g_Cycle = 6;
int g_SinOffset = 135;

/*
Function Name   :    SetAmplitude
Description :        设置振幅
Input Parameters:    Amplitude :振幅值
*/
void SetAmplitude(int Amplitude)
{
    g_Amplitude =  Amplitude;
}

/*
Function Name   :    SetCycle
Description :        设置周期
Input Parameters:    Cycle :周期
*/
void SetCycle(int Cycle)
{
    g_Cycle = Cycle;
}

/*
Function Name   :    SetSinOffset
Description :        设置偏移量
Input Parameters:    SinOffset :y轴的偏移量
*/
void SetSinOffset( int SinOffset)
{
    g_SinOffset = SinOffset;
}


/*
Function Name:   RecvData
Description  :   设置完参数后,用来重新生成数据,更新Sin图像
*/
void UpdateSinData(int AxisX[],int AxisY[])
{

    int m;
    for(m = 0;  m <g_AxisIndex ; m++)
    {
        AxisX[m] =m;
        AxisY[m] = g_Amplitude * sin( PI / g_Cycle * (AxisX[m])) + g_SinOffset;     
    }
}


/*
Function Name:   void DrawSin
Description  :   产生sin图像的坐标
*/
void DrawSin()
{
    g_AxisY = g_Amplitude * sin( PI /  g_Cycle *  g_AxisIndex )+ g_SinOffset;
    g_AxisX = g_AxisIndex;
    g_AxisIndex++;
}


/*
Function Name   :    GetSinData
Description     :    获取sin图像的坐标,供外部调用
Input Parameters:   AxisX :x轴坐标   AxisY:y轴坐标
Return Value    :   1 :成功获取 
                     0 :获取失败(接收数据的缓冲区没满)
                    -1:获取失败
*/
int GetSinData(int *AxisX,int *AxisY)
{
    if( g_BufferIndex  == BUFFER_SIZE)
    {
        DrawSin();
        *AxisX = g_AxisX;
        *AxisY = g_AxisY ; 
        static int s_Tick = 0;
        s_Tick++;
        g_BufferIndex = 0;
        return 1;
    }

    return 0;
}

/*
Function Name:   RecvData
Description  :   模仿接收1K的数据
*/
void RecvData()
{
    while(1)
    {
        if(g_BufferIndex == BUFFER_SIZE)
            return;
        g_Buffer[g_BufferIndex] = g_BufferIndex ;
            g_BufferIndex++;    

    }   
}

void *RecvDataPthread(void *arg)
{
    while(1)
    {
        RecvData();
    }
}

/*
Function Name   :    StartSin
Description     :    启动线程,不停地获取数据
*/

void StartSin()
{
    pthread_t pid;
    pthread_create(&pid,NULL,RecvDataPthread,NULL);
}

以上代码只是用来产生Sin曲线的数据点,并提供接口去改变Sin曲线的振幅和周期,这里我是在linux下用C语言编写的,把它编译成So库,供Qt调用。

gcc SinData.c -o SinData.o -c -lm -lpthread
gcc -fPIC -shared SinData.o -c libsin.so

这样就生成了动态库。
接着在Qt的.pro文件中加上

INCLUDEPATH += /mnt/hgfs/shareFile/newTask             // SinData.h的 绝对路径
LIBS += -L/mnt/hgfs/shareFile/newTask -lsin            // libsin.so 的绝对路径

并且在Qt中的头文件中添加

extern "C"{
    #include "SinData.h"     // 表示需要使用到C中的头文件
}

这样产生Sin曲线的库已经准备好了,在SinData.h里面用来产生数据点的函数接口就可以使用了。

二、在Qt上绘制Sin图像

在Qt上绘制Sin图像,可以选择使用QCustomPlot和Qwt,在这里我选择使用Qwt,虽然Qwt的安装相比QCustomPlot来说比较复杂(QCustomPlot直接把.h 和.cpp文件放在工程即可),但是Qwt却更加好用。其实Qwt安装也不是很复杂,这里有个教程,几分钟就可以编译安装完毕,linux下安装Qwt详细步骤
装好Qwt后,就开始画曲线了。

/**************************************************************************
Function Name:   InitSinGrid
Description:     初始化网格
**************************************************************************/
void Widget::InitSinGrid()
{

    m_FHRQwtPlot = new QwtPlot(this);
    m_FHRQwtPlot->move(10,5);
    m_FHRQwtPlot->setFixedWidth(780);    // 设置坐标轴的大小
    m_FHRQwtPlot->setFixedHeight(210);
    m_FHRQwtPlot->setTitle(" ");         // 设置坐标轴的标题
    m_FHRQwtPlot->setAxisScale(QwtPlot::yLeft,30,240,30); // 坐标轴的范围和间隔
    m_FHRQwtPlot->setAxisScale(QwtPlot::yRight,30,240,30);
    m_FHRQwtPlot->setAxisScale(QwtPlot::xBottom,0,100,20);
    m_FHRQwtPlot->enableAxis(QwtPlot::yRight,true);
    m_FHRQwtPlot->enableAxis(QwtPlot::xBottom,false);  // 隐藏X轴
    m_FHRQwtPlot->setStyleSheet("background:transparent;");//删除坐标轴背景
    m_FHRQwtPlot->setAxisMaxMinor(QwtPlot::xBottom,4);
    m_FHRQwtPlot->setAxisMaxMinor(QwtPlot::yLeft,3);//设置每一个大格有都少个小格
    m_FHRQwtPlot->setAxisMaxMinor(QwtPlot::yRight,3);
    m_FHRQwtPlot->setAxisScaleDraw(QwtPlot::xBottom,new TimeScaleDraw( QDateTime::currentDateTime()));// 横坐标显示当前时间(需要把X轴显示出来,才能显示出时间)
    m_FHRQwtPlot->setCanvasBackground(Qt::white); // 设置背景白色

    // 删除坐标轴间的间距
    for ( int n = 0; n < m_FHRQwtPlot->axisCnt; n++ )
     {
          QwtScaleWidget *poScaleWidget = m_FHRQwtPlot->axisWidget( n);
          if (poScaleWidget)
          {
              poScaleWidget->setMargin( 0 );
          }

          QwtScaleDraw *poScaleDraw = m_FHRQwtPlot->axisScaleDraw( n );
          if ( poScaleDraw )
          {
              poScaleDraw->enableComponent( QwtAbstractScaleDraw::Backbone, false );
          }
     }
    m_FHRQwtPlot->plotLayout()->setAlignCanvasToScales( true );

    //画网格
    QwtPlotGrid *grid = new QwtPlotGrid();
    grid->enableXMin(true);
    grid->enableYMin(true);
    grid->enableX(true);
    grid->enableY(true);
    grid->setMajorPen(QPen(Qt::gray,1,Qt::SolidLine));  // 大网格
    grid->setMinorPen(QPen(QColor(186,186,186),0,Qt::DotLine));// 小网格
    grid->attach(m_FHRQwtPlot);

    // 往坐标系添加曲线
    m_FHRCurve = new QwtPlotCurve;
    m_FHRCurve->setPen(QColor(255,85,255),2,Qt::SolidLine);
    m_FHRCurve->setCurveAttribute(QwtPlotCurve::Fitted,true);   //圆滑显示曲线
    m_FHRCurve->setRenderHint(QwtPlotItem::RenderAntialiased,true);
    m_FHRCurve->attach(m_FHRQwtPlot);  // 把曲线加载到坐标轴上

    //m_UpdateFlag = 0;

    //m_LimitBackGroud = new Background(m_HighLimit,m_LowLimit);//给绘图填充背景
    //m_LimitBackGroud->attach( m_FHRQwtPlot );//插入plot

    // 启动定时器
    m_SinTimer = new QTimer();    
    connect(m_SinTimer,SIGNAL(timeout()),this,SLOT(TimeSlot()));
    StartSin();   // 开始启动线程,产生Sin坐标点(上面SinData.h里面的接口函数)
    m_SinTimer->start(500);

}



/**************************************************************************
Function Name:   TimeSlot
Description:     定时器槽函数,用来绘制Sin图像
**************************************************************************/
void Widget::TimeSlot()
{
    static int s_BeginAixsX = 0;  // 起始横坐标
    static int s_Result ;

    if ( (s_Result =GetSinData(&m_SinPointX,&m_SinPointY))  <=0 )  // 获取Sin坐标点
    {
       return ;// 获取失败,直接返回
    }

    if(m_SinPointX >= 100)  // 当当前坐标轴画满了,就把坐标轴往前移动,(更新X轴范围)
    {
         s_BeginAixsX++;
         m_FHRQwtPlot->setAxisScale(QwtPlot::xBottom,s_BeginAixsX,m_SinPointX,20);// 更新坐标轴
         m_DataVectorX.erase(m_DataVectorX.begin(),m_DataVectorX.begin()+1);
         m_DataVectorY.erase(m_DataVectorY.begin(),m_DataVectorY.begin()+1);
    }

    m_DataVectorX.append((double)m_SinPointX);
    m_DataVectorY.append((double)m_SinPointY);
    m_FHRCurve->setSamples(m_DataVectorX,m_DataVectorY);  // 绘图

    m_FHRQwtPlot->replot();  // 重绘,一定要调用否则没效果
}

经过了上面的代码,此时Sin图像已经绘制成功了,并能显示出动态的效果,但是还没有调整振幅和周期。这个需要画两个QPushButton或者QToolButton,在点击信号对应的槽上来处理。当点击按钮时,Sin图像就会产生相应的变化。

//更改sin图像的Y轴偏移量、振幅、周期的槽函数。

void Widget::ChangeSinOffset()  
{
    m_UpdateFlag = DATA_UPDATA;
    static int s_SinOffset = 120;
    if(s_SinOffset == 160)
        s_SinOffset = 120;
    s_SinOffset +=10;
    SetSinOffset(s_SinOffset);
    UpdateData();
}

void Widget::ChangeAmplitude()
{

    static int s_SinAmplitude = 20;
    if(s_SinAmplitude == 50)
        s_SinAmplitude = 20;
    m_UpdateFlag = DATA_UPDATA;

    s_SinAmplitude += 5 ;
    SetAmplitude(s_SinAmplitude);
    UpdateData();

}

void Widget::ChangeCycle()
{
    static int s_SinCycle = 4;
    if(s_SinCycle == 10)
        s_SinCycle = 4;
    m_UpdateFlag = DATA_UPDATA;

    s_SinCycle +=1;
    SetCycle(s_SinCycle);
    UpdateData();
}
/**************************************************************************/
//Function Name:     updateData
//Description:     更新sin图形的坐标,重绘
/**************************************************************************/
void Widget::UpdateData()
{
    int axisX[m_SinPointX];
    int axisY[m_SinPointX];
    UpdateSinData(axisX,axisY);  // SinData.h的接口函数
    m_DataVectorX.clear();
    m_DataVectorY.clear();
    for(int n = 0 ; n <m_SinPointX ; n++)
    {
        m_DataVectorX.append((double)axisX[n]);
        m_DataVectorY.append((double)axisY[n]);
    }

    if(m_SinPointX >100)
    {
        int len = m_DataVectorX.length() -100;
        if( len < 0 )
            len = 0-len;
        //m_FHRQwtPlot->setAxisScale(QwtPlot::xBottom,len,m_SinPointX,20);
        m_DataVectorX.erase(m_DataVectorX.begin(),m_DataVectorX.begin()+len);
        m_DataVectorY.erase(m_DataVectorY.begin(),m_DataVectorY.begin()+len);
    }

    m_FHRCurve->setSamples(m_DataVectorX,m_DataVectorY);
    m_FHRQwtPlot->replot();
    m_UpdateFlag = 0;
}

这样,这个Sin图像就完成了,和效果图一致。动态显示Sin曲线,并可调整振幅和周期。

<think>好的,我现在需要处理用户关于在Qt绘制动态曲线图的第三方库的问题。首先,用户已经提到了QtCharts,引用了两篇相关的文章。我需要确认用户是否已经了解Qt自带的QtCharts库,或者他们是否在寻找其他第三方库作为补充或替代。 首先,我应该回顾一下QtCharts的基本功能。根据用户提供的引用,QtCharts可以绘制样条曲线动态曲线实时显示。比如引用[1]中提到了使用QtCharts绘制样条曲线图的步骤代码,引用[2]则介绍了实时绘制的示例。这说明QtCharts本身具备一定的动态曲线绘制能力,用户可能已经尝试过,但可能遇到了性能或功能上的限制,所以才会寻求第三方库。 接下来,我需要考虑用户的需求场景。动态曲线图通常需要高效的数据更新渲染能力,尤其是在实时数据展示时,比如传感器数据、实时监控等。如果QtCharts无法满足性能需求,或者用户需要更复杂的图表类型,可能需要第三方库的支持。 然后,我应该列出Qt中常用的第三方图表库。常见的选项包括QCustomPlot、QWTQt Data Visualization模块,以及一些跨平台的库如Matplotlib与Qt结合使用。需要逐个分析这些库的特点,是否支持动态曲线,实时性能如何,以及集成到Qt中的难易程度。 QCustomPlot是一个轻量级的Qt绘图库,专注于2D图表,支持实时数据更新,性能较好,适合需要高频更新的场景。用户评价通常不错,文档也较为完善,这可能是一个好的推荐。 QWTQt Widgets for Technical Applications)是另一个历史较久的库,适用于科学工程应用,提供丰富的控件如曲线图、柱状图等。但它的学习曲线可能较陡,且对新版Qt的支持可能需要确认,比如是否支持Qt5或Qt6。 Qt Data Visualization模块是Qt官方的一部分,主要用于3D图表,但如果用户需要3D效果,这可能相关,但动态2D曲线可能不是其强项。 第三方库如Matplotlib虽然强大,但通过嵌入到Qt中可能需要使用Qt绘图部件或者将图像导出,这样实时性可能不如原生库。另外,可能需要结合其他工具如PyQt或C++绑定,这可能增加复杂性。 另外,用户可能需要考虑跨平台支持许可证问题。例如,QCustomPlot是GPL许可,如果需要商业用途,可能需要购买商业许可证。而QWT同样采用GPL,需要注意这一点。 接下来,我需要确保推荐的库有良好的文档社区支持,这样用户在集成使用过程中遇到问题时能够找到帮助。例如,引用[2]中的示例代码链接可能帮助用户开始使用QtCharts,但如果需要更高性能,QCustomPlot可能更合适。 最后,用户可能需要示例代码或集成步骤。例如,使用QCustomPlot时,如何添加数据点,如何设置定时器刷新等。这些细节可以帮助用户更快地上手。 总结下来,可能的第三方库推荐顺序是:QCustomPlot、QWT,然后是其他选项。同时需要提醒用户Qt自带的QtCharts可能已经足够,但在性能或功能不足时再考虑第三方库。</think>在Qt绘制动态曲线图,除了自带的QtCharts模块外,还可以考虑以下第三方库: --- ### 1. **QCustomPlot** - **特点**:轻量级、高性能的Qt绘图库,专为实时数据可视化设计,支持动态曲线刷新交互操作。 - **优势**: -Qt实现,无需外部依赖 - 支持实时数据更新(每秒万级数据点)[^1] - 提供丰富的图表类型(曲线、柱状图、散点图等) - **集成方式**: - 下载源码后直接添加`qcustomplot.h``qcustomplot.cpp`到项目 - 示例代码片段: ```cpp QCustomPlot *plot = new QCustomPlot; plot->addGraph(); plot->graph(0)->setData(xData, yData); plot->replot(); // 动态刷新 ``` --- ### 2. **QWT (Qt Widgets for Technical Applications)** - **特点**:面向科学工程应用的库,适合高精度动态曲线绘制- **优势**: - 支持大规模数据集复杂数学运算 - 提供刻度标记、图例等专业控件[^2] - **注意**: - 需要编译安装,对Qt6支持有限(主要适配Qt5) - 开源协议为GPL,商用需授权 --- ### 3. **Qt Data Visualization** - **特点**:Qt官方3D图表模块,适合需要3D动态效果(如曲面波动)的场景。 - **示例场景**: - 实时3D频谱分析 - 动态热力图 --- ### 4. **Matplotlib (通过Python绑定)** - **特点**:通过PyQt/PySide嵌入Python的Matplotlib图表。 - **适用场景**: - 快速验证算法时结合Python脚本 - 需要复杂数学计算时(如傅里叶变换) --- ### 性能对比建议 | 库名称 | 实时性 | 开发难度 | 适用场景 | |----------------|--------|----------|-------------------| | QtCharts | 中 | 低 | 基础动态曲线 | | QCustomPlot | 高 | 中 | 高频数据刷新 | | QWT | 中高 | 高 | 科学/工程应用 | | Matplotlib | 低 | 中 | 复杂计算+可视化 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值