2026-03-09 16:50:21 +08:00
|
|
|
|
#include "CustomQwtPlot.h"
|
|
|
|
|
|
#include <QwtPlotCurve>
|
|
|
|
|
|
#include <QwtLegend>
|
|
|
|
|
|
#include <QwtPlotCanvas>
|
2026-03-12 20:23:55 +08:00
|
|
|
|
#include <QwtText>
|
|
|
|
|
|
#include <QwtPlotMarker>
|
2026-03-13 15:36:08 +08:00
|
|
|
|
#include <QwtPlotZoneItem>
|
|
|
|
|
|
#include <QwtScaleMap>
|
|
|
|
|
|
#include <QwtScaleDiv>
|
2026-03-09 16:50:21 +08:00
|
|
|
|
#include <QPen>
|
2026-03-13 15:36:08 +08:00
|
|
|
|
#include <QMouseEvent>
|
|
|
|
|
|
#include <QwtScaleWidget>
|
2026-03-09 16:50:21 +08:00
|
|
|
|
|
|
|
|
|
|
CustomQwtPlot::CustomQwtPlot(QWidget *parent)
|
|
|
|
|
|
: QwtPlot(parent)
|
|
|
|
|
|
{
|
2026-03-13 15:36:08 +08:00
|
|
|
|
}
|
2026-03-09 16:50:21 +08:00
|
|
|
|
|
2026-03-13 15:36:08 +08:00
|
|
|
|
void CustomQwtPlot::SetEventFilterFunc(std::function<bool (QObject *, QEvent *)> event_filter_func)
|
|
|
|
|
|
{
|
|
|
|
|
|
this->_event_filter_func = event_filter_func;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void CustomQwtPlot::SetXaxisDragScale(bool enable)
|
|
|
|
|
|
{
|
|
|
|
|
|
QwtScaleWidget *x_scale = axisWidget(QwtPlot::xBottom);
|
|
|
|
|
|
x_scale->setMouseTracking(enable);
|
|
|
|
|
|
x_scale->installEventFilter(enable ? this : nullptr);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void CustomQwtPlot::ResetPlot()
|
|
|
|
|
|
{
|
|
|
|
|
|
this->setAxisScale(QwtPlot::xBottom, _init_x_min, _init_x_max);
|
|
|
|
|
|
this->setAxisScale(QwtPlot::yLeft, _init_y_min, _init_y_max);
|
|
|
|
|
|
this->replot();
|
2026-03-09 16:50:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-12 20:23:55 +08:00
|
|
|
|
QwtPlotCurve *CustomQwtPlot::GetCurve(const QString &curve_name)
|
2026-03-09 16:50:21 +08:00
|
|
|
|
{
|
2026-03-12 20:23:55 +08:00
|
|
|
|
return _curves.value(curve_name, nullptr);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QList<QwtPlotCurve *> CustomQwtPlot::GetCurveList() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return _curves.values();
|
2026-03-09 16:50:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void CustomQwtPlot::AddCurve(QwtPlotCurve *curve)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (curve) {
|
2026-03-12 20:23:55 +08:00
|
|
|
|
curve->setPen(QPen(getDistinctColorForManyCurves(_curves.count()), 1));
|
2026-03-09 16:50:21 +08:00
|
|
|
|
curve->attach(this);
|
2026-03-12 20:23:55 +08:00
|
|
|
|
_curves[curve->title().text()] = curve;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QwtPlotMarker *CustomQwtPlot::GetMarker(const QString &marker_name, const QString &postion)
|
|
|
|
|
|
{
|
|
|
|
|
|
return _markers.value(marker_name).value(postion);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QList<QwtPlotMarker *> CustomQwtPlot::GetMarkerList() const
|
|
|
|
|
|
{
|
|
|
|
|
|
QList<QwtPlotMarker *> markers;
|
|
|
|
|
|
for (auto tmp_markers : this->_markers) {
|
|
|
|
|
|
markers.append(tmp_markers.values());
|
|
|
|
|
|
}
|
|
|
|
|
|
return markers;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void CustomQwtPlot::AddMarker(QwtPlotMarker *marker, const QString &marker_name, const QString &postion)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (marker) {
|
|
|
|
|
|
QwtPlotCurve* curve = GetCurve(marker_name);
|
|
|
|
|
|
if (curve) {
|
|
|
|
|
|
QPen pen = curve->pen();
|
|
|
|
|
|
pen.setWidth(2);
|
|
|
|
|
|
marker->setLinePen(pen);
|
|
|
|
|
|
marker->attach(this);
|
|
|
|
|
|
_markers[marker_name][postion] = marker;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void CustomQwtPlot::RemoveMarker(const QString &marker_name, const QString &postion)
|
|
|
|
|
|
{
|
|
|
|
|
|
QwtPlotMarker* marker = GetMarker(marker_name, postion);
|
|
|
|
|
|
if (marker) {
|
|
|
|
|
|
marker->detach();
|
|
|
|
|
|
delete marker;
|
|
|
|
|
|
}
|
|
|
|
|
|
_markers[marker_name].remove(postion);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void CustomQwtPlot::CleanMarkers()
|
|
|
|
|
|
{
|
|
|
|
|
|
QList<QwtPlotMarker *> markers = GetMarkerList();
|
|
|
|
|
|
for (auto marker : markers) {
|
|
|
|
|
|
if (marker) {
|
|
|
|
|
|
marker->detach();
|
|
|
|
|
|
delete marker;
|
|
|
|
|
|
}
|
2026-03-09 16:50:21 +08:00
|
|
|
|
}
|
2026-03-12 20:23:55 +08:00
|
|
|
|
this->_markers.clear();
|
2026-03-09 16:50:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-13 15:36:08 +08:00
|
|
|
|
QwtPlotZoneItem* CustomQwtPlot::GetZoneItem(const QString& zone_item_name)
|
|
|
|
|
|
{
|
|
|
|
|
|
return _zone_items.value(zone_item_name);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QList<QwtPlotZoneItem *> CustomQwtPlot::GetZoneItemList() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return this->_zone_items.values();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void CustomQwtPlot::AddZoneItem(QwtPlotZoneItem *zone_item, const QString &zone_item_name)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (zone_item) {
|
|
|
|
|
|
zone_item->setPen(Qt::transparent); // 无边框
|
|
|
|
|
|
zone_item->setBrush(QColor(0, 120, 215, 95)); // 半透明蓝色(RGBA:A=80 透明度)
|
|
|
|
|
|
zone_item->attach(this);
|
|
|
|
|
|
_zone_items[zone_item_name] = zone_item;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void CustomQwtPlot::RemoveZoneItem(const QString& zone_item_name)
|
|
|
|
|
|
{
|
|
|
|
|
|
QwtPlotZoneItem* zone_item = GetZoneItem(zone_item_name);
|
|
|
|
|
|
if (zone_item) {
|
|
|
|
|
|
zone_item->detach();
|
|
|
|
|
|
delete zone_item;
|
|
|
|
|
|
}
|
|
|
|
|
|
_zone_items.remove(zone_item_name);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void CustomQwtPlot::CleanZoneItems()
|
|
|
|
|
|
{
|
|
|
|
|
|
QList<QwtPlotZoneItem *> zone_items = GetZoneItemList();
|
|
|
|
|
|
for (auto zone_item : zone_items) {
|
|
|
|
|
|
if (zone_item) {
|
|
|
|
|
|
zone_item->detach();
|
|
|
|
|
|
delete zone_item;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
this->_zone_items.clear();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool CustomQwtPlot::eventFilter(QObject *obj, QEvent *event)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (this->_event_filter_func) {
|
|
|
|
|
|
bool filted = this->_event_filter_func(obj, event);
|
|
|
|
|
|
if (filted) {
|
|
|
|
|
|
return filted;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (dynamic_cast<QwtScaleWidget*>(obj) != this->axisWidget(QwtPlot::xBottom))
|
|
|
|
|
|
return false;
|
|
|
|
|
|
QMouseEvent *me = static_cast<QMouseEvent*>(event);
|
|
|
|
|
|
QwtScaleMap map = this->canvasMap(QwtPlot::xBottom);
|
|
|
|
|
|
// ===================== 1. 鼠标拖动 X 轴 =====================
|
|
|
|
|
|
if (event->type() == QEvent::MouseButtonPress) {
|
|
|
|
|
|
this->_is_dragging = true;
|
|
|
|
|
|
this->_last_pos = me->pos();
|
|
|
|
|
|
return true;
|
|
|
|
|
|
} else if (event->type() == QEvent::MouseMove && this->_is_dragging) {
|
|
|
|
|
|
// 计算坐标偏移
|
|
|
|
|
|
double old_x = map.invTransform(_last_pos.x());
|
|
|
|
|
|
double new_x = map.invTransform(me->pos().x());
|
|
|
|
|
|
double dx = old_x - new_x;
|
|
|
|
|
|
// 移动 X 轴
|
|
|
|
|
|
double x_min = axisScaleDiv(QwtPlot::xBottom).lowerBound() + dx;
|
|
|
|
|
|
double x_max = axisScaleDiv(QwtPlot::xBottom).upperBound() + dx;
|
|
|
|
|
|
this->setAxisScale(QwtPlot::xBottom, x_min, x_max);
|
|
|
|
|
|
this->replot();
|
|
|
|
|
|
this->_last_pos = me->pos();
|
|
|
|
|
|
return true;
|
|
|
|
|
|
} else if (event->type() == QEvent::MouseButtonRelease) {
|
|
|
|
|
|
this->_is_dragging = false;
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
// ===================== 2. 滚轮缩放 X 轴 =====================
|
|
|
|
|
|
if (event->type() == QEvent::Wheel) {
|
|
|
|
|
|
QWheelEvent *we = static_cast<QWheelEvent*>(event);
|
|
|
|
|
|
double scale = we->angleDelta().y() > 0 ? 0.8 : 1.25; // 放大/缩小
|
|
|
|
|
|
// 以鼠标所在位置为中心缩放
|
|
|
|
|
|
double mouse_x = map.invTransform(we->pos().x());
|
|
|
|
|
|
double x1 = this->axisScaleDiv(QwtPlot::xBottom).lowerBound();
|
|
|
|
|
|
double x2 = this->axisScaleDiv(QwtPlot::xBottom).upperBound();
|
|
|
|
|
|
double new1 = mouse_x - (mouse_x - x1) * scale;
|
|
|
|
|
|
double new2 = mouse_x + (x2 - mouse_x) * scale;
|
|
|
|
|
|
this->setAxisScale(QwtPlot::xBottom, new1, new2);
|
|
|
|
|
|
this->replot();
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
return QwtPlot::eventFilter(obj, event);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void CustomQwtPlot::showEvent(QShowEvent *event)
|
|
|
|
|
|
{
|
|
|
|
|
|
Q_UNUSED(event);
|
|
|
|
|
|
this->updateAxes();
|
|
|
|
|
|
double x_min = this->axisScaleDiv(QwtPlot::xBottom).lowerBound();
|
|
|
|
|
|
double x_max = this->axisScaleDiv(QwtPlot::xBottom).upperBound();
|
|
|
|
|
|
double y_min = this->axisScaleDiv(QwtPlot::yLeft).lowerBound();
|
|
|
|
|
|
double y_max = this->axisScaleDiv(QwtPlot::yLeft).upperBound();
|
|
|
|
|
|
this->_init_x_min = x_min;
|
|
|
|
|
|
this->_init_x_max = x_max;
|
|
|
|
|
|
this->_init_y_min = y_min;
|
|
|
|
|
|
this->_init_y_max = y_max;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
CustomQwtPlotXaxisPanner::CustomQwtPlotXaxisPanner(QWidget *canvas)
|
|
|
|
|
|
: QwtPlotPanner(canvas)
|
|
|
|
|
|
{
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void CustomQwtPlotXaxisPanner::moveCanvas(int dx, int dy)
|
|
|
|
|
|
{
|
|
|
|
|
|
QwtPlotPanner::moveCanvas(dx, 0);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
CustomQwtPlotXaxisMagnifier::CustomQwtPlotXaxisMagnifier(QWidget *canvas)
|
|
|
|
|
|
: QwtPlotMagnifier(canvas)
|
|
|
|
|
|
{
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void CustomQwtPlotXaxisMagnifier::rescale(double factor)
|
|
|
|
|
|
{
|
|
|
|
|
|
factor = qBound(0.1, factor, 10.0);
|
|
|
|
|
|
|
|
|
|
|
|
QwtScaleMap x_map = plot()->canvasMap(QwtPlot::xBottom);
|
|
|
|
|
|
double center = x_map.invTransform(plot()->canvas()->width() / 2);
|
|
|
|
|
|
plot()->setAxisScale(
|
|
|
|
|
|
QwtPlot::xBottom,
|
|
|
|
|
|
center - (center - x_map.s1()) * factor,
|
|
|
|
|
|
center + (x_map.s2() - center) * factor
|
|
|
|
|
|
);
|
|
|
|
|
|
plot()->replot();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-09 16:50:21 +08:00
|
|
|
|
QColor getDistinctColorForManyCurves(int curve_index)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 1. 定义基础色相(覆盖不同主色系,0-360度)
|
|
|
|
|
|
const QList<int> base_hues = {
|
|
|
|
|
|
0, // 红色
|
|
|
|
|
|
30, // 橙色
|
|
|
|
|
|
60, // 黄色
|
|
|
|
|
|
90, // 黄绿色
|
|
|
|
|
|
120, // 绿色
|
|
|
|
|
|
150, // 青绿色
|
|
|
|
|
|
180, // 青色
|
|
|
|
|
|
210, // 天蓝色
|
|
|
|
|
|
240, // 蓝色
|
|
|
|
|
|
270, // 紫色
|
|
|
|
|
|
300, // 洋红色
|
|
|
|
|
|
330 // 玫红色
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 定义不同的饱和度/明度组合(避免颜色太暗/太灰)
|
|
|
|
|
|
const QList<QPair<int, int>> sv_combinations = {
|
|
|
|
|
|
{85, 90}, // 高饱和、高明度
|
|
|
|
|
|
{70, 85}, // 中高饱和、中高明度
|
|
|
|
|
|
{85, 75}, // 高饱和、中明度
|
|
|
|
|
|
{60, 80}, // 中饱和、中高明度
|
|
|
|
|
|
{75, 70}, // 中高饱和、中明度
|
|
|
|
|
|
{90, 80} // 极高饱和、中高明度
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 3. 计算当前曲线对应的色相和饱和度/明度
|
|
|
|
|
|
int hue_index = curve_index % base_hues.size(); // 循环使用基础色相
|
|
|
|
|
|
int sv_index = (curve_index / base_hues.size()) % sv_combinations.size(); // 循环使用饱和度/明度组合
|
|
|
|
|
|
|
|
|
|
|
|
// 4. 获取HSV参数(色相0-360,饱和度0-255,明度0-255)
|
|
|
|
|
|
int hue = base_hues[hue_index];
|
|
|
|
|
|
int saturation = sv_combinations[sv_index].first * 255 / 100; // 转换为0-255范围
|
|
|
|
|
|
int value = sv_combinations[sv_index].second * 255 / 100; // 转换为0-255范围
|
|
|
|
|
|
|
|
|
|
|
|
// 5. 生成并返回颜色(HSV转RGB)
|
|
|
|
|
|
QColor color;
|
|
|
|
|
|
color.setHsv(hue, saturation, value);
|
|
|
|
|
|
|
|
|
|
|
|
// 额外优化:避免极浅/极暗的颜色(保证曲线可见)
|
|
|
|
|
|
if (value < 50 * 255 / 100) {
|
|
|
|
|
|
value = 50 * 255 / 100;
|
|
|
|
|
|
color.setHsv(hue, saturation, value);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return color;
|
|
|
|
|
|
}
|
|
|
|
|
|
|