频谱图,又称光谱图,显示了信号的频谱强度随时间的变化,不同的强度采用不同的颜色显示,呈线性映射关系。在频谱图上,横轴表示频率,纵轴表示时间,每个点用不同颜色表示信号的强度。
在项目中遇到需要用Qt来实现频谱图绘制,数据每隔1秒发一批,每批数据包含不同频率对应的信号强度,频率最大到23300Hz,同时需要记录过去360秒的数据,最终程序需要运行在国产目标机上。
分析一下,需要建立坐标系来绘制,横轴有23300个维度,纵轴有360个维度,图上有23300×360=8388000个点,每秒更新一次。
实现方法有很多种,可以用QPainter、paintGL、QChart、QCustomPlot等,不同方法各有利弊,比如用QPainter实现的话性能较差,容易卡死;paintGL需要依赖OpenGL环境,国产机不一定支持;QChart只支持Qt5.7之后的版本;QCustomPlot功能强大,性能较好,而且开源,但是封装程度高,自由度小,可扩展接口较少。
这里提供另一种实现方法:
用QImage实时存储并显示,将信号强度对应的颜色存储到QImage中,每次更新时去掉第一个横向维度的数据,在最后添加一个横向维度的数据,组成新的图片更新显示。实测效果还不错,性能优良。
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QTimer>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
protected:
void initSetting(); // 初始化设置图片参数
QRgb getRgb(); // 获取每个像素点的rgb(可自定义)
void showToLabel(); // 图片显示到界面
protected slots:
void updateImage(); // 定时器更新图片
private:
Ui::Widget *ui;
QImage m_image; // 图片
int m_width; // 宽度
int m_height; // 高度
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
// 定时器
QTimer *timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(updateImage()));
timer->start(1000);
// 初始化设置
initSetting();
}
Widget::~Widget()
{
delete ui;
}
void Widget::initSetting()
{
m_width = 23300;
m_height = 360;
m_image = QImage(m_width, m_height, QImage::Format_RGB32);
for (int i = 0; i < m_width; ++i)
{
for (int j = 0; j < m_height; ++j)
{
m_image.setPixel(QPoint(i, j), qRgb(21, 197, 212)); // 设置默认的背景颜色
}
}
showToLabel();
}
QRgb Widget::getRgb()
{
// 自定义强度和颜色的映射关系,暂用随机数
int a = qrand() % 256;
int b = qrand() % 256;
int c = qrand() % 256;
return qRgb(a, b, c);
}
void Widget::showToLabel()
{
// 显示到label
int pxWidth = this->width(), pxHeight = this->height();
QImage imageTemp = m_image.scaled(pxWidth, pxHeight);
QPixmap pixmap;
pixmap.convertFromImage(imageTemp);
ui->label->setPixmap(pixmap);
}
void Widget::updateImage()
{
// 注:copy和setPixel接口的x和y指的是第x列,第y行
QImage tempImage = m_image.copy(0, -1, m_width, m_height); // 复制上一时刻背景,去掉最后一行,保留第一行为空(空即为黑色)
for (int i = 0; i < m_width; ++i) // 第一行赋值新的rgb
tempImage.setPixel(QPoint(i, 0), getRgb());
m_image = tempImage;
showToLabel();
}
运行效果: