QT 基于qcustomplot实现热力图(一)-优快云博客
QT 基于qcustomplot实现热力图(二)-优快云博客
1.背景
前面个两篇文章基本完成了热力图的基本使用场景,但对于一些负复杂的项目来说功能还是不够,本片讲继续完善。
-
a.增加动态刷新,从右想做刷新;
-
b.增加鼠标点击事件,获得当前的鼠标对应的行和列的值;
-
c.增加通过比例尺的缩过滤显示区域的数据;
-
d.x轴自动更新坐标
2.先上效果
3.实现
本次还是再之前的工程中完善优化,修改后的代码如下:
3.1动态刷新
#if static_Type
for (int i = 0; i < data_.size(); ++i)
{
// 更新 ColorMap 数据
for (int j = 0; j < data_.at(i).size(); ++j)
{
m_colorMap_->data()->setCell(i, j, data_[i][j]);
}
}
timer_.stop();
#else
#if dynamic_Type
for (int i = 0; i < data_.size(); ++i)
{
// 更新 ColorMap 数据
for (int j = 0; j < data_.at(i).size(); ++j)
{
m_colorMap_->data()->setCell(i, j, qrand() % 100);
}
}
#else //real_update_Type
static int indexCols = 0;
indexCols++;
if(indexCols > numCols)
{
numCols += 1;
m_colorMap_->data()->setSize(numCols, numRows);
m_colorMap_->data()->setRange(QCPRange(0, numCols-1), QCPRange(0, numRows-1));
}
//新增1列数据
QVector<double>tmp;
for(int j = 0; j < numRows; j++)
{
tmp.append(qrand() % 100);
}
data_.append(tmp);
// 更新 ColorMap 数据
for(int i = 0; i < data_.size(); i++)
{
for (int j = 0; j < data_.at(i).size(); ++j)
{
m_colorMap_->data()->setCell(i, j, data_[i][j]);
//qDebug() << data_.size() << indexCols << i << j << data_[i][j];
}
}
ui->widget->yAxis->setRange(0, indexCols);
#endif
#endif
3.2鼠标点击
customMouseTimer_ = new QTimer(this);
connect(customMouseTimer_, SIGNAL(timeout()), this, SLOT(customMouseTimerSlot()));
connect(ui->widget, SIGNAL(mousePress(QMouseEvent*)), this, SLOT(customMousePressSlot(QMouseEvent*)));
connect(ui->widget, SIGNAL(mouseRelease(QMouseEvent*)), this, SLOT(customMouseReleaseSlot(QMouseEvent*)));
槽函数
void MainWindow::customMousePressSlot(QMouseEvent *event)
{
qDebug() << "customMousePressSlot";
//单机xy轴或者可读,暂停5秒数据更新,可以进行缩放等操作,
if (ui->widget->xAxis->selectedParts().testFlag(QCPAxis::spAxis) || ui->widget->xAxis->selectedParts().testFlag(QCPAxis::spTickLabels) ||
ui->widget->yAxis->selectedParts().testFlag(QCPAxis::spAxis) || ui->widget->yAxis->selectedParts().testFlag(QCPAxis::spTickLabels))
{
customXAxisUpdateMutex_.tryLock();
customXAxisUpdateFlag_ = false;
customXAxisUpdateMutex_.unlock();
}
//获取坐标点,转换为数据点
if(event->button() == Qt::LeftButton)
{
// 获取鼠标点击的坐标位置
QPointF pos = event->pos();
// 将像素坐标转换为数据坐标
double x = ui->widget->xAxis->pixelToCoord(pos.x());
double y = ui->widget->yAxis->pixelToCoord(pos.y());
//点击混图区域为不响应,避免点击坐标轴
if(x < 0 || y < 0)
{
return;
}
// 计算数据索引(这里仅作示例,您可能需要根据具体情况进行调整)
int indexX = static_cast<int>(x);
int indexY = static_cast<int>(y);
// 显示数据索引
qDebug() << "Clicked on index (" << indexX << ", " << indexY << ")";
}
}
点击的如果是xy轴或者刻度,则数5秒内暂停刷新,5,秒后正常
void MainWindow::customMouseReleaseSlot(QMouseEvent *event)
{
qDebug() << "customMouseTimerSlot";
//重启定时器
customMouseTimer_->stop();
customMouseTimer_->start(5000);
}
void MainWindow::customMouseTimerSlot()
{
qDebug() << "customMouseTimerSlot";
customXAxisUpdateMutex_.tryLock();
customXAxisUpdateFlag_ = true;
customXAxisUpdateMutex_.unlock();
customMouseTimer_->stop();
}
3.3 鼠标滚轮
1.滚轮缩放,该功能此处不在讲解;
2.选中右侧的热力图标尺,滚动滚轮,选择标尺的范围,可控制热力图中的数据显示
至此该功能已经完成,后续在完善剔除之前数据的问题。
完整代码
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QTimer>
#include "qcustomplot.h"
#include <QtCharts>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
public slots:
void timeoutPro();
private slots:
void customMouseTimerSlot();
void customMousePressSlot(QMouseEvent *event);
void customMouseReleaseSlot(QMouseEvent *event);
private:
Ui::MainWindow *ui;
QTimer timer_;
QVector<QVector<double>> data_;
QVector<double> xdata_;
QVector<double> ydata_;
QCPColorScale *m_colorScale_;
QCPColorMap *m_colorMap_;
QTimer *customMouseTimer_;// 曲线图鼠标释放后5秒开始刷新
bool customXAxisUpdateFlag_;// 曲线图开始刷新标志
QMutex customXAxisUpdateMutex_;// 曲线图开始刷新标志设置锁
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
#include "ui_mainwindow.h"
int numRows = 10;
int numCols = 10;
#define static_Type 0 //静态热力图
#define dynamic_Type 0 //动态热力图
#define real_update_Type 1 //实时刷新热力图
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
//添加画布
ui->widget->addGraph();
m_colorMap_ = new QCPColorMap(ui->widget->xAxis, ui->widget->yAxis);
//添加一个色阶
m_colorScale_ = new QCPColorScale(ui->widget);
ui->widget->plotLayout()->addElement(0, 1, m_colorScale_); // add it to the right of the main axis rect
m_colorScale_->setType(QCPAxis::atRight); // scale shall be vertical bar with tick/axis labels right (actually atRight is already the default)
m_colorMap_->setColorScale(m_colorScale_); // associate the color map with the color scale
//设置渐变色风格
m_colorMap_->setGradient(QCPColorGradient::gpJet);
//设置颜色空间的大小m*n矩形 下面需要注意行和列对应的x和y
m_colorMap_->data()->setSize(numCols, numRows);
m_colorMap_->data()->setRange(QCPRange(0, numCols-1), QCPRange(0, numRows-1));
// 设置颜色映射
QCPColorGradient gradient;
gradient.setColorInterpolation(QCPColorGradient::ciRGB);
gradient.setColorStopAt(0, Qt::blue);
gradient.setColorStopAt(0.5, Qt::green);
gradient.setColorStopAt(1, Qt::red);
m_colorMap_->setGradient(gradient);
//x周可自由变换
ui->widget->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectAxes |
QCP::iSelectLegend | QCP::iSelectPlottables);
customMouseTimer_ = new QTimer(this);
connect(customMouseTimer_, SIGNAL(timeout()), this, SLOT(customMouseTimerSlot()));
connect(ui->widget, SIGNAL(mousePress(QMouseEvent*)), this, SLOT(customMousePressSlot(QMouseEvent*)));
connect(ui->widget, SIGNAL(mouseRelease(QMouseEvent*)), this, SLOT(customMouseReleaseSlot(QMouseEvent*)));
customXAxisUpdateFlag_ = true;
#if !real_update_Type
//初始化数据
for(int i = 0 ; i < numCols; i++)
{
QVector<double>tmp;
for(int j = 0; j < numCols; j++)
{
tmp.append(/*i*10*/qrand() % 100);
}
data_.append(tmp);
}
//初始化x
for(int i = 0 ; i < numRows; i++)
{
ydata_.append(i);
}
//初始化y
for(int i = 0 ; i < numCols; i++)
{
xdata_.append(i);
}
#endif
timer_.start(1*1000);
connect(&timer_, SIGNAL(timeout()), this, SLOT(timeoutPro()));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::timeoutPro()
{
#if static_Type
for (int i = 0; i < data_.size(); ++i)
{
// 更新 ColorMap 数据
for (int j = 0; j < data_.at(i).size(); ++j)
{
m_colorMap_->data()->setCell(i, j, data_[i][j]);
}
}
timer_.stop();
#else
#if dynamic_Type
for (int i = 0; i < data_.size(); ++i)
{
// 更新 ColorMap 数据
for (int j = 0; j < data_.at(i).size(); ++j)
{
m_colorMap_->data()->setCell(i, j, qrand() % 100);
}
}
#else //real_update_Type
//新增1列数据
QVector<double>tmp;
for(int j = 0; j < numRows; j++)
{
tmp.append(qrand() % 100);
}
data_.append(tmp);
static int indexCols = 0;
indexCols++;
if(indexCols > numCols)
{
numCols += 1;
if(customXAxisUpdateFlag_)
{
m_colorMap_->data()->setSize(numCols, numRows);
m_colorMap_->data()->setRange(QCPRange(0, numCols-1), QCPRange(0, numRows-1));
}
}
if(customXAxisUpdateFlag_)
{
// 更新 ColorMap 数据
for(int i = 0; i < data_.size(); i++)
{
for (int j = 0; j < data_.at(i).size(); ++j)
{
m_colorMap_->data()->setCell(i, j, data_[i][j]);
//qDebug() << data_.size() << indexCols << i << j << data_[i][j];
}
}
ui->widget->yAxis->setRange(0, indexCols);
}
#endif
#endif
m_colorMap_->rescaleDataRange();
m_colorMap_->rescaleAxes();
ui->widget->replot();
}
void MainWindow::customMouseTimerSlot()
{
qDebug() << "customMouseTimerSlot";
customXAxisUpdateMutex_.tryLock();
customXAxisUpdateFlag_ = true;
customXAxisUpdateMutex_.unlock();
customMouseTimer_->stop();
}
void MainWindow::customMousePressSlot(QMouseEvent *event)
{
qDebug() << "customMousePressSlot";
//单机xy轴或者可读,暂停5秒数据更新,可以进行缩放等操作,
if (ui->widget->xAxis->selectedParts().testFlag(QCPAxis::spAxis) || ui->widget->xAxis->selectedParts().testFlag(QCPAxis::spTickLabels) ||
ui->widget->yAxis->selectedParts().testFlag(QCPAxis::spAxis) || ui->widget->yAxis->selectedParts().testFlag(QCPAxis::spTickLabels))
{
customXAxisUpdateMutex_.tryLock();
customXAxisUpdateFlag_ = false;
customXAxisUpdateMutex_.unlock();
}
//获取坐标点,转换为数据点
if(event->button() == Qt::LeftButton)
{
// 获取鼠标点击的坐标位置
QPointF pos = event->pos();
// 将像素坐标转换为数据坐标
double x = ui->widget->xAxis->pixelToCoord(pos.x());
double y = ui->widget->yAxis->pixelToCoord(pos.y());
//点击混图区域为不响应,避免点击坐标轴
if(x < 0 || y < 0)
{
return;
}
// 计算数据索引(这里仅作示例,您可能需要根据具体情况进行调整)
int indexX = static_cast<int>(x);
int indexY = static_cast<int>(y);
// 显示数据索引
qDebug() << "Clicked on index (" << indexX << ", " << indexY << ")";
}
}
void MainWindow::customMouseReleaseSlot(QMouseEvent *event)
{
qDebug() << "customMouseTimerSlot";
//重启定时器
customMouseTimer_->stop();
timer_.stop();
customMouseTimer_->start(1000);
}