feat:data slove and update heatmap

This commit is contained in:
2025-10-29 14:09:28 +08:00
parent c50b44efe2
commit c6cef3d89d
200 changed files with 100674 additions and 52814 deletions

View File

@@ -1,131 +1,131 @@
//
// Created by Lenn on 2025/10/17.
//
#ifndef TOUCHSENSOR_HEATMAP_H
#define TOUCHSENSOR_HEATMAP_H
#include "modern-qt/utility/theme/theme.hh"
#include "modern-qt/utility/wrapper/common.hh"
#include "modern-qt/utility/wrapper/pimpl.hh"
#include "modern-qt/utility/wrapper/property.hh"
#include "qcustomplot/qcustomplot.h"
#include "modern-qt/utility/wrapper/widget.hh"
#include <concepts>
#include <qcontainerfwd.h>
#include <qvector.h>
struct point_data {
double x;
double y;
double z;
explicit point_data(double x, double y, double z) : x{x}, y{y}, z{z} {}
};
using PointData = struct point_data;
namespace creeper {
class HeatMapPlot;
namespace plot_widget::internal {
class BasicPlot : public QCustomPlot {
CREEPER_PIMPL_DEFINITION(BasicPlot)
friend class HeatMapPlot;
public:
// BasicPlot();
// ~BasicPlot();
// BasicPlot(const BasicPlot&) = delete;
// BasicPlot& operator=(const BasicPlot&) = delete;
void init_plot()const;
void load_theme_manager(ThemeManager&)const;
void set_xlabel_text(const QString&)const;
void set_ylabel_text(const QString&)const;
void set_matrix_size(const QSize&)const;
void set_matrix_size(const int& w, const int& h)const;
void set_data(const QVector<PointData>& data)const;
void set_color_gradient_range(const double& min, const double& max)const;
QSize get_matrix_size() const;
bool is_initialized() const;
public slots:
void update_dynamic_heatmap(const QVector<PointData>& map)const;
void dataChanged(const QVector<PointData>& map)const;
void dataRangeChanged(const double& min, const double& max)const;
protected:
void paintEvent(QPaintEvent*) override;
private:
friend struct Impl;
};
} // namespace plot_widget::internal
namespace plot_widget::pro {
using Token = common::Token<plot_widget::internal::BasicPlot>;
using XLabelText = common::pro::String<Token,
[](auto& self, const auto& string) {
self.set_xlabel_text(string);
}>;
using YLabelText = common::pro::String<Token,
[](auto& self, const auto& string) {
self.set_ylabel_text(string);
}>;
struct MatrixSize : Token {
QSize size;
explicit MatrixSize(const int& w, const int& h) : size{w, h} {}
explicit MatrixSize(const QSize& s) : size{s} {}
void apply(auto& self) const {
self.set_matrix_size(size);
}
};
using Data = common::pro::Vector<Token, PointData,
[](auto& self, const auto& data) {
self.set_data(data);
}>;
struct ColorRange : Token {
double min;
double max;
explicit ColorRange(double min, double max) : min{min}, max{max} {}
void apply(auto& self) const {
self.set_color_gradient_range(min, max);
}
};
template <typename F>
using OnDataChanged =
common::pro::SignalInjection<F, Token, &internal::BasicPlot::dataChanged>;
template <typename F>
using OnDataRangeChanged =
common::pro::SignalInjection<F, Token, &internal::BasicPlot::dataRangeChanged>;
template<class PlotWidget>
concept trait = std::derived_from<PlotWidget, Token>;
using PlotData = common::pro::Vector<Token, PointData, [](auto& self, const auto& vec) {
self.set_data(vec);
}>;
using DataRange = common::pro::Array<Token, int, 2, [](auto& self, const auto& arr) {
self.set_color_gradient_range(arr[0], arr[1]);
}>;
CREEPER_DEFINE_CHECKER(trait);
using namespace widget::pro;
using namespace theme::pro;
}
struct HeatMapPlot
: public Declarative<plot_widget::internal::BasicPlot,
CheckerOr<plot_widget::pro::checker, widget::pro::checker, theme::pro::checker>> {
using Declarative::Declarative;
void paintEvent(QPaintEvent*) override;
};
}
#endif // TOUCHSENSOR_HEATMAP_H
//
// Created by Lenn on 2025/10/17.
//
#ifndef TOUCHSENSOR_HEATMAP_H
#define TOUCHSENSOR_HEATMAP_H
#include "modern-qt/utility/theme/theme.hh"
#include "modern-qt/utility/wrapper/common.hh"
#include "modern-qt/utility/wrapper/pimpl.hh"
#include "modern-qt/utility/wrapper/property.hh"
#include "qcustomplot/qcustomplot.h"
#include "modern-qt/utility/wrapper/widget.hh"
#include <concepts>
#include <qcontainerfwd.h>
#include <qvector.h>
struct point_data {
double x;
double y;
double z;
explicit point_data(double x, double y, double z) : x{x}, y{y}, z{z} {}
};
using PointData = struct point_data;
namespace creeper {
class HeatMapPlot;
namespace plot_widget::internal {
class BasicPlot : public QCustomPlot {
CREEPER_PIMPL_DEFINITION(BasicPlot)
friend class HeatMapPlot;
public:
// BasicPlot();
// ~BasicPlot();
// BasicPlot(const BasicPlot&) = delete;
// BasicPlot& operator=(const BasicPlot&) = delete;
void init_plot()const;
void load_theme_manager(ThemeManager&)const;
void set_xlabel_text(const QString&)const;
void set_ylabel_text(const QString&)const;
void set_matrix_size(const QSize&)const;
void set_matrix_size(const int& w, const int& h)const;
void set_data(const QVector<PointData>& data)const;
void set_color_gradient_range(const double& min, const double& max)const;
QSize get_matrix_size() const;
bool is_initialized() const;
public slots:
void update_dynamic_heatmap(const QVector<PointData>& map)const;
void dataChanged(const QVector<PointData>& map)const;
void dataRangeChanged(const double& min, const double& max)const;
protected:
void paintEvent(QPaintEvent*) override;
private:
friend struct Impl;
};
} // namespace plot_widget::internal
namespace plot_widget::pro {
using Token = common::Token<plot_widget::internal::BasicPlot>;
using XLabelText = common::pro::String<Token,
[](auto& self, const auto& string) {
self.set_xlabel_text(string);
}>;
using YLabelText = common::pro::String<Token,
[](auto& self, const auto& string) {
self.set_ylabel_text(string);
}>;
struct MatrixSize : Token {
QSize size;
explicit MatrixSize(const int& w, const int& h) : size{w, h} {}
explicit MatrixSize(const QSize& s) : size{s} {}
void apply(auto& self) const {
self.set_matrix_size(size);
}
};
using Data = common::pro::Vector<Token, PointData,
[](auto& self, const auto& data) {
self.set_data(data);
}>;
struct ColorRange : Token {
double min;
double max;
explicit ColorRange(double min, double max) : min{min}, max{max} {}
void apply(auto& self) const {
self.set_color_gradient_range(min, max);
}
};
template <typename F>
using OnDataChanged =
common::pro::SignalInjection<F, Token, &internal::BasicPlot::dataChanged>;
template <typename F>
using OnDataRangeChanged =
common::pro::SignalInjection<F, Token, &internal::BasicPlot::dataRangeChanged>;
template<class PlotWidget>
concept trait = std::derived_from<PlotWidget, Token>;
using PlotData = common::pro::Vector<Token, PointData, [](auto& self, const auto& vec) {
self.set_data(vec);
}>;
using DataRange = common::pro::Array<Token, int, 2, [](auto& self, const auto& arr) {
self.set_color_gradient_range(arr[0], arr[1]);
}>;
CREEPER_DEFINE_CHECKER(trait);
using namespace widget::pro;
using namespace theme::pro;
}
struct HeatMapPlot
: public Declarative<plot_widget::internal::BasicPlot,
CheckerOr<plot_widget::pro::checker, widget::pro::checker, theme::pro::checker>> {
using Declarative::Declarative;
void paintEvent(QPaintEvent*) override;
};
}
#endif // TOUCHSENSOR_HEATMAP_H

View File

@@ -1,197 +1,177 @@
//
// Created by Lenn on 2025/10/17.
//
#ifndef TOUCHSENSOR_HEATMAP_IMPL_HH
#define TOUCHSENSOR_HEATMAP_IMPL_HH
#include "heatmap.hh"
#include "modern-qt/utility/theme/theme.hh"
#include "modern-qt/widget/sliders.hh"
#include <memory>
#include <qcolor.h>
#include <qdebug.h>
using namespace creeper::plot_widget::internal;
struct BasicPlot::Impl {
explicit Impl(BasicPlot& self) noexcept : self{self}, initialized(false), matrix_size(QSize{3, 4}) {}
public:
auto set_xlabel_text(const QString& text) -> void {
xlabel = text;
if (initialized) {
self.xAxis->setLabel(text);
self.replot();
}
}
auto set_ylabel_text(const QString& text) -> void {
ylabel = text;
if (initialized) {
self.yAxis->setLabel(text);
self.replot();
}
}
auto set_matrix_size(const QSize& size) -> void {
matrix_size = size;
if (initialized) {
// 重新初始化热力图以适应新的矩阵大小
reset_plot();
if (!data_points.isEmpty()) {
set_data(data_points);
}
}
}
auto load_theme_manager(ThemeManager& mgr) -> void {
mgr.append_handler(&self, [this](const ThemeManager& mgr) {
// 可以根据主题更新颜色渐变等
if (initialized) {
self.replot();
}
});
}
auto set_color_gradient_range(const double& min, const double& max) -> void {
if (initialized && self.plottableCount() > 0) {
auto* cpmp = static_cast<QCPColorMap*>(self.plottable(0));
cpmp->setDataRange(QCPRange(min, max));
self.replot();
}
color_min = min;
color_max = max;
}
auto set_data(const QVector<PointData>& data) -> void {
data_points = data;
if (initialized) {
update_plot_data();
}
}
auto initialize_plot() -> void {
if (initialized) return;
QCPColorMap* cpmp = new QCPColorMap(self.xAxis, self.yAxis);
cpmp->data()->setSize(matrix_size.width(), matrix_size.height());
cpmp->data()->setRange(QCPRange(0.5, matrix_size.width() - 0.5),
QCPRange(0.5, matrix_size.height() - 0.5));
// 配置坐标轴
QSharedPointer<QCPAxisTickerText> xticker(new QCPAxisTickerText);
QSharedPointer<QCPAxisTickerText> yticker(new QCPAxisTickerText);
xticker->setSubTickCount(1);
yticker->setSubTickCount(1);
self.xAxis->setVisible(false);
self.yAxis->setVisible(false);
self.xAxis->setTicker(xticker);
self.yAxis->setTicker(yticker);
// 设置网格
self.xAxis->grid()->setPen(Qt::NoPen);
self.yAxis->grid()->setPen(Qt::NoPen);
self.xAxis->grid()->setSubGridVisible(true);
self.yAxis->grid()->setSubGridVisible(true);
self.xAxis->setSubTicks(true);
self.yAxis->setSubTicks(true);
self.xAxis->setTickLength(0);
self.yAxis->setTickLength(0);
self.xAxis->setSubTickLength(6);
self.yAxis->setSubTickLength(6);
// 设置范围
self.xAxis->setRange(0, matrix_size.width());
self.yAxis->setRange(0, matrix_size.height());
// 设置标签
if (!xlabel.isEmpty()) self.xAxis->setLabel(xlabel);
if (!ylabel.isEmpty()) self.yAxis->setLabel(ylabel);
// 添加/复用颜色刻度(避免重复 addElement 到相同单元格导致告警)
QCPLayoutElement* occupied = self.plotLayout()->element(1, 0);
QCPColorScale* color_scale = occupied ? qobject_cast<QCPColorScale*>(occupied) : nullptr;
if (!color_scale) {
if (occupied) {
// 单元格被其他元素占用,移除并删除后再放入 ColorScale
self.plotLayout()->remove(occupied);
delete occupied;
occupied = nullptr;
}
color_scale = new QCPColorScale(&self);
color_scale->setType(QCPAxis::atBottom);
self.plotLayout()->addElement(1, 0, color_scale);
}
cpmp->setColorScale(color_scale);
// 设置颜色渐变
QCPColorGradient gradient;
gradient.setColorStopAt(0.0, QColor(246, 239, 166)); // F6EFA6
gradient.setColorStopAt(1.0, QColor(191, 68, 76)); // BF444C
cpmp->setGradient(gradient);
// 设置数据范围
cpmp->setDataRange(QCPRange(color_min, color_max));
cpmp->setInterpolate(false);
// 配置边距
QCPMarginGroup *margin_group = new QCPMarginGroup(&self);
self.axisRect()->setMarginGroup(QCP::msLeft | QCP::msRight, margin_group);
color_scale->setMarginGroup(QCP::msLeft | QCP::msRight, margin_group);
initialized = true;
// 如果已有数据,更新图表
if (!data_points.isEmpty()) {
update_plot_data();
}
}
auto reset_plot() -> void {
// 清除所有绘图元素
self.clearPlottables();
self.clearGraphs();
self.clearItems();
self.clearFocus();
// 重新初始化
initialized = false;
initialize_plot();
}
auto update_plot_data() -> void {
if (!initialized || self.plottableCount() == 0) return;
auto* cpmp = static_cast<QCPColorMap*>(self.plottable(0));
// 设置新数据
for (const auto& item : data_points) {
if (item.x >= 0 && item.x < matrix_size.width() &&
item.y >= 0 && item.y < matrix_size.height()) {
cpmp->data()->setCell(item.x, item.y, item.z);
}
}
// 重绘
self.replot();
}
auto is_plot_initialized() const -> bool {
return initialized;
}
auto get_matrix_size() const -> QSize {
return matrix_size;
}
private:
QString xlabel;
QString ylabel;
QSize matrix_size;
QVector<PointData> data_points;
double color_min = 0.0;
double color_max = 800.0;
bool initialized;
BasicPlot& self;
};
#endif // TOUCHSENSOR_HEATMAP_IMPL_HH
//
// Created by Lenn on 2025/10/17.
//
#ifndef TOUCHSENSOR_HEATMAP_IMPL_HH
#define TOUCHSENSOR_HEATMAP_IMPL_HH
#include "heatmap.hh"
#include "modern-qt/utility/theme/theme.hh"
#include "modern-qt/widget/sliders.hh"
#include <memory>
#include <qcolor.h>
#include <qdebug.h>
using namespace creeper::plot_widget::internal;
struct BasicPlot::Impl {
explicit Impl(BasicPlot& self) noexcept : self{self}, initialized(false), matrix_size(QSize{3, 4}) {}
public:
auto set_xlabel_text(const QString& text) -> void {
xlabel = text;
if (initialized) {
self.xAxis->setLabel(text);
self.replot();
}
}
auto set_ylabel_text(const QString& text) -> void {
ylabel = text;
if (initialized) {
self.yAxis->setLabel(text);
self.replot();
}
}
auto set_matrix_size(const QSize& size) -> void {
matrix_size = size;
if (initialized) {
reset_plot();
if (!data_points.isEmpty()) {
set_data(data_points);
}
}
}
auto load_theme_manager(ThemeManager& mgr) -> void {
mgr.append_handler(&self, [this](const ThemeManager& mgr) {
if (initialized) {
self.replot();
}
});
}
auto set_color_gradient_range(const double& min, const double& max) -> void {
if (initialized && self.plottableCount() > 0) {
auto* cpmp = static_cast<QCPColorMap*>(self.plottable(0));
cpmp->setDataRange(QCPRange(min, max));
self.replot();
}
color_min = min;
color_max = max;
}
auto set_data(const QVector<PointData>& data) -> void {
data_points = data;
if (initialized) {
update_plot_data();
}
}
auto initialize_plot() -> void {
if (initialized) return;
QCPColorMap* cpmp = new QCPColorMap(self.xAxis, self.yAxis);
cpmp->data()->setSize(matrix_size.width(), matrix_size.height());
cpmp->data()->setRange(QCPRange(0.5, matrix_size.width() - 0.5),
QCPRange(0.5, matrix_size.height() - 0.5));
QSharedPointer<QCPAxisTickerText> xticker(new QCPAxisTickerText);
QSharedPointer<QCPAxisTickerText> yticker(new QCPAxisTickerText);
xticker->setSubTickCount(1);
yticker->setSubTickCount(1);
self.xAxis->setVisible(false);
self.yAxis->setVisible(false);
self.xAxis->setTicker(xticker);
self.yAxis->setTicker(yticker);
self.xAxis->grid()->setPen(Qt::NoPen);
self.yAxis->grid()->setPen(Qt::NoPen);
self.xAxis->grid()->setSubGridVisible(true);
self.yAxis->grid()->setSubGridVisible(true);
self.xAxis->setSubTicks(true);
self.yAxis->setSubTicks(true);
self.xAxis->setTickLength(0);
self.yAxis->setTickLength(0);
self.xAxis->setSubTickLength(6);
self.yAxis->setSubTickLength(6);
self.xAxis->setRange(0, matrix_size.width());
self.yAxis->setRange(0, matrix_size.height());
if (!xlabel.isEmpty()) self.xAxis->setLabel(xlabel);
if (!ylabel.isEmpty()) self.yAxis->setLabel(ylabel);
QCPColorScale* color_scale = new QCPColorScale(&self);
color_scale->setType(QCPAxis::atBottom);
self.plotLayout()->addElement(1, 0, color_scale);
cpmp->setColorScale(color_scale);
QCPColorGradient gradient;
gradient.setColorStopAt(0.0, QColor(246, 239, 166)); // F6EFA6
gradient.setColorStopAt(1.0, QColor(191, 68, 76)); // BF444C
cpmp->setGradient(gradient);
cpmp->setDataRange(QCPRange(color_min, color_max));
cpmp->setInterpolate(false);
QCPMarginGroup *margin_group = new QCPMarginGroup(&self);
self.axisRect()->setMarginGroup(QCP::msLeft | QCP::msRight, margin_group);
color_scale->setMarginGroup(QCP::msLeft | QCP::msRight, margin_group);
initialized = true;
if (!data_points.isEmpty()) {
update_plot_data();
}
}
auto reset_plot() -> void {
// 清除所有绘图元素
self.clearPlottables();
self.clearGraphs();
self.clearItems();
self.clearFocus();
// 重新初始化
initialized = false;
initialize_plot();
}
auto update_plot_data() -> void {
if (!initialized || self.plottableCount() == 0) return;
auto* cpmp = static_cast<QCPColorMap*>(self.plottable(0));
// 设置新数据
for (const auto& item : data_points) {
if (item.x >= 0 && item.x < matrix_size.width() &&
item.y >= 0 && item.y < matrix_size.height()) {
cpmp->data()->setCell(item.x, item.y, item.z);
}
}
// 重绘
self.replot();
}
auto is_plot_initialized() const -> bool {
return initialized;
}
auto get_matrix_size() const -> QSize {
return matrix_size;
}
private:
QString xlabel;
QString ylabel;
QSize matrix_size;
QVector<PointData> data_points;
double color_min = 0.0;
double color_max = 800.0;
bool initialized;
BasicPlot& self;
};
#endif // TOUCHSENSOR_HEATMAP_IMPL_HH