From 8494a9c94e11e71ff1879fdee987f8ed5df7fa15 Mon Sep 17 00:00:00 2001 From: lenn Date: Thu, 23 Oct 2025 10:09:13 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=E7=83=AD=E5=8A=9B=E5=9B=BE?= =?UTF-8?q?=E9=83=A8=E5=88=86=EF=BC=8C=E5=8F=96=E6=B6=88=E8=BE=B9=E6=A1=86?= =?UTF-8?q?=EF=BC=8C=E8=83=8C=E6=99=AF=E9=80=8F=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/charts/heatmap.cc | 122 +++++++------------ components/charts/heatmap.hh | 85 ++++++++----- components/charts/heatmap.impl.hh | 182 +++++++++++++++++++++++++--- components/view.cc | 13 ++ main.cc | 6 +- modern-qt/utility/wrapper/common.hh | 19 +++ 6 files changed, 302 insertions(+), 125 deletions(-) diff --git a/components/charts/heatmap.cc b/components/charts/heatmap.cc index 19fabda..fa03122 100644 --- a/components/charts/heatmap.cc +++ b/components/charts/heatmap.cc @@ -13,25 +13,31 @@ #include #include +using namespace creeper::plot_widget::internal; + BasicPlot::BasicPlot() : pimpl{std::make_unique(*this)} { - init_plot(); + // setStyleSheet("background-color: rgb(255, 0, 0);"); + // QColor lightBlue(230, 240, 250); + setBackground(Qt::transparent); } BasicPlot::~BasicPlot() {} void BasicPlot::set_matrix_size(const QSize& size) { qDebug() << "set matrix size" << size; - matrix_size = size; pimpl->set_matrix_size(size); } void BasicPlot::set_matrix_size(const int& w, const int& h) { QSize size(w, h); qDebug() << "set matrix size" << size; - matrix_size = size; pimpl->set_matrix_size(size); } +void BasicPlot::set_color_gradient_range(const double& min, const double& max) { + pimpl->set_color_gradient_range(min, max); +} + void BasicPlot::set_xlabel_text(const QString& text) { pimpl->set_xlabel_text(text); } @@ -41,94 +47,50 @@ void BasicPlot::set_ylabel_text(const QString& text) { } void BasicPlot::load_theme_manager(ThemeManager& mgr) { - + pimpl->load_theme_manager(mgr); } -QSize BasicPlot::get_matrix_size() { - return matrix_size; +QSize BasicPlot::get_matrix_size() const { + return pimpl->get_matrix_size(); } + +void BasicPlot::set_data(const QVector& data) { + qDebug() << "set data" << data.size(); + pimpl->set_data(data); +} + +bool BasicPlot::is_initialized() const { + return pimpl->is_plot_initialized(); +} + void BasicPlot::init_plot() { - QVector origin_data = {PointData{0, 0, 5}, PointData{0, 1, 3}, PointData{0, 2, 0}, - PointData{1, 0, 5}, PointData{1, 1, 3}, PointData{1, 2, 9}, - PointData{2, 0, 5}, PointData{2, 1, 3}, PointData{2, 2, 3}, - PointData{3, 0, 5}, PointData{3, 1, 3}, PointData{3, 2, 8}}; - QCPColorMap* cpmp = new QCPColorMap(xAxis, yAxis); - qDebug() << "cpmp->data()->setSize():" << matrix_size; - cpmp->data()->setSize(matrix_size.width(), matrix_size.height()); - cpmp->data()->setRange(QCPRange(0.5, matrix_size.width()), (QCPRange(0.5, matrix_size.height()))); - // cpmp ->setSize(matrix_size.width(), matrix_size.height()); - // cpmp ->setRange(QCPRange(0.5, matrix_size.width() - 0.5), QCPRange(0.5, matrix_size.height() - 0.5)); - QSharedPointer xticker(new QCPAxisTickerText); - QSharedPointer yticker(new QCPAxisTickerText); - xticker->setSubTickCount(1); - xticker->setSubTickCount(1); - xAxis->setTicker(xticker); - yAxis->setTicker(yticker); - xAxis->grid()->setPen(Qt::NoPen); - yAxis->grid()->setPen(Qt::NoPen); - xAxis->grid()->setSubGridVisible(true); - yAxis->grid()->setSubGridVisible(true); - xAxis->setSubTicks(true); - yAxis->setSubTicks(true); - xAxis->setTickLength(0); - yAxis->setTickLength(0); - xAxis->setSubTickLength(6); - yAxis->setSubTickLength(6); - xAxis->setRange(0, matrix_size.width()); - yAxis->setRange(0, matrix_size.height()); - - for (const auto& item : origin_data) { - cpmp->data()->setCell(item.x, item.y, item.z); - } - - QCPColorScale* color_scale = new QCPColorScale(this); - color_scale->setType(QCPAxis::atBottom); - this->plotLayout()->addElement(1, 0, color_scale); - cpmp->setColorScale(color_scale); - QCPColorGradient gradient; - gradient.setColorStopAt(0.0, QColor("#F6EFA6")); - gradient.setColorStopAt(1.0, QColor("#BF444C")); - cpmp->setGradient(gradient); - - cpmp->setDataRange(QCPRange(0, 10)); - cpmp->setInterpolate(false); - - QCPMarginGroup *margin_group = new QCPMarginGroup(this); - axisRect()->setMarginGroup(QCP::msLeft | QCP::msRight, margin_group); - color_scale->setMarginGroup(QCP::msLeft | QCP::msRight, margin_group); + pimpl->initialize_plot(); } -void BasicPlot::dataChanged(QVector& map) { - auto *cpmp = static_cast(this->plottable(0)); - - size_t key_size = cpmp->data()->keySize(); - size_t value_size = cpmp->data()->valueSize(); - for (auto& item : map) { - if (cpmp->data()->alpha(item.x, item.y)) { - if (cpmp->data()->alpha(item.x, item.y)) { - cpmp->data()->setCell(item.x, item.y, item.z); - } - } +void BasicPlot::paintEvent(QPaintEvent* event) { + // 确保在绘制前初始化 + if (!is_initialized()) { + init_plot(); } - - replot(); + QCustomPlot::paintEvent(event); } -void BasicPlot::update_dynamic_heatmap(QVector& map) { - auto *cpmp = static_cast(this->plottable(0)); +void BasicPlot::dataChanged(const QVector& map) { + set_data(map); + // emit dataChanged(map); +} - size_t key_size = cpmp->data()->keySize(); - size_t value_size = cpmp->data()->valueSize(); - for (auto& item: map) { - if (cpmp->data()->alpha(item.x, item.y)) { - cpmp->data()->setCell(item.x, item.y, item.z); - } - } +void BasicPlot::dataRangeChanged(const double& min, const double& max) { + set_color_gradient_range(min, max); + // emit dataRangeChanged(min, max); +} - replot(); +void BasicPlot::update_dynamic_heatmap(const QVector& map) { + set_data(map); } using namespace creeper; -auto HeatMapPlot::paintEvent(QPaintEvent* event) -> void { - QCustomPlot::paintEvent(event); -} + +void creeper::HeatMapPlot::paintEvent(QPaintEvent* event) { + BasicPlot::paintEvent(event); +} \ No newline at end of file diff --git a/components/charts/heatmap.hh b/components/charts/heatmap.hh index ff08f10..c8ae84c 100644 --- a/components/charts/heatmap.hh +++ b/components/charts/heatmap.hh @@ -13,6 +13,7 @@ #include "modern-qt/utility/wrapper/widget.hh" #include #include +#include struct point_data { double x; @@ -26,47 +27,49 @@ class HeatMapPlot; namespace plot_widget::internal { class BasicPlot : public QCustomPlot { + CREEPER_PIMPL_DEFINITION(BasicPlot) + friend class HeatMapPlot; public: - // CREEPER_PIMPL_DEFINITION(BasicPlot); - BasicPlot(); - ~BasicPlot(); - BasicPlot(const BasicPlot&) = delete; - BasicPlot& operator=(const BasicPlot&) = delete; private: struct Impl; - std::unique_ptr pimpl; - - friend HeatMapPlot; - - public: - struct ColorSpace {}; + // BasicPlot(); + // ~BasicPlot(); + // BasicPlot(const BasicPlot&) = delete; + // BasicPlot& operator=(const BasicPlot&) = delete; void init_plot(); - - void load_theme_manager(ThemeManager&); void set_xlabel_text(const QString&); + void load_theme_manager(ThemeManager&); + void set_xlabel_text(const QString&); void set_ylabel_text(const QString&); - void set_matrix_size(const QSize&); - void set_matrix_size(const int& w, const int& h); + void set_data(const QVector& data); + void set_color_gradient_range(const double& min, const double& max); + QSize get_matrix_size() const; + bool is_initialized() const; public slots: - void update_dynamic_heatmap(QVector& map); - void dataChanged(QVector& map); + void update_dynamic_heatmap(const QVector& map); + void dataChanged(const QVector& map); + void dataRangeChanged(const double& min, const double& max); + +protected: + void paintEvent(QPaintEvent*) override; + private: - QSize get_matrix_size(); - private: - friend class Impl; - QSize matrix_size = {3, 4}; + friend struct Impl; + }; } // namespace plot_widget::internal namespace plot_widget::pro { - using Token = common::Token; - using XLabelText = - common::pro::String; + + using XLabelText = common::pro::String; - using YLabelText = - common::pro::String; @@ -75,21 +78,48 @@ namespace plot_widget::pro { 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.width(), size.height()); + self.set_matrix_size(size); + } + }; + + using Data = common::pro::Vector; + + 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 using OnDataChanged = common::pro::SignalInjection; + + template + using OnDataRangeChanged = + common::pro::SignalInjection; template concept trait = std::derived_from; + using PlotData = common::pro::Vector; + + using DataRange = common::pro::Array; + CREEPER_DEFINE_CHECKER(trait); using namespace widget::pro; using namespace theme::pro; } + struct HeatMapPlot : public Declarative> { @@ -97,4 +127,5 @@ struct HeatMapPlot void paintEvent(QPaintEvent*) override; }; } + #endif // TOUCHSENSOR_HEATMAP_H diff --git a/components/charts/heatmap.impl.hh b/components/charts/heatmap.impl.hh index 6d62bdf..de066d6 100644 --- a/components/charts/heatmap.impl.hh +++ b/components/charts/heatmap.impl.hh @@ -9,34 +9,182 @@ #include "modern-qt/utility/theme/theme.hh" #include "modern-qt/widget/sliders.hh" #include +#include +#include using namespace creeper::plot_widget::internal; struct BasicPlot::Impl { - explicit Impl(BasicPlot& self) noexcept : self{self} {} + 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; } - - auto set_ylabel_text(const QString& text) -> void { ylabel = text; } - - auto set_matrix_size(const QSize& size) { - matrix_size.setWidth(size.width()); - matrix_size.setHeight(size.height()); +public: + auto set_xlabel_text(const QString& text) -> void { + xlabel = text; + if (initialized) { + self.xAxis->setLabel(text); + self.replot(); + } } - auto load_theme_manager(ThemeManager& mgr) { - // mgr.append_handler(&self, [this](const ThemeManager& mgr){ - // set_color_scheme(mgr.color_scheme()); - // }); + auto set_ylabel_text(const QString& text) -> void { + ylabel = text; + if (initialized) { + self.yAxis->setLabel(text); + self.replot(); + } } - // auto set_color_scheme(slider::pro::Measurements measurements) { - // - // } - private: + 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(self.plottable(0)); + cpmp->setDataRange(QCPRange(min, max)); + self.replot(); + } + color_min = min; + color_max = max; + } + + auto set_data(const QVector& 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); + qDebug() << "initialize_plot() size:" << matrix_size; + 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 xticker(new QCPAxisTickerText); + QSharedPointer 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(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 data_points; + double color_min = 0.0; + double color_max = 15.0; + bool initialized; BasicPlot& self; }; diff --git a/components/view.cc b/components/view.cc index 41e1028..369ac51 100644 --- a/components/view.cc +++ b/components/view.cc @@ -44,6 +44,8 @@ static auto ComConfigComponent(ThemeManager& manager, auto&& callback) { auto select_baud_context = std::make_shared>(); select_baud_context->set_silent(QStringList {"9600", "115200"}); + + const auto row = new Row { // lnpro::Item { // text_field::pro::ThemeManager {manager}, @@ -148,11 +150,22 @@ static auto ComConfigComponent(ThemeManager& manager, auto&& callback) { } static auto DisplayComponent(ThemeManager& manager, int index = 0) noexcept { + auto heatmap_context = std::make_shared>>(); + heatmap_context->set_silent(QVector{ + PointData{0, 0, 1}, PointData{1, 0, 2}, PointData{2, 0, 3}, + PointData{0, 1, 3}, PointData{1, 1, 4}, PointData{2, 1, 5}, + PointData{0, 2, 6}, PointData{1, 2, 7}, PointData{2, 2, 8}, + PointData{0, 3, 9}, PointData{1, 3, 10}, PointData{2, 3, 11}, + }); const auto row = new Row{ lnpro::Item { plot_widget::pro::SizePolicy { QSizePolicy::Expanding, }, + MutableForward { + plot_widget::pro::PlotData {}, + heatmap_context, + }, pwpro::MatrixSize { QSize{3, 4} }, diff --git a/main.cc b/main.cc index 39b00c4..983520a 100644 --- a/main.cc +++ b/main.cc @@ -46,7 +46,11 @@ auto main(int argc, char *argv[]) -> int { [&](MainWindow& window) noexcept { }, - mwpro::FixedSize {1080, 720}, + // mwpro::FixedSize {1080, 720}, + mwpro::MinimumSize { + 1080, + 720 + }, mwpro::Central { capro::ThemeManager {manager}, capro::Radius {0}, diff --git a/modern-qt/utility/wrapper/common.hh b/modern-qt/utility/wrapper/common.hh index 275c5f6..5973af1 100644 --- a/modern-qt/utility/wrapper/common.hh +++ b/modern-qt/utility/wrapper/common.hh @@ -131,6 +131,25 @@ namespace pro { } }; + template + struct Array : public std::array, Token { + using std::array::array; + explicit Array(const std::array& arr) noexcept + : std::array { arr } { } + + template + requires (sizeof...(Args) == N) + explicit Array(Args&&... args) noexcept + : std::array { + std::forward(args)...} {} + + void apply(auto& self) const + requires requires {setter(self, *this);} + { + setter(self, *this); + } + }; + // template // Vector::Vector(const QVector &vec) noexcept:QVector { vec } { }