// // Created by Codex on 2025/12/10. // #include "line_chart.hh" #include #include #include #include using creeper::line_widget::internal::LinePlot; LinePlot::LinePlot() { setBackground(Qt::transparent); } LinePlot::~LinePlot() = default; void LinePlot::load_theme_manager(creeper::ThemeManager& mgr) { mgr.append_handler(this, [this](const creeper::ThemeManager& manager) { scheme_ = manager.color_scheme(); apply_theme(); if (initialized_) { replot(); } }); } void LinePlot::set_data(const QVector& points) { points_ = points; trim_points(); if (initialized_) { update_graph(); } } void LinePlot::set_max_points(int count) { const int clamped = std::max(1, count); if (clamped == max_points_) { return; } max_points_ = clamped; trim_points(); if (initialized_) { update_graph(); } } void LinePlot::paintEvent(QPaintEvent* event) { if (!initialized_) { initialize_plot(); } QCustomPlot::paintEvent(event); } void LinePlot::initialize_plot() { if (initialized_) { return; } axisRect()->setAutoMargins(QCP::msNone); axisRect()->setMargins(QMargins(40, 12, 12, 18)); axisRect()->setBackground(QBrush(QColor(246, 249, 255))); legend->setVisible(false); xAxis->setVisible(true); yAxis->setVisible(true); xAxis->setRange(0.0, std::max(10, max_points_)); yAxis->setRange(0.0, default_y_range_); xAxis->grid()->setVisible(false); yAxis->grid()->setVisible(false); xAxis->setTicks(false); yAxis->setTicks(true); xAxis->setSubTicks(false); yAxis->setSubTicks(false); xAxis->setTickLength(0); yAxis->setTickLength(0); yAxis->setTickLabelPadding(6); yAxis->setLabelPadding(8); graph_ = addGraph(); graph_->setLineStyle(QCPGraph::lsLine); graph_->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, QPen(Qt::NoPen), QBrush(Qt::white), 6)); graph_->setBrush(QColor(16, 54, 128, 42)); graph_->setAntialiased(true); graph_->setAdaptiveSampling(true); initialized_ = true; apply_theme(); update_graph(); } void LinePlot::reset_graph_range() { xAxis->setRange(0.0, std::max(10, max_points_)); yAxis->setRange(0.0, default_y_range_); } void LinePlot::apply_theme() { QColor line_color{ 16, 54, 128 }; QColor text_color{ 30, 30, 30 }; QColor bg_color{ 232, 238, 248 }; if (scheme_.has_value()) { if (scheme_->primary.isValid()) { line_color = scheme_->primary; } if (scheme_->on_surface.isValid()) { text_color = scheme_->on_surface; } if (scheme_->surface_container_high.isValid()) { bg_color = scheme_->surface_container_high; } else if (scheme_->surface_container.isValid()) { bg_color = scheme_->surface_container; } else if (scheme_->surface.isValid()) { bg_color = scheme_->surface; } } QColor grid_color = bg_color; QPen axis_pen(bg_color); axis_pen.setWidthF(1.0); xAxis->setBasePen(axis_pen); yAxis->setBasePen(axis_pen); xAxis->setTickPen(axis_pen); yAxis->setTickPen(axis_pen); xAxis->setTickLabelColor(Qt::transparent); yAxis->setTickLabelColor(text_color); xAxis->setLabelColor(Qt::transparent); yAxis->setLabelColor(text_color); axisRect()->setBackground(bg_color); if (graph_) { QPen pen(line_color); pen.setWidthF(3.0); pen.setCapStyle(Qt::RoundCap); graph_->setPen(pen); QLinearGradient fill_grad(0, 0, 0, 1); fill_grad.setCoordinateMode(QGradient::CoordinateMode::ObjectBoundingMode); fill_grad.setColorAt(0.0, QColor(line_color.red(), line_color.green(), line_color.blue(), 70)); fill_grad.setColorAt(1.0, QColor(line_color.red(), line_color.green(), line_color.blue(), 18)); graph_->setBrush(QBrush(fill_grad)); auto scatter = graph_->scatterStyle(); scatter.setPen(QPen(line_color, 1.5)); scatter.setBrush(QBrush(QColor(bg_color).lighter(104))); scatter.setSize(7); graph_->setScatterStyle(scatter); } } void LinePlot::update_graph() { if (!initialized_) { return; } if (!graph_) { graph_ = addGraph(); apply_theme(); } if (points_.isEmpty()) { graph_->data()->clear(); reset_graph_range(); replot(); return; } QVector keys(points_.size()); QVector values(points_.size()); double min_key = std::numeric_limits::max(); double max_key = std::numeric_limits::lowest(); double min_val = std::numeric_limits::max(); double max_val = std::numeric_limits::lowest(); for (int i = 0; i < points_.size(); ++i) { const auto& pt = points_[i]; keys[i] = pt.x(); values[i] = pt.y(); min_key = std::min(min_key, pt.x()); max_key = std::max(max_key, pt.x()); min_val = std::min(min_val, pt.y()); max_val = std::max(max_val, pt.y()); } graph_->setData(keys, values, true); if (min_key == std::numeric_limits::max()) { reset_graph_range(); } else { const double key_span = std::max(1e-3, max_key - min_key); xAxis->setRange(min_key, max_key + key_span * 0.02); double value_span = max_val - min_val; if (value_span < 1e-3) { value_span = std::max(std::abs(max_val), 1.0); min_val = max_val - value_span * 0.5; } const double padding = std::max(value_span * 0.25, 1.0); yAxis->setRange(min_val - padding, max_val + padding); } replot(); } void LinePlot::trim_points() { if (max_points_ <= 0 || points_.size() <= max_points_) { return; } const int start = points_.size() - max_points_; points_ = points_.mid(start); } using namespace creeper; void SumLinePlot::paintEvent(QPaintEvent* event) { line_widget::internal::LinePlot::paintEvent(event); }