EnergySpectrumAnalyer/src/CustomQwtPlot.cpp

402 lines
11 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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 <QDebug>
CustomQwtPlot::CustomQwtPlot(QWidget *parent)
: QwtPlot(parent)
{
}
CustomQwtPlot::~CustomQwtPlot()
{
qDebug() << "~CustomQwtPlot";
}
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();
}
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)
{
if (curve) {
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)); // 半透明蓝色RGBAA=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)
{
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)
{
}
CustomQwtPlotXaxisPanner::~CustomQwtPlotXaxisPanner()
{
qDebug() << "~CustomQwtPlotXaxisPanner";
}
void CustomQwtPlotXaxisPanner::moveCanvas(int dx, int dy)
{
QwtPlotPanner::moveCanvas(dx, 0);
}
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);
}
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;
}