476 lines
14 KiB
C++
476 lines
14 KiB
C++
#include "CustomQwtPlot.h"
|
||
#include <QwtPlotCurve>
|
||
#include <QwtLegend>
|
||
#include <QwtPlotCanvas>
|
||
#include <QwtText>
|
||
#include <QwtPlotMarker>
|
||
#include <QwtPlotZoneItem>
|
||
#include <QwtScaleMap>
|
||
#include <QwtScaleDiv>
|
||
#include <QPen>
|
||
#include <QMouseEvent>
|
||
#include <QwtScaleWidget>
|
||
#include <QwtPlotPicker>
|
||
#include <QwtPlotShapeItem>
|
||
#include <QwtPickerDragRectMachine>
|
||
#include <QwtPlotMagnifier>
|
||
#include <QDebug>
|
||
|
||
CustomQwtPlotAxisPanner::CustomQwtPlotAxisPanner(QwtScaleWidget *scale_widget)
|
||
: QwtPlotPanner(scale_widget)
|
||
{
|
||
if (scale_widget) {
|
||
if (scale_widget->alignment() == QwtScaleDraw::BottomScale) {
|
||
this->setOrientations(Qt::Horizontal);
|
||
} else if (scale_widget->alignment() == QwtScaleDraw::LeftScale) {
|
||
this->setOrientations(Qt::Vertical);
|
||
} else if (scale_widget->alignment() == QwtScaleDraw::TopScale) {
|
||
this->setOrientations(Qt::Horizontal);
|
||
} else if (scale_widget->alignment() == QwtScaleDraw::RightScale) {
|
||
this->setOrientations(Qt::Vertical);
|
||
}
|
||
}
|
||
}
|
||
|
||
CustomQwtPlotAxisPanner::~CustomQwtPlotAxisPanner()
|
||
{
|
||
qDebug() << "~CustomQwtPlotXaxisPanner";
|
||
}
|
||
|
||
void CustomQwtPlotAxisPanner::moveCanvas(int dx, int dy)
|
||
{
|
||
QwtScaleWidget* scale_widget = dynamic_cast<QwtScaleWidget*>(this->canvas());
|
||
if (scale_widget) {
|
||
if (scale_widget->alignment() == QwtScaleDraw::BottomScale) {
|
||
QwtPlotPanner::moveCanvas(dx, 0);
|
||
} else if (scale_widget->alignment() == QwtScaleDraw::LeftScale) {
|
||
QwtPlotPanner::moveCanvas(0, dy);
|
||
} else if (scale_widget->alignment() == QwtScaleDraw::TopScale) {
|
||
QwtPlotPanner::moveCanvas(dx, 0);
|
||
} else if (scale_widget->alignment() == QwtScaleDraw::RightScale) {
|
||
QwtPlotPanner::moveCanvas(0, dy);
|
||
}
|
||
}
|
||
}
|
||
|
||
// CustomQwtPlotXaxisMagnifier::CustomQwtPlotXaxisMagnifier(QWidget *canvas)
|
||
// : QwtPlotMagnifier(canvas)
|
||
// {
|
||
// }
|
||
|
||
// CustomQwtPlotXaxisMagnifier::~CustomQwtPlotXaxisMagnifier()
|
||
// {
|
||
// qDebug() << "~CustomQwtPlotXaxisMagnifier";
|
||
// }
|
||
|
||
// 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();
|
||
// }
|
||
|
||
CustomQwtPlotXaxisSelector::CustomQwtPlotXaxisSelector(QWidget *canvas)
|
||
: QwtPlotPicker(QwtPlot::xBottom, QwtPlot::yLeft,
|
||
QwtPicker::NoRubberBand,
|
||
QwtPicker::AlwaysOn,
|
||
canvas)
|
||
, _min_x(0.0)
|
||
, _max_x(0.0)
|
||
{
|
||
this->setStateMachine(new QwtPickerDragRectMachine());
|
||
this->setRubberBandPen(QPen(Qt::NoPen));
|
||
|
||
_overlay = new QwtPlotShapeItem;
|
||
_overlay->setRenderHint(QwtPlotItem::RenderAntialiased);
|
||
_overlay->setPen(QPen(Qt::red, 2));
|
||
_overlay->setBrush(QBrush(QColor(0, 120, 210, 50)));
|
||
_overlay->setZ(100);
|
||
_overlay->attach(plot());
|
||
_overlay->setVisible(false);
|
||
}
|
||
|
||
CustomQwtPlotXaxisSelector::~CustomQwtPlotXaxisSelector()
|
||
{
|
||
qDebug() << "~CustomQwtPlotXaxisSelector";
|
||
}
|
||
|
||
|
||
double CustomQwtPlotXaxisSelector::selectedMinX() const
|
||
{
|
||
return _min_x;
|
||
}
|
||
|
||
double CustomQwtPlotXaxisSelector::selectedMaxX() const
|
||
{
|
||
return _max_x;
|
||
}
|
||
|
||
void CustomQwtPlotXaxisSelector::clearSelection()
|
||
{
|
||
_min_x = 0.0;
|
||
_max_x = 0.0;
|
||
if (_overlay)
|
||
_overlay->setVisible(false);
|
||
}
|
||
|
||
void CustomQwtPlotXaxisSelector::move(const QPoint & pos)
|
||
{
|
||
QPolygon points = pickedPoints();
|
||
if (points.size() < 2) {
|
||
clearSelection();
|
||
return QwtPlotPicker::move(pos);
|
||
}
|
||
|
||
QPointF p1 = invTransform(points.first());
|
||
QPointF p2 = invTransform(points.last());
|
||
|
||
// 计算X范围
|
||
_min_x = qMin(p1.x(), p2.x());
|
||
_max_x = qMax(p1.x(), p2.x());
|
||
|
||
// Y轴铺满
|
||
double y_min = plot()->axisScaleDiv(QwtPlot::yLeft).lowerBound();
|
||
double y_max = plot()->axisScaleDiv(QwtPlot::yLeft).upperBound();
|
||
QRectF area(_min_x, y_min, _max_x - _min_x, y_max - y_min);
|
||
|
||
_overlay->setRect(area);
|
||
_overlay->setVisible(true);
|
||
plot()->replot();
|
||
|
||
QwtPlotPicker::move(pos);
|
||
}
|
||
|
||
bool CustomQwtPlotXaxisSelector::end(bool ok)
|
||
{
|
||
if (!ok) {
|
||
clearSelection();
|
||
return QwtPlotPicker::end(ok);
|
||
}
|
||
if (_overlay->isVisible()) {
|
||
_overlay->setVisible(false);
|
||
plot()->replot();
|
||
}
|
||
if ( (_max_x - _min_x) > 3 ) {
|
||
emit selectionFinished(_min_x, _max_x);
|
||
}
|
||
return QwtPlotPicker::end(ok);
|
||
}
|
||
|
||
CustomQwtPlot::CustomQwtPlot(QWidget *parent)
|
||
: QwtPlot(parent)
|
||
{
|
||
|
||
}
|
||
|
||
CustomQwtPlot::~CustomQwtPlot()
|
||
{
|
||
qDebug() << "~CustomQwtPlot";
|
||
}
|
||
|
||
void CustomQwtPlot::SetAxisDragScale(Axis axis_id, bool enable)
|
||
{
|
||
switch (axis_id) {
|
||
case xBottom: {
|
||
if ( enable ) {
|
||
if ( nullptr == _x_axis_panner ) {
|
||
_x_axis_panner = new CustomQwtPlotAxisPanner(this->axisWidget(QwtPlot::xBottom));
|
||
_x_axis_panner->setMouseButton(Qt::LeftButton);
|
||
_x_axis_panner->setAxisEnabled(QwtPlot::xBottom, true);
|
||
_x_axis_panner->setAxisEnabled(QwtPlot::yLeft, false);
|
||
} else {
|
||
_x_axis_panner->setAxisEnabled(QwtPlot::xBottom, true);
|
||
_x_axis_panner->setAxisEnabled(QwtPlot::yLeft, false);
|
||
}
|
||
if ( nullptr == _x_axis_magnifier ) {
|
||
_x_axis_magnifier = new QwtPlotMagnifier(this->axisWidget(QwtPlot::xBottom));
|
||
_x_axis_magnifier->setAxisEnabled(QwtPlot::xBottom, true);
|
||
_x_axis_magnifier->setAxisEnabled(QwtPlot::yLeft, false);
|
||
_x_axis_magnifier->setWheelFactor(1.1);
|
||
} else {
|
||
_x_axis_magnifier->setAxisEnabled(QwtPlot::xBottom, true);
|
||
_x_axis_magnifier->setAxisEnabled(QwtPlot::yLeft, false);
|
||
}
|
||
} else {
|
||
if (_x_axis_panner) {
|
||
delete _x_axis_panner;
|
||
_x_axis_panner = nullptr;
|
||
}
|
||
if (_x_axis_magnifier) {
|
||
delete _x_axis_magnifier;
|
||
_x_axis_magnifier = nullptr;
|
||
}
|
||
}
|
||
|
||
} break;
|
||
case yLeft: {
|
||
if ( enable ) {
|
||
if (nullptr == _y_axis_panner) {
|
||
_y_axis_panner = new CustomQwtPlotAxisPanner(this->axisWidget(QwtPlot::yLeft));
|
||
_y_axis_panner->setMouseButton(Qt::LeftButton);
|
||
} else {
|
||
_y_axis_panner->setAxisEnabled(QwtPlot::xBottom, false);
|
||
_y_axis_panner->setAxisEnabled(QwtPlot::yLeft, true);
|
||
}
|
||
if (nullptr == _y_axis_magnifier) {
|
||
_y_axis_magnifier = new QwtPlotMagnifier(this->axisWidget(QwtPlot::yLeft));
|
||
_y_axis_magnifier->setAxisEnabled(QwtPlot::xBottom, false);
|
||
_y_axis_magnifier->setAxisEnabled(QwtPlot::yLeft, true);
|
||
_y_axis_magnifier->setWheelFactor(1.1);
|
||
} else {
|
||
_y_axis_magnifier->setAxisEnabled(QwtPlot::xBottom, false);
|
||
_y_axis_magnifier->setAxisEnabled(QwtPlot::yLeft, true);
|
||
}
|
||
} else {
|
||
if (_y_axis_panner) {
|
||
delete _y_axis_panner;
|
||
_y_axis_panner = nullptr;
|
||
}
|
||
if (_y_axis_magnifier) {
|
||
delete _y_axis_magnifier;
|
||
_y_axis_magnifier = nullptr;
|
||
}
|
||
}
|
||
} break;
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
|
||
void CustomQwtPlot::SetAxisInitRange(Axis axis_id, double min, double max)
|
||
{
|
||
this->setAxisScale(axis_id, min, max);
|
||
if ( axis_id == QwtPlot::xBottom ) {
|
||
_init_x_min = min;
|
||
_init_x_max = max;
|
||
}
|
||
if ( axis_id == QwtPlot::yLeft ) {
|
||
_init_y_min = min;
|
||
_init_y_max = max;
|
||
}
|
||
}
|
||
|
||
|
||
void CustomQwtPlot::RegisterEventFilterFunc(std::function<bool (QObject *, QEvent *)> event_filter_func)
|
||
{
|
||
this->_event_filter_func_list.push_back(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();
|
||
}
|
||
|
||
QwtPlotCurve *CustomQwtPlot::GetCurve(const QString &curve_name)
|
||
{
|
||
return _curves.value(curve_name, nullptr);
|
||
}
|
||
|
||
QList<QwtPlotCurve *> CustomQwtPlot::GetCurveList() const
|
||
{
|
||
return _curves.values();
|
||
}
|
||
|
||
void CustomQwtPlot::AddCurve(QwtPlotCurve *curve, bool auto_color)
|
||
{
|
||
if (curve) {
|
||
if ( auto_color ) {
|
||
curve->setPen(QPen(getDistinctColorForManyCurves(_curves.count()), 1));
|
||
}
|
||
curve->setZ(0);
|
||
curve->attach(this);
|
||
_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->setZ(20);
|
||
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;
|
||
}
|
||
}
|
||
this->_markers.clear();
|
||
}
|
||
|
||
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->setZ(10);
|
||
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)
|
||
{
|
||
bool has_filter = false;
|
||
for (auto filter : _event_filter_func_list) {
|
||
filter(obj, event);
|
||
has_filter = true;
|
||
}
|
||
if (has_filter) {
|
||
return true;
|
||
}
|
||
return QwtPlot::eventFilter(obj, event);
|
||
}
|
||
|
||
// void CustomQwtPlot::showEvent(QShowEvent *event)
|
||
// {
|
||
// Q_UNUSED(event);
|
||
// this->updateAxes();
|
||
// if ((0 == this->_init_x_max) && (0 == this->_init_y_max)) {
|
||
// // 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 = 0;
|
||
// this->_init_x_max = x_max;
|
||
// this->_init_y_min = 0;
|
||
// this->_init_y_max = y_max;
|
||
// this->setAxisScale(QwtPlot::xBottom, 0, x_max);
|
||
// }
|
||
// }
|
||
|
||
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;
|
||
}
|
||
|