VisualizationGraphWidget.cpp
1577 lines
| 51.9 KiB
| text/x-c
|
CppLexer
r95 | #include "Visualization/VisualizationGraphWidget.h" | |||
Alexandre Leroux
|
r207 | #include "Visualization/IVisualizationWidgetVisitor.h" | ||
r960 | #include "Visualization/VisualizationCursorItem.h" | |||
Alexandre Leroux
|
r582 | #include "Visualization/VisualizationDefs.h" | ||
r243 | #include "Visualization/VisualizationGraphHelper.h" | |||
Alexandre Leroux
|
r480 | #include "Visualization/VisualizationGraphRenderingDelegate.h" | ||
r1085 | #include "Visualization/VisualizationMultiZoneSelectionDialog.h" | |||
r1044 | #include "Visualization/VisualizationSelectionZoneItem.h" | |||
r1049 | #include "Visualization/VisualizationSelectionZoneManager.h" | |||
#include "Visualization/VisualizationWidget.h" | ||||
r839 | #include "Visualization/VisualizationZoneWidget.h" | |||
r58 | #include "ui_VisualizationGraphWidget.h" | |||
r1077 | #include <Actions/ActionsGuiController.h> | |||
r1328 | #include <Actions/FilteringAction.h> | |||
r848 | #include <Common/MimeTypesDef.h> | |||
r1386 | #include <Common/containers.h> | |||
r1420 | #include <Data/DateTimeRangeHelper.h> | |||
r1075 | #include <DragAndDrop/DragDropGuiController.h> | |||
Alexandre Leroux
|
r470 | #include <Settings/SqpSettingsDefs.h> | ||
r235 | #include <SqpApplication.h> | |||
r878 | #include <Time/TimeController.h> | |||
r1420 | #include <Variable/Variable2.h> | |||
r1349 | #include <Variable/VariableController2.h> | |||
r235 | ||||
r118 | #include <unordered_map> | |||
Alexandre Leroux
|
r219 | Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget") | ||
r1420 | namespace | |||
{ | ||||
Alexandre Leroux
|
r179 | |||
r1047 | /// Key pressed to enable drag&drop in all modes | |||
const auto DRAG_DROP_MODIFIER = Qt::AltModifier; | ||||
Alexandre Leroux
|
r179 | /// Key pressed to enable zoom on horizontal axis | ||
r958 | const auto HORIZONTAL_ZOOM_MODIFIER = Qt::ControlModifier; | |||
Alexandre Leroux
|
r179 | |||
/// Key pressed to enable zoom on vertical axis | ||||
r958 | const auto VERTICAL_ZOOM_MODIFIER = Qt::ShiftModifier; | |||
/// Speed of a step of a wheel event for a pan, in percentage of the axis range | ||||
const auto PAN_SPEED = 5; | ||||
/// Key pressed to enable a calibration pan | ||||
const auto VERTICAL_PAN_MODIFIER = Qt::AltModifier; | ||||
Alexandre Leroux
|
r179 | |||
r1049 | /// Key pressed to enable multi selection of selection zones | |||
const auto MULTI_ZONE_SELECTION_MODIFIER = Qt::ControlModifier; | ||||
r959 | /// Minimum size for the zoom box, in percentage of the axis range | |||
const auto ZOOM_BOX_MIN_SIZE = 0.8; | ||||
r960 | /// Format of the dates appearing in the label of a cursor | |||
const auto CURSOR_LABELS_DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd\nhh:mm:ss:zzz"); | ||||
Alexandre Leroux
|
r179 | |||
} // namespace | ||||
r1420 | struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate | |||
{ | ||||
r118 | ||||
r1420 | explicit VisualizationGraphWidgetPrivate(const QString& name) | |||
: m_Name { name } | ||||
, m_Flags { GraphFlag::EnableAll } | ||||
, m_IsCalibration { false } | ||||
, m_RenderingDelegate { nullptr } | ||||
Alexandre Leroux
|
r480 | { | ||
r1364 | m_plot = new QCustomPlot(); | |||
// Necessary for all platform since Qt::AA_EnableHighDpiScaling is enable. | ||||
m_plot->setPlottingHint(QCP::phFastPolylines, true); | ||||
Alexandre Leroux
|
r480 | } | ||
r445 | ||||
r1420 | void updateData( | |||
PlottablesMap& plottables, std::shared_ptr<Variable2> variable, const DateTimeRange& range) | ||||
Alexandre Leroux
|
r1020 | { | ||
Alexandre Leroux
|
r1280 | VisualizationGraphHelper::updateData(plottables, variable, range); | ||
Alexandre Leroux
|
r1020 | |||
// Prevents that data has changed to update rendering | ||||
m_RenderingDelegate->onPlotUpdated(); | ||||
} | ||||
Alexandre Leroux
|
r724 | QString m_Name; | ||
r118 | // 1 variable -> n qcpplot | |||
r1420 | std::map<std::shared_ptr<Variable2>, PlottablesMap> m_VariableToPlotMultiMap; | |||
Alexandre Leroux
|
r1271 | GraphFlags m_Flags; | ||
r445 | bool m_IsCalibration; | |||
r1364 | QCustomPlot* m_plot; | |||
QPoint m_lastMousePos; | ||||
r1373 | QCPRange m_lastXRange; | |||
QCPRange m_lastYRange; | ||||
Alexandre Leroux
|
r480 | /// Delegate used to attach rendering features to the plot | ||
std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate; | ||||
r959 | ||||
r1364 | QCPItemRect* m_DrawingZoomRect = nullptr; | |||
r1420 | QStack<QPair<QCPRange, QCPRange>> m_ZoomStack; | |||
r1044 | ||||
r960 | std::unique_ptr<VisualizationCursorItem> m_HorizontalCursor = nullptr; | |||
std::unique_ptr<VisualizationCursorItem> m_VerticalCursor = nullptr; | ||||
r959 | ||||
r1420 | VisualizationSelectionZoneItem* m_DrawingZone = nullptr; | |||
VisualizationSelectionZoneItem* m_HoveredZone = nullptr; | ||||
QVector<VisualizationSelectionZoneItem*> m_SelectionZones; | ||||
r959 | ||||
r1051 | bool m_HasMovedMouse = false; // Indicates if the mouse moved in a releaseMouse even | |||
r1050 | ||||
r1292 | bool m_VariableAutoRangeOnInit = true; | |||
r1373 | inline void enterPlotDrag(const QPoint& position) | |||
r959 | { | |||
r1373 | m_lastMousePos = m_plot->mapFromParent(position); | |||
m_lastXRange = m_plot->xAxis->range(); | ||||
m_lastYRange = m_plot->yAxis->range(); | ||||
r1364 | } | |||
r1420 | inline bool isDrawingZoomRect() { return m_DrawingZoomRect != nullptr; } | |||
r1364 | void updateZoomRect(const QPoint& newPos) | |||
{ | ||||
r1420 | QPointF pos { m_plot->xAxis->pixelToCoord(newPos.x()), | |||
m_plot->yAxis->pixelToCoord(newPos.y()) }; | ||||
r1364 | m_DrawingZoomRect->bottomRight->setCoords(pos); | |||
m_plot->replot(QCustomPlot::rpQueuedReplot); | ||||
} | ||||
void applyZoomRect() | ||||
{ | ||||
auto axisX = m_plot->axisRect()->axis(QCPAxis::atBottom); | ||||
auto axisY = m_plot->axisRect()->axis(QCPAxis::atLeft); | ||||
r1420 | auto newAxisXRange = QCPRange { m_DrawingZoomRect->topLeft->coords().x(), | |||
m_DrawingZoomRect->bottomRight->coords().x() }; | ||||
r1364 | ||||
r1420 | auto newAxisYRange = QCPRange { m_DrawingZoomRect->topLeft->coords().y(), | |||
m_DrawingZoomRect->bottomRight->coords().y() }; | ||||
r1364 | ||||
removeDrawingRect(); | ||||
if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0) | ||||
r1420 | && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) | |||
{ | ||||
r1364 | m_ZoomStack.push(qMakePair(axisX->range(), axisY->range())); | |||
axisX->setRange(newAxisXRange); | ||||
axisY->setRange(newAxisYRange); | ||||
m_plot->replot(QCustomPlot::rpQueuedReplot); | ||||
} | ||||
} | ||||
r1420 | inline bool isDrawingZoneRect() { return m_DrawingZone != nullptr; } | |||
r1364 | void updateZoneRect(const QPoint& newPos) | |||
{ | ||||
m_DrawingZone->setEnd(m_plot->xAxis->pixelToCoord(newPos.x())); | ||||
m_plot->replot(QCustomPlot::rpQueuedReplot); | ||||
} | ||||
r1420 | void startDrawingRect(const QPoint& pos) | |||
r1364 | { | |||
removeDrawingRect(); | ||||
r959 | ||||
r1364 | auto axisPos = posToAxisPos(pos); | |||
r959 | ||||
r1420 | m_DrawingZoomRect = new QCPItemRect { m_plot }; | |||
r1044 | QPen p; | |||
p.setWidth(2); | ||||
m_DrawingZoomRect->setPen(p); | ||||
r959 | ||||
r1044 | m_DrawingZoomRect->topLeft->setCoords(axisPos); | |||
m_DrawingZoomRect->bottomRight->setCoords(axisPos); | ||||
r959 | } | |||
r1364 | void removeDrawingRect() | |||
r959 | { | |||
r1420 | if (m_DrawingZoomRect) | |||
{ | ||||
r1364 | m_plot->removeItem(m_DrawingZoomRect); // the item is deleted by QCustomPlot | |||
r1044 | m_DrawingZoomRect = nullptr; | |||
r1364 | m_plot->replot(QCustomPlot::rpQueuedReplot); | |||
r959 | } | |||
} | ||||
r1420 | void selectZone(const QPoint& pos) | |||
r1044 | { | |||
r1365 | auto zoneAtPos = selectionZoneAt(pos); | |||
r1420 | setSelectionZonesEditionEnabled( | |||
sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones); | ||||
r1365 | } | |||
r1420 | void startDrawingZone(const QPoint& pos) | |||
r1365 | { | |||
endDrawingZone(); | ||||
r1044 | ||||
r1364 | auto axisPos = posToAxisPos(pos); | |||
r1044 | ||||
r1420 | m_DrawingZone = new VisualizationSelectionZoneItem { m_plot }; | |||
r1044 | m_DrawingZone->setRange(axisPos.x(), axisPos.x()); | |||
m_DrawingZone->setEditionEnabled(false); | ||||
} | ||||
r1365 | void endDrawingZone() | |||
r1044 | { | |||
r1420 | if (m_DrawingZone) | |||
{ | ||||
r1044 | auto drawingZoneRange = m_DrawingZone->range(); | |||
r1420 | if (qAbs(drawingZoneRange.m_TEnd - drawingZoneRange.m_TStart) > 0) | |||
{ | ||||
r1044 | m_DrawingZone->setEditionEnabled(true); | |||
r1049 | addSelectionZone(m_DrawingZone); | |||
r1044 | } | |||
r1420 | else | |||
{ | ||||
r1365 | m_plot->removeItem(m_DrawingZone); | |||
r1044 | } | |||
r1365 | m_plot->replot(QCustomPlot::rpQueuedReplot); | |||
r1044 | m_DrawingZone = nullptr; | |||
} | ||||
} | ||||
r1365 | void moveSelectionZone(const QPoint& destination) | |||
{ | ||||
/* | ||||
* I give up on this for now | ||||
* @TODO implement this, the difficulty is that selection zones have their own | ||||
* event handling code which seems to rely on QCP GUI event handling propagation | ||||
* which was a realy bad design choice. | ||||
r1420 | */ | |||
r1365 | } | |||
r1044 | void setSelectionZonesEditionEnabled(bool value) | |||
{ | ||||
r1420 | for (auto s : m_SelectionZones) | |||
{ | ||||
r1044 | s->setEditionEnabled(value); | |||
} | ||||
} | ||||
r1420 | void addSelectionZone(VisualizationSelectionZoneItem* zone) { m_SelectionZones << zone; } | |||
r1049 | ||||
r1420 | VisualizationSelectionZoneItem* selectionZoneAt(const QPoint& pos) const | |||
r1047 | { | |||
r1420 | VisualizationSelectionZoneItem* selectionZoneItemUnderCursor = nullptr; | |||
r1047 | auto minDistanceToZone = -1; | |||
r1420 | for (auto zone : m_SelectionZones) | |||
{ | ||||
r1047 | auto distanceToZone = zone->selectTest(pos, false); | |||
if ((minDistanceToZone < 0 || distanceToZone <= minDistanceToZone) | ||||
r1420 | && distanceToZone >= 0 && distanceToZone < m_plot->selectionTolerance()) | |||
{ | ||||
r1047 | selectionZoneItemUnderCursor = zone; | |||
} | ||||
} | ||||
return selectionZoneItemUnderCursor; | ||||
} | ||||
r1420 | QVector<VisualizationSelectionZoneItem*> selectionZonesAt( | |||
const QPoint& pos, const QCustomPlot& plot) const | ||||
r1085 | { | |||
r1420 | QVector<VisualizationSelectionZoneItem*> zones; | |||
for (auto zone : m_SelectionZones) | ||||
{ | ||||
r1085 | auto distanceToZone = zone->selectTest(pos, false); | |||
r1420 | if (distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) | |||
{ | ||||
r1085 | zones << zone; | |||
} | ||||
} | ||||
return zones; | ||||
} | ||||
r1420 | void moveSelectionZoneOnTop(VisualizationSelectionZoneItem* zone, QCustomPlot& plot) | |||
r1085 | { | |||
r1420 | if (!m_SelectionZones.isEmpty() && m_SelectionZones.last() != zone) | |||
{ | ||||
r1085 | zone->moveToTop(); | |||
m_SelectionZones.removeAll(zone); | ||||
m_SelectionZones.append(zone); | ||||
} | ||||
} | ||||
r1420 | QPointF posToAxisPos(const QPoint& pos) const | |||
r959 | { | |||
r1364 | auto axisX = m_plot->axisRect()->axis(QCPAxis::atBottom); | |||
auto axisY = m_plot->axisRect()->axis(QCPAxis::atLeft); | ||||
r1420 | return QPointF { axisX->pixelToCoord(pos.x()), axisY->pixelToCoord(pos.y()) }; | |||
r959 | } | |||
r960 | ||||
r1420 | bool pointIsInAxisRect(const QPointF& axisPoint, QCustomPlot& plot) const | |||
r960 | { | |||
auto axisX = plot.axisRect()->axis(QCPAxis::atBottom); | ||||
auto axisY = plot.axisRect()->axis(QCPAxis::atLeft); | ||||
return axisX->range().contains(axisPoint.x()) && axisY->range().contains(axisPoint.y()); | ||||
} | ||||
r1363 | ||||
r1420 | inline QCPRange _pixDistanceToRange(double pos1, double pos2, QCPAxis* axis) | |||
r1363 | { | |||
if (axis->scaleType() == QCPAxis::stLinear) | ||||
{ | ||||
auto diff = axis->pixelToCoord(pos1) - axis->pixelToCoord(pos2); | ||||
r1420 | return QCPRange { axis->range().lower + diff, axis->range().upper + diff }; | |||
r1363 | } | |||
else | ||||
{ | ||||
auto diff = axis->pixelToCoord(pos1) / axis->pixelToCoord(pos2); | ||||
r1420 | return QCPRange { axis->range().lower * diff, axis->range().upper * diff }; | |||
r1363 | } | |||
} | ||||
r1420 | void setRange(const DateTimeRange& newRange, bool updateVar = true) | |||
r1363 | { | |||
r1377 | this->m_plot->xAxis->setRange(newRange.m_TStart, newRange.m_TEnd); | |||
r1420 | if (updateVar) | |||
r1363 | { | |||
r1420 | for (auto it = m_VariableToPlotMultiMap.begin(), end = m_VariableToPlotMultiMap.end(); | |||
r1363 | it != end; it = m_VariableToPlotMultiMap.upper_bound(it->first)) | |||
{ | ||||
r1373 | sqpApp->variableController().asyncChangeRange(it->first, newRange); | |||
r1363 | } | |||
} | ||||
r1377 | m_plot->replot(QCustomPlot::rpQueuedReplot); | |||
r1363 | } | |||
r1420 | void setRange(const QCPRange& newRange) | |||
r1373 | { | |||
r1420 | auto graphRange = DateTimeRange { newRange.lower, newRange.upper }; | |||
r1373 | setRange(graphRange); | |||
} | ||||
r1420 | void rescaleY() { m_plot->yAxis->rescale(true); } | |||
r1377 | ||||
r1420 | std::tuple<double, double> moveGraph(const QPoint& destination) | |||
r1363 | { | |||
r1373 | auto currentPos = m_plot->mapFromParent(destination); | |||
r1364 | auto xAxis = m_plot->axisRect()->rangeDragAxis(Qt::Horizontal); | |||
auto yAxis = m_plot->axisRect()->rangeDragAxis(Qt::Vertical); | ||||
r1373 | auto oldXRange = xAxis->range(); | |||
auto oldYRange = yAxis->range(); | ||||
double dx = xAxis->pixelToCoord(m_lastMousePos.x()) - xAxis->pixelToCoord(currentPos.x()); | ||||
r1420 | xAxis->setRange(m_lastXRange.lower + dx, m_lastXRange.upper + dx); | |||
if (yAxis->scaleType() == QCPAxis::stLinear) | ||||
r1373 | { | |||
r1420 | double dy | |||
= yAxis->pixelToCoord(m_lastMousePos.y()) - yAxis->pixelToCoord(currentPos.y()); | ||||
yAxis->setRange(m_lastYRange.lower + dy, m_lastYRange.upper + dy); | ||||
r1373 | } | |||
else | ||||
{ | ||||
r1420 | double dy | |||
= yAxis->pixelToCoord(m_lastMousePos.y()) / yAxis->pixelToCoord(currentPos.y()); | ||||
yAxis->setRange(m_lastYRange.lower * dy, m_lastYRange.upper * dy); | ||||
r1373 | } | |||
auto newXRange = xAxis->range(); | ||||
auto newYRange = yAxis->range(); | ||||
r1363 | setRange(xAxis->range()); | |||
r1420 | // m_lastMousePos = currentPos; | |||
return { newXRange.lower - oldXRange.lower, newYRange.lower - oldYRange.lower }; | ||||
r1373 | } | |||
void zoom(double factor, int center, Qt::Orientation orientation) | ||||
{ | ||||
r1420 | QCPAxis* axis = m_plot->axisRect()->rangeZoomAxis(orientation); | |||
r1373 | axis->scaleRange(factor, axis->pixelToCoord(center)); | |||
if (orientation == Qt::Horizontal) | ||||
setRange(axis->range()); | ||||
m_plot->replot(QCustomPlot::rpQueuedReplot); | ||||
} | ||||
r1420 | void transform(const DateTimeRangeTransformation& tranformation) | |||
r1377 | { | |||
auto graphRange = m_plot->xAxis->range(); | ||||
r1420 | DateTimeRange range { graphRange.lower, graphRange.upper }; | |||
r1377 | range = range.transform(tranformation); | |||
setRange(range); | ||||
m_plot->replot(QCustomPlot::rpQueuedReplot); | ||||
} | ||||
r1373 | void move(double dx, double dy) | |||
{ | ||||
auto xAxis = m_plot->axisRect()->rangeDragAxis(Qt::Horizontal); | ||||
auto yAxis = m_plot->axisRect()->rangeDragAxis(Qt::Vertical); | ||||
r1420 | xAxis->setRange(QCPRange(xAxis->range().lower + dx, xAxis->range().upper + dx)); | |||
yAxis->setRange(QCPRange(yAxis->range().lower + dy, yAxis->range().upper + dy)); | ||||
r1373 | setRange(xAxis->range()); | |||
m_plot->replot(QCustomPlot::rpQueuedReplot); | ||||
} | ||||
void move(double factor, Qt::Orientation orientation) | ||||
{ | ||||
auto oldRange = m_plot->xAxis->range(); | ||||
r1420 | QCPAxis* axis = m_plot->axisRect()->rangeDragAxis(orientation); | |||
if (m_plot->xAxis->scaleType() == QCPAxis::stLinear) | ||||
{ | ||||
r1373 | double rg = (axis->range().upper - axis->range().lower) * (factor / 10); | |||
axis->setRange(axis->range().lower + (rg), axis->range().upper + (rg)); | ||||
} | ||||
r1420 | else if (m_plot->xAxis->scaleType() == QCPAxis::stLogarithmic) | |||
{ | ||||
r1373 | int start = 0, stop = 0; | |||
double diff = 0.; | ||||
r1420 | if (factor > 0.0) | |||
{ | ||||
r1373 | stop = m_plot->width() * factor / 10; | |||
start = 2 * m_plot->width() * factor / 10; | ||||
} | ||||
r1420 | if (factor < 0.0) | |||
{ | ||||
r1373 | factor *= -1.0; | |||
start = m_plot->width() * factor / 10; | ||||
stop = 2 * m_plot->width() * factor / 10; | ||||
} | ||||
diff = axis->pixelToCoord(start) / axis->pixelToCoord(stop); | ||||
axis->setRange(m_plot->axisRect()->rangeDragAxis(orientation)->range().lower * diff, | ||||
r1420 | m_plot->axisRect()->rangeDragAxis(orientation)->range().upper * diff); | |||
r1373 | } | |||
if (orientation == Qt::Horizontal) | ||||
setRange(axis->range()); | ||||
m_plot->replot(QCustomPlot::rpQueuedReplot); | ||||
r1363 | } | |||
r118 | }; | |||
r1420 | VisualizationGraphWidget::VisualizationGraphWidget(const QString& name, QWidget* parent) | |||
: VisualizationDragWidget { parent } | ||||
, ui { new Ui::VisualizationGraphWidget } | ||||
, impl { spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name) } | ||||
r58 | { | |||
ui->setupUi(this); | ||||
r1364 | this->layout()->addWidget(impl->m_plot); | |||
Alexandre Leroux
|
r266 | // 'Close' options : widget is deleted when closed | ||
setAttribute(Qt::WA_DeleteOnClose); | ||||
Alexandre Leroux
|
r196 | |||
Alexandre Leroux
|
r724 | // The delegate must be initialized after the ui as it uses the plot | ||
impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this); | ||||
r960 | // Init the cursors | |||
impl->m_HorizontalCursor = std::make_unique<VisualizationCursorItem>(&plot()); | ||||
impl->m_HorizontalCursor->setOrientation(Qt::Horizontal); | ||||
impl->m_VerticalCursor = std::make_unique<VisualizationCursorItem>(&plot()); | ||||
impl->m_VerticalCursor->setOrientation(Qt::Vertical); | ||||
r1362 | this->setFocusPolicy(Qt::WheelFocus); | |||
r1363 | this->setMouseTracking(true); | |||
r1364 | impl->m_plot->setAttribute(Qt::WA_TransparentForMouseEvents); | |||
impl->m_plot->setContextMenuPolicy(Qt::CustomContextMenu); | ||||
impl->m_plot->setParent(this); | ||||
r1381 | ||||
r1420 | connect(&sqpApp->variableController(), &VariableController2::variableDeleted, this, | |||
&VisualizationGraphWidget::variableDeleted); | ||||
r58 | } | |||
Alexandre Leroux
|
r227 | |||
r58 | VisualizationGraphWidget::~VisualizationGraphWidget() | |||
{ | ||||
delete ui; | ||||
} | ||||
r118 | ||||
r1420 | VisualizationZoneWidget* VisualizationGraphWidget::parentZoneWidget() const noexcept | |||
r839 | { | |||
auto parent = parentWidget(); | ||||
r1420 | while (parent != nullptr && !qobject_cast<VisualizationZoneWidget*>(parent)) | |||
{ | ||||
r839 | parent = parent->parentWidget(); | |||
r846 | } | |||
r839 | ||||
r1420 | return qobject_cast<VisualizationZoneWidget*>(parent); | |||
r839 | } | |||
r1420 | VisualizationWidget* VisualizationGraphWidget::parentVisualizationWidget() const | |||
r1049 | { | |||
auto parent = parentWidget(); | ||||
r1420 | while (parent != nullptr && !qobject_cast<VisualizationWidget*>(parent)) | |||
{ | ||||
r1049 | parent = parent->parentWidget(); | |||
} | ||||
r1420 | return qobject_cast<VisualizationWidget*>(parent); | |||
r1049 | } | |||
Alexandre Leroux
|
r1271 | void VisualizationGraphWidget::setFlags(GraphFlags flags) | ||
r444 | { | |||
Alexandre Leroux
|
r1271 | impl->m_Flags = std::move(flags); | ||
r444 | } | |||
r1420 | void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable2> variable, DateTimeRange range) | |||
r118 | { | |||
Alexandre Leroux
|
r1284 | // Uses delegate to create the qcpplot components according to the variable | ||
r1364 | auto createdPlottables = VisualizationGraphHelper::create(variable, *impl->m_plot); | |||
Alexandre Leroux
|
r1284 | |||
// Sets graph properties | ||||
impl->m_RenderingDelegate->setGraphProperties(*variable, createdPlottables); | ||||
r314 | ||||
r1420 | impl->m_VariableToPlotMultiMap.insert({ variable, std::move(createdPlottables) }); | |||
Alexandre Leroux
|
r1284 | |||
r1378 | setGraphRange(range); | |||
Alexandre Leroux
|
r1284 | // If the variable already has its data loaded, load its units and its range in the graph | ||
r1420 | if (variable->data() != nullptr) | |||
{ | ||||
r1351 | impl->m_RenderingDelegate->setAxesUnits(*variable); | |||
r1378 | } | |||
else | ||||
{ | ||||
r1420 | auto context = new QObject { this }; | |||
connect( | ||||
variable.get(), &Variable2::updated, context, [this, variable, context, range](QUuid) { | ||||
this->impl->m_RenderingDelegate->setAxesUnits(*variable); | ||||
this->impl->m_plot->replot(QCustomPlot::rpQueuedReplot); | ||||
delete context; | ||||
}); | ||||
Alexandre Leroux
|
r1284 | } | ||
r1352 | //@TODO this is bad! when variable is moved to another graph it still fires | |||
// even if this has been deleted | ||||
r1420 | connect(variable.get(), &Variable2::updated, this, &VisualizationGraphWidget::variableUpdated); | |||
r1362 | this->onUpdateVarDisplaying(variable, range); // My bullshit | |||
r548 | emit variableAdded(variable); | |||
r314 | } | |||
r1420 | void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable2> variable) noexcept | |||
Alexandre Leroux
|
r270 | { | ||
Alexandre Leroux
|
r271 | // Each component associated to the variable : | ||
// - is removed from qcpplot (which deletes it) | ||||
// - is no longer referenced in the map | ||||
Alexandre Leroux
|
r582 | auto variableIt = impl->m_VariableToPlotMultiMap.find(variable); | ||
r1420 | if (variableIt != impl->m_VariableToPlotMultiMap.cend()) | |||
{ | ||||
Alexandre Leroux
|
r737 | emit variableAboutToBeRemoved(variable); | ||
r1420 | auto& plottablesMap = variableIt->second; | |||
Alexandre Leroux
|
r582 | |||
for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend(); | ||||
r1420 | plottableIt != plottableEnd;) | |||
{ | ||||
r1364 | impl->m_plot->removePlottable(plottableIt->second); | |||
Alexandre Leroux
|
r582 | plottableIt = plottablesMap.erase(plottableIt); | ||
} | ||||
impl->m_VariableToPlotMultiMap.erase(variableIt); | ||||
Alexandre Leroux
|
r271 | } | ||
// Updates graph | ||||
r1364 | impl->m_plot->replot(QCustomPlot::rpQueuedReplot); | |||
Alexandre Leroux
|
r270 | } | ||
r1420 | std::vector<std::shared_ptr<Variable2>> VisualizationGraphWidget::variables() const | |||
r839 | { | |||
r1420 | auto variables = std::vector<std::shared_ptr<Variable2>> {}; | |||
r844 | for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap); | |||
r1420 | it != std::cend(impl->m_VariableToPlotMultiMap); ++it) | |||
{ | ||||
r1362 | variables.push_back(it->first); | |||
r839 | } | |||
return variables; | ||||
} | ||||
r1420 | void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable2> variable) | |||
r548 | { | |||
r1420 | if (!variable) | |||
{ | ||||
Alexandre Leroux
|
r900 | qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null"; | ||
return; | ||||
} | ||||
r1364 | VisualizationGraphHelper::setYAxisRange(variable, *impl->m_plot); | |||
r548 | } | |||
r1346 | DateTimeRange VisualizationGraphWidget::graphRange() const noexcept | |||
r444 | { | |||
r1364 | auto graphRange = impl->m_plot->xAxis->range(); | |||
r1420 | return DateTimeRange { graphRange.lower, graphRange.upper }; | |||
r444 | } | |||
r1420 | void VisualizationGraphWidget::setGraphRange( | |||
const DateTimeRange& range, bool updateVar, bool forward) | ||||
r444 | { | |||
r1377 | impl->setRange(range, updateVar); | |||
r1420 | if (forward) | |||
r1377 | { | |||
r1384 | emit this->setrange_sig(this->graphRange(), true, false); | |||
r1377 | } | |||
r438 | } | |||
r1292 | void VisualizationGraphWidget::setAutoRangeOnVariableInitialization(bool value) | |||
{ | ||||
impl->m_VariableAutoRangeOnInit = value; | ||||
} | ||||
r1346 | QVector<DateTimeRange> VisualizationGraphWidget::selectionZoneRanges() const | |||
r1048 | { | |||
r1346 | QVector<DateTimeRange> ranges; | |||
r1420 | for (auto zone : impl->m_SelectionZones) | |||
{ | ||||
r1048 | ranges << zone->range(); | |||
} | ||||
return ranges; | ||||
} | ||||
r1420 | void VisualizationGraphWidget::addSelectionZones(const QVector<DateTimeRange>& ranges) | |||
r1048 | { | |||
r1420 | for (const auto& range : ranges) | |||
{ | ||||
r1049 | // note: ownership is transfered to QCustomPlot | |||
r1048 | auto zone = new VisualizationSelectionZoneItem(&plot()); | |||
zone->setRange(range.m_TStart, range.m_TEnd); | ||||
r1049 | impl->addSelectionZone(zone); | |||
r1048 | } | |||
plot().replot(QCustomPlot::rpQueuedReplot); | ||||
} | ||||
r1420 | VisualizationSelectionZoneItem* VisualizationGraphWidget::addSelectionZone( | |||
const QString& name, const DateTimeRange& range) | ||||
r1293 | { | |||
// note: ownership is transfered to QCustomPlot | ||||
auto zone = new VisualizationSelectionZoneItem(&plot()); | ||||
zone->setName(name); | ||||
zone->setRange(range.m_TStart, range.m_TEnd); | ||||
impl->addSelectionZone(zone); | ||||
plot().replot(QCustomPlot::rpQueuedReplot); | ||||
return zone; | ||||
} | ||||
r1420 | void VisualizationGraphWidget::removeSelectionZone(VisualizationSelectionZoneItem* selectionZone) | |||
r1079 | { | |||
r1082 | parentVisualizationWidget()->selectionZoneManager().setSelected(selectionZone, false); | |||
r1420 | if (impl->m_HoveredZone == selectionZone) | |||
{ | ||||
r1082 | impl->m_HoveredZone = nullptr; | |||
setCursor(Qt::ArrowCursor); | ||||
} | ||||
r1079 | impl->m_SelectionZones.removeAll(selectionZone); | |||
plot().removeItem(selectionZone); | ||||
plot().replot(QCustomPlot::rpQueuedReplot); | ||||
} | ||||
r1046 | void VisualizationGraphWidget::undoZoom() | |||
{ | ||||
auto zoom = impl->m_ZoomStack.pop(); | ||||
auto axisX = plot().axisRect()->axis(QCPAxis::atBottom); | ||||
auto axisY = plot().axisRect()->axis(QCPAxis::atLeft); | ||||
axisX->setRange(zoom.first); | ||||
axisY->setRange(zoom.second); | ||||
plot().replot(QCustomPlot::rpQueuedReplot); | ||||
} | ||||
r1420 | void VisualizationGraphWidget::zoom( | |||
double factor, int center, Qt::Orientation orientation, bool forward) | ||||
r1373 | { | |||
impl->zoom(factor, center, orientation); | ||||
r1420 | if (forward && orientation == Qt::Horizontal) | |||
r1384 | emit this->setrange_sig(this->graphRange(), true, false); | |||
r1373 | } | |||
r1362 | ||||
r1373 | void VisualizationGraphWidget::move(double factor, Qt::Orientation orientation, bool forward) | |||
r1362 | { | |||
r1373 | impl->move(factor, orientation); | |||
r1420 | if (forward) | |||
r1384 | emit this->setrange_sig(this->graphRange(), true, false); | |||
r1362 | } | |||
r1373 | void VisualizationGraphWidget::move(double dx, double dy, bool forward) | |||
r1362 | { | |||
r1373 | impl->move(dx, dy); | |||
r1420 | if (forward) | |||
r1384 | emit this->setrange_sig(this->graphRange(), true, false); | |||
r1362 | } | |||
r1420 | void VisualizationGraphWidget::transform( | |||
const DateTimeRangeTransformation& tranformation, bool forward) | ||||
r1377 | { | |||
impl->transform(tranformation); | ||||
r1420 | if (forward) | |||
r1384 | emit this->setrange_sig(this->graphRange(), true, false); | |||
r1377 | } | |||
r1420 | void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor* visitor) | |||
r118 | { | |||
r1420 | if (visitor) | |||
{ | ||||
Alexandre Leroux
|
r208 | visitor->visit(this); | ||
} | ||||
r1420 | else | |||
{ | ||||
Alexandre Leroux
|
r219 | qCCritical(LOG_VisualizationGraphWidget()) | ||
<< tr("Can't visit widget : the visitor is null"); | ||||
} | ||||
r118 | } | |||
r1420 | bool VisualizationGraphWidget::canDrop(Variable2& variable) const | |||
Alexandre Leroux
|
r209 | { | ||
r1420 | auto isSpectrogram | |||
= [](auto& variable) { return variable.type() == DataSeriesType::SPECTROGRAM; }; | ||||
Alexandre Leroux
|
r1022 | |||
// - A spectrogram series can't be dropped on graph with existing plottables | ||||
// - No data series can be dropped on graph with existing spectrogram series | ||||
return isSpectrogram(variable) | ||||
r1420 | ? impl->m_VariableToPlotMultiMap.empty() | |||
: std::none_of(impl->m_VariableToPlotMultiMap.cbegin(), | ||||
impl->m_VariableToPlotMultiMap.cend(), | ||||
[isSpectrogram](const auto& entry) { return isSpectrogram(*entry.first); }); | ||||
Alexandre Leroux
|
r209 | } | ||
r1420 | bool VisualizationGraphWidget::contains(Variable2& variable) const | |||
Alexandre Leroux
|
r327 | { | ||
// Finds the variable among the keys of the map | ||||
auto variablePtr = &variable; | ||||
auto findVariable | ||||
r1420 | = [variablePtr](const auto& entry) { return variablePtr == entry.first.get(); }; | |||
Alexandre Leroux
|
r327 | |||
auto end = impl->m_VariableToPlotMultiMap.cend(); | ||||
auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable); | ||||
return it != end; | ||||
} | ||||
r119 | QString VisualizationGraphWidget::name() const | |||
r118 | { | |||
Alexandre Leroux
|
r724 | return impl->m_Name; | ||
r118 | } | |||
r1420 | QMimeData* VisualizationGraphWidget::mimeData(const QPoint& position) const | |||
r839 | { | |||
r846 | auto mimeData = new QMimeData; | |||
r839 | ||||
r1364 | auto selectionZoneItemUnderCursor = impl->selectionZoneAt(position); | |||
r1047 | if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones | |||
r1420 | && selectionZoneItemUnderCursor) | |||
{ | ||||
mimeData->setData(MIME_TYPE_TIME_RANGE, | ||||
TimeController::mimeDataForTimeRange(selectionZoneItemUnderCursor->range())); | ||||
mimeData->setData(MIME_TYPE_SELECTION_ZONE, | ||||
TimeController::mimeDataForTimeRange(selectionZoneItemUnderCursor->range())); | ||||
r1047 | } | |||
r1420 | else | |||
{ | ||||
mimeData->setData(MIME_TYPE_GRAPH, QByteArray {}); | ||||
r1047 | ||||
auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange()); | ||||
mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData); | ||||
} | ||||
r878 | ||||
r844 | return mimeData; | |||
r839 | } | |||
r1420 | QPixmap VisualizationGraphWidget::customDragPixmap(const QPoint& dragPosition) | |||
r1047 | { | |||
r1364 | auto selectionZoneItemUnderCursor = impl->selectionZoneAt(dragPosition); | |||
r1047 | if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones | |||
r1420 | && selectionZoneItemUnderCursor) | |||
{ | ||||
r1047 | ||||
auto zoneTopLeft = selectionZoneItemUnderCursor->topLeft->pixelPosition(); | ||||
auto zoneBottomRight = selectionZoneItemUnderCursor->bottomRight->pixelPosition(); | ||||
r1420 | auto zoneSize = QSizeF { qAbs(zoneBottomRight.x() - zoneTopLeft.x()), | |||
qAbs(zoneBottomRight.y() - zoneTopLeft.y()) } | ||||
r1047 | .toSize(); | |||
auto pixmap = QPixmap(zoneSize); | ||||
r1420 | render(&pixmap, QPoint(), QRegion { QRect { zoneTopLeft.toPoint(), zoneSize } }); | |||
r1047 | ||||
return pixmap; | ||||
} | ||||
return QPixmap(); | ||||
} | ||||
r839 | bool VisualizationGraphWidget::isDragAllowed() const | |||
{ | ||||
return true; | ||||
} | ||||
r873 | void VisualizationGraphWidget::highlightForMerge(bool highlighted) | |||
{ | ||||
r1420 | if (highlighted) | |||
{ | ||||
r873 | plot().setBackground(QBrush(QColor("#BBD5EE"))); | |||
} | ||||
r1420 | else | |||
{ | ||||
r873 | plot().setBackground(QBrush(Qt::white)); | |||
} | ||||
plot().update(); | ||||
} | ||||
r960 | void VisualizationGraphWidget::addVerticalCursor(double time) | |||
{ | ||||
impl->m_VerticalCursor->setPosition(time); | ||||
impl->m_VerticalCursor->setVisible(true); | ||||
auto text | ||||
= DateUtils::dateTime(time).toString(CURSOR_LABELS_DATETIME_FORMAT).replace(' ', '\n'); | ||||
impl->m_VerticalCursor->setLabelText(text); | ||||
} | ||||
void VisualizationGraphWidget::addVerticalCursorAtViewportPosition(double position) | ||||
{ | ||||
impl->m_VerticalCursor->setAbsolutePosition(position); | ||||
impl->m_VerticalCursor->setVisible(true); | ||||
auto axis = plot().axisRect()->axis(QCPAxis::atBottom); | ||||
auto text | ||||
= DateUtils::dateTime(axis->pixelToCoord(position)).toString(CURSOR_LABELS_DATETIME_FORMAT); | ||||
impl->m_VerticalCursor->setLabelText(text); | ||||
} | ||||
void VisualizationGraphWidget::removeVerticalCursor() | ||||
{ | ||||
impl->m_VerticalCursor->setVisible(false); | ||||
plot().replot(QCustomPlot::rpQueuedReplot); | ||||
} | ||||
void VisualizationGraphWidget::addHorizontalCursor(double value) | ||||
{ | ||||
impl->m_HorizontalCursor->setPosition(value); | ||||
impl->m_HorizontalCursor->setVisible(true); | ||||
impl->m_HorizontalCursor->setLabelText(QString::number(value)); | ||||
} | ||||
void VisualizationGraphWidget::addHorizontalCursorAtViewportPosition(double position) | ||||
{ | ||||
impl->m_HorizontalCursor->setAbsolutePosition(position); | ||||
impl->m_HorizontalCursor->setVisible(true); | ||||
auto axis = plot().axisRect()->axis(QCPAxis::atLeft); | ||||
impl->m_HorizontalCursor->setLabelText(QString::number(axis->pixelToCoord(position))); | ||||
} | ||||
void VisualizationGraphWidget::removeHorizontalCursor() | ||||
{ | ||||
impl->m_HorizontalCursor->setVisible(false); | ||||
plot().replot(QCustomPlot::rpQueuedReplot); | ||||
} | ||||
r1420 | void VisualizationGraphWidget::closeEvent(QCloseEvent* event) | |||
Alexandre Leroux
|
r738 | { | ||
Q_UNUSED(event); | ||||
r1420 | for (auto i : impl->m_SelectionZones) | |||
{ | ||||
r1324 | parentVisualizationWidget()->selectionZoneManager().setSelected(i, false); | |||
} | ||||
Alexandre Leroux
|
r738 | // Prevents that all variables will be removed from graph when it will be closed | ||
r1420 | for (auto& variableEntry : impl->m_VariableToPlotMultiMap) | |||
{ | ||||
Alexandre Leroux
|
r738 | emit variableAboutToBeRemoved(variableEntry.first); | ||
} | ||||
} | ||||
r1420 | void VisualizationGraphWidget::enterEvent(QEvent* event) | |||
Alexandre Leroux
|
r728 | { | ||
Q_UNUSED(event); | ||||
impl->m_RenderingDelegate->showGraphOverlay(true); | ||||
} | ||||
r1420 | void VisualizationGraphWidget::leaveEvent(QEvent* event) | |||
Alexandre Leroux
|
r728 | { | ||
Q_UNUSED(event); | ||||
impl->m_RenderingDelegate->showGraphOverlay(false); | ||||
r960 | ||||
r1420 | if (auto parentZone = parentZoneWidget()) | |||
{ | ||||
r960 | parentZone->notifyMouseLeaveGraph(this); | |||
} | ||||
r1420 | else | |||
{ | ||||
r960 | qCWarning(LOG_VisualizationGraphWidget()) << "leaveEvent: No parent zone widget"; | |||
} | ||||
r1044 | ||||
r1420 | if (impl->m_HoveredZone) | |||
{ | ||||
r1044 | impl->m_HoveredZone->setHovered(false); | |||
impl->m_HoveredZone = nullptr; | ||||
} | ||||
Alexandre Leroux
|
r728 | } | ||
r1420 | void VisualizationGraphWidget::wheelEvent(QWheelEvent* event) | |||
r1362 | { | |||
double factor; | ||||
double wheelSteps = event->delta() / 120.0; // a single step delta is +/-120 usually | ||||
r1420 | if (event->modifiers() == Qt::ControlModifier) | |||
{ | ||||
r1362 | if (event->orientation() == Qt::Vertical) // mRangeZoom.testFlag(Qt::Vertical)) | |||
{ | ||||
setCursor(Qt::SizeVerCursor); | ||||
r1364 | factor = pow(impl->m_plot->axisRect()->rangeZoomFactor(Qt::Vertical), wheelSteps); | |||
r1362 | zoom(factor, event->pos().y(), Qt::Vertical); | |||
} | ||||
} | ||||
r1420 | else if (event->modifiers() == Qt::ShiftModifier) | |||
{ | ||||
r1362 | if (event->orientation() == Qt::Vertical) // mRangeZoom.testFlag(Qt::Vertical)) | |||
{ | ||||
setCursor(Qt::SizeHorCursor); | ||||
r1364 | factor = pow(impl->m_plot->axisRect()->rangeZoomFactor(Qt::Horizontal), wheelSteps); | |||
r1362 | zoom(factor, event->pos().x(), Qt::Horizontal); | |||
} | ||||
} | ||||
r1420 | else | |||
{ | ||||
r1362 | move(wheelSteps, Qt::Horizontal); | |||
} | ||||
r1374 | event->accept(); | |||
r1362 | } | |||
r1363 | ||||
r1420 | void VisualizationGraphWidget::mouseMoveEvent(QMouseEvent* event) | |||
r1362 | { | |||
r1420 | if (impl->isDrawingZoomRect()) | |||
r1362 | { | |||
r1364 | impl->updateZoomRect(event->pos()); | |||
r1362 | } | |||
r1364 | else if (impl->isDrawingZoneRect()) | |||
r1362 | { | |||
r1364 | impl->updateZoneRect(event->pos()); | |||
r1362 | } | |||
r1364 | else if (event->buttons() == Qt::LeftButton) | |||
r1363 | { | |||
r1420 | if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::None) | |||
r1365 | { | |||
r1420 | auto [dx, dy] = impl->moveGraph(event->pos()); | |||
r1384 | emit this->setrange_sig(this->graphRange(), true, false); | |||
r1373 | } | |||
r1420 | else if (sqpApp->plotsInteractionMode() | |||
== SqpApplication::PlotsInteractionMode::SelectionZones) | ||||
r1373 | { | |||
r1388 | auto posInPlot = this->impl->m_plot->mapFromParent(event->pos()); | |||
r1420 | if (auto item = impl->m_plot->itemAt(posInPlot)) | |||
r1387 | { | |||
r1420 | if (qobject_cast<VisualizationSelectionZoneItem*>(item)) | |||
r1387 | { | |||
r1420 | QMouseEvent e { QEvent::MouseMove, posInPlot, event->button(), event->buttons(), | |||
event->modifiers() }; | ||||
r1387 | sqpApp->sendEvent(this->impl->m_plot, &e); | |||
r1388 | this->impl->m_plot->replot(QCustomPlot::rpImmediateRefresh); | |||
r1387 | } | |||
} | ||||
r1365 | } | |||
r1362 | } | |||
r1363 | else | |||
{ | ||||
impl->m_RenderingDelegate->updateTooltip(event); | ||||
} | ||||
r1420 | // event->accept(); | |||
r1376 | QWidget::mouseMoveEvent(event); | |||
r1362 | } | |||
r1420 | void VisualizationGraphWidget::mouseReleaseEvent(QMouseEvent* event) | |||
r1362 | { | |||
r1420 | if (impl->isDrawingZoomRect()) | |||
r1364 | { | |||
r1377 | auto oldRange = this->graphRange(); | |||
r1364 | impl->applyZoomRect(); | |||
r1377 | auto newRange = this->graphRange(); | |||
r1420 | if (auto tf = DateTimeRangeHelper::computeTransformation(oldRange, newRange)) | |||
r1377 | emit this->transform_sig(tf.value(), false); | |||
r1364 | } | |||
r1420 | else if (impl->isDrawingZoneRect()) | |||
r1364 | { | |||
r1365 | impl->endDrawingZone(); | |||
r1364 | } | |||
else | ||||
{ | ||||
setCursor(Qt::ArrowCursor); | ||||
} | ||||
r1387 | auto posInPlot = this->impl->m_plot->mapFromParent(event->pos()); | |||
r1420 | if (auto item = impl->m_plot->itemAt(posInPlot)) | |||
r1387 | { | |||
r1420 | if (qobject_cast<VisualizationSelectionZoneItem*>(item)) | |||
r1387 | { | |||
r1420 | QMouseEvent e { QEvent::MouseButtonRelease, posInPlot, event->button(), | |||
event->buttons(), event->modifiers() }; | ||||
r1387 | sqpApp->sendEvent(this->impl->m_plot, &e); | |||
} | ||||
} | ||||
r1374 | event->accept(); | |||
r1362 | } | |||
r1420 | void VisualizationGraphWidget::mousePressEvent(QMouseEvent* event) | |||
r1362 | { | |||
r1420 | if (event->button() == Qt::RightButton) | |||
r1365 | { | |||
onGraphMenuRequested(event->pos()); | ||||
} | ||||
else | ||||
{ | ||||
auto selectedZone = impl->selectionZoneAt(event->pos()); | ||||
switch (sqpApp->plotsInteractionMode()) | ||||
r1364 | { | |||
r1420 | case SqpApplication::PlotsInteractionMode::DragAndDrop: | |||
break; | ||||
case SqpApplication::PlotsInteractionMode::SelectionZones: | ||||
impl->setSelectionZonesEditionEnabled(true); | ||||
if ((event->modifiers() == Qt::ControlModifier) && (selectedZone != nullptr)) | ||||
{ | ||||
auto alreadySelectedZones | ||||
= parentVisualizationWidget()->selectionZoneManager().selectedItems(); | ||||
r1386 | selectedZone->setAssociatedEditedZones(alreadySelectedZones); | |||
r1420 | if (SciQLop::containers::contains(alreadySelectedZones, selectedZone)) | |||
r1386 | { | |||
alreadySelectedZones.removeOne(selectedZone); | ||||
} | ||||
else | ||||
{ | ||||
alreadySelectedZones.append(selectedZone); | ||||
} | ||||
r1420 | parentVisualizationWidget()->selectionZoneManager().select( | |||
alreadySelectedZones); | ||||
r1365 | } | |||
else | ||||
{ | ||||
r1420 | if (!selectedZone) | |||
{ | ||||
parentVisualizationWidget()->selectionZoneManager().clearSelection(); | ||||
impl->startDrawingZone(event->pos()); | ||||
} | ||||
else | ||||
{ | ||||
parentVisualizationWidget()->selectionZoneManager().select( | ||||
{ selectedZone }); | ||||
} | ||||
r1364 | } | |||
r1387 | { | |||
r1420 | auto posInPlot = this->impl->m_plot->mapFromParent(event->pos()); | |||
if (auto item = impl->m_plot->itemAt(posInPlot)) | ||||
r1387 | { | |||
r1420 | if (qobject_cast<VisualizationSelectionZoneItem*>(item)) | |||
{ | ||||
QMouseEvent e { QEvent::MouseButtonPress, posInPlot, event->button(), | ||||
event->buttons(), event->modifiers() }; | ||||
sqpApp->sendEvent(this->impl->m_plot, &e); | ||||
} | ||||
r1387 | } | |||
} | ||||
r1420 | break; | |||
case SqpApplication::PlotsInteractionMode::ZoomBox: | ||||
impl->startDrawingRect(event->pos()); | ||||
break; | ||||
default: | ||||
if (auto item = impl->m_plot->itemAt(event->pos())) | ||||
{ | ||||
emit impl->m_plot->itemClick(item, event); | ||||
if (qobject_cast<VisualizationSelectionZoneItem*>(item)) | ||||
{ | ||||
setCursor(Qt::ClosedHandCursor); | ||||
impl->enterPlotDrag(event->pos()); | ||||
} | ||||
} | ||||
else | ||||
r1383 | { | |||
setCursor(Qt::ClosedHandCursor); | ||||
impl->enterPlotDrag(event->pos()); | ||||
} | ||||
r1362 | } | |||
} | ||||
r1420 | // event->accept(); | |||
r1376 | QWidget::mousePressEvent(event); | |||
r1362 | } | |||
r1420 | void VisualizationGraphWidget::mouseDoubleClickEvent(QMouseEvent* event) | |||
r1363 | { | |||
impl->m_RenderingDelegate->onMouseDoubleClick(event); | ||||
} | ||||
r1420 | void VisualizationGraphWidget::keyReleaseEvent(QKeyEvent* event) | |||
r1362 | { | |||
r1420 | switch (event->key()) | |||
{ | ||||
r1362 | case Qt::Key_Control: | |||
event->accept(); | ||||
break; | ||||
case Qt::Key_Shift: | ||||
event->accept(); | ||||
break; | ||||
default: | ||||
QWidget::keyReleaseEvent(event); | ||||
break; | ||||
} | ||||
setCursor(Qt::ArrowCursor); | ||||
r1420 | // event->accept(); | |||
r1362 | } | |||
r1420 | void VisualizationGraphWidget::keyPressEvent(QKeyEvent* event) | |||
r1362 | { | |||
r1420 | switch (event->key()) | |||
{ | ||||
r1362 | case Qt::Key_Control: | |||
setCursor(Qt::CrossCursor); | ||||
break; | ||||
case Qt::Key_Shift: | ||||
break; | ||||
case Qt::Key_M: | ||||
r1377 | impl->rescaleY(); | |||
r1364 | impl->m_plot->replot(QCustomPlot::rpQueuedReplot); | |||
r1362 | break; | |||
case Qt::Key_Left: | ||||
r1420 | if (event->modifiers() != Qt::ControlModifier) | |||
{ | ||||
r1362 | move(-0.1, Qt::Horizontal); | |||
} | ||||
r1420 | else | |||
{ | ||||
r1362 | zoom(2, this->width() / 2, Qt::Horizontal); | |||
} | ||||
break; | ||||
case Qt::Key_Right: | ||||
r1420 | if (event->modifiers() != Qt::ControlModifier) | |||
{ | ||||
r1362 | move(0.1, Qt::Horizontal); | |||
} | ||||
r1420 | else | |||
{ | ||||
r1362 | zoom(0.5, this->width() / 2, Qt::Horizontal); | |||
} | ||||
break; | ||||
case Qt::Key_Up: | ||||
r1420 | if (event->modifiers() != Qt::ControlModifier) | |||
{ | ||||
r1362 | move(0.1, Qt::Vertical); | |||
} | ||||
r1420 | else | |||
{ | ||||
r1362 | zoom(0.5, this->height() / 2, Qt::Vertical); | |||
} | ||||
break; | ||||
case Qt::Key_Down: | ||||
r1420 | if (event->modifiers() != Qt::ControlModifier) | |||
{ | ||||
r1362 | move(-0.1, Qt::Vertical); | |||
} | ||||
r1420 | else | |||
{ | ||||
r1362 | zoom(2, this->height() / 2, Qt::Vertical); | |||
} | ||||
break; | ||||
default: | ||||
QWidget::keyPressEvent(event); | ||||
break; | ||||
} | ||||
} | ||||
r1420 | QCustomPlot& VisualizationGraphWidget::plot() const noexcept | |||
Alexandre Leroux
|
r725 | { | ||
r1364 | return *impl->m_plot; | |||
Alexandre Leroux
|
r725 | } | ||
r1420 | void VisualizationGraphWidget::onGraphMenuRequested(const QPoint& pos) noexcept | |||
r118 | { | |||
r1420 | QMenu graphMenu {}; | |||
Alexandre Leroux
|
r269 | |||
Alexandre Leroux
|
r270 | // Iterates on variables (unique keys) | ||
for (auto it = impl->m_VariableToPlotMultiMap.cbegin(), | ||||
r1420 | end = impl->m_VariableToPlotMultiMap.cend(); | |||
it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) | ||||
{ | ||||
Alexandre Leroux
|
r270 | // 'Remove variable' action | ||
graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()), | ||||
r1420 | [this, var = it->first]() { removeVariable(var); }); | |||
Alexandre Leroux
|
r196 | } | ||
Alexandre Leroux
|
r269 | |||
r1420 | if (!impl->m_ZoomStack.isEmpty()) | |||
{ | ||||
if (!graphMenu.isEmpty()) | ||||
{ | ||||
r1046 | graphMenu.addSeparator(); | |||
} | ||||
graphMenu.addAction(tr("Undo Zoom"), [this]() { undoZoom(); }); | ||||
} | ||||
r1083 | // Selection Zone Actions | |||
r1364 | auto selectionZoneItem = impl->selectionZoneAt(pos); | |||
r1420 | if (selectionZoneItem) | |||
{ | ||||
r1077 | auto selectedItems = parentVisualizationWidget()->selectionZoneManager().selectedItems(); | |||
r1081 | selectedItems.removeAll(selectionZoneItem); | |||
selectedItems.prepend(selectionZoneItem); // Put the current selection zone first | ||||
r1077 | auto zoneActions = sqpApp->actionsGuiController().selectionZoneActions(); | |||
r1420 | if (!zoneActions.isEmpty() && !graphMenu.isEmpty()) | |||
{ | ||||
r1077 | graphMenu.addSeparator(); | |||
} | ||||
r1420 | QHash<QString, QMenu*> subMenus; | |||
r1083 | QHash<QString, bool> subMenusEnabled; | |||
r1420 | QHash<QString, FilteringAction*> filteredMenu; | |||
r1083 | ||||
r1420 | for (auto zoneAction : zoneActions) | |||
{ | ||||
r1083 | ||||
auto isEnabled = zoneAction->isEnabled(selectedItems); | ||||
auto menu = &graphMenu; | ||||
r1328 | QString menuPath; | |||
r1420 | for (auto subMenuName : zoneAction->subMenuList()) | |||
{ | ||||
r1328 | menuPath += '/'; | |||
menuPath += subMenuName; | ||||
r1420 | if (!subMenus.contains(menuPath)) | |||
{ | ||||
r1083 | menu = menu->addMenu(subMenuName); | |||
r1328 | subMenus[menuPath] = menu; | |||
subMenusEnabled[menuPath] = isEnabled; | ||||
r1083 | } | |||
r1420 | else | |||
{ | ||||
r1328 | menu = subMenus.value(menuPath); | |||
r1420 | if (isEnabled) | |||
{ | ||||
r1083 | // The sub menu is enabled if at least one of its actions is enabled | |||
r1328 | subMenusEnabled[menuPath] = true; | |||
r1083 | } | |||
} | ||||
} | ||||
r1420 | FilteringAction* filterAction = nullptr; | |||
if (sqpApp->actionsGuiController().isMenuFiltered(zoneAction->subMenuList())) | ||||
{ | ||||
r1328 | filterAction = filteredMenu.value(menuPath); | |||
r1420 | if (!filterAction) | |||
{ | ||||
filterAction = new FilteringAction { this }; | ||||
r1328 | filteredMenu[menuPath] = filterAction; | |||
menu->addAction(filterAction); | ||||
} | ||||
} | ||||
r1083 | auto action = menu->addAction(zoneAction->name()); | |||
action->setEnabled(isEnabled); | ||||
r1082 | action->setShortcut(zoneAction->displayedShortcut()); | |||
r1081 | QObject::connect(action, &QAction::triggered, | |||
r1420 | [zoneAction, selectedItems]() { zoneAction->execute(selectedItems); }); | |||
r1328 | ||||
r1420 | if (filterAction && zoneAction->isFilteringAllowed()) | |||
{ | ||||
r1328 | filterAction->addActionToFilter(action); | |||
} | ||||
r1077 | } | |||
r1083 | ||||
r1420 | for (auto it = subMenus.cbegin(); it != subMenus.cend(); ++it) | |||
{ | ||||
r1083 | it.value()->setEnabled(subMenusEnabled[it.key()]); | |||
} | ||||
r1077 | } | |||
r1420 | if (!graphMenu.isEmpty()) | |||
{ | ||||
Alexandre Leroux
|
r655 | graphMenu.exec(QCursor::pos()); | ||
Alexandre Leroux
|
r196 | } | ||
r118 | } | |||
Alexandre Leroux
|
r179 | |||
r1420 | void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent* event) noexcept | |||
Alexandre Leroux
|
r1002 | { | ||
impl->m_RenderingDelegate->onMouseDoubleClick(event); | ||||
} | ||||
r1420 | void VisualizationGraphWidget::onMouseMove(QMouseEvent* event) noexcept | |||
Alexandre Leroux
|
r481 | { | ||
// Handles plot rendering when mouse is moving | ||||
r1363 | impl->m_RenderingDelegate->updateTooltip(event); | |||
r839 | ||||
r1364 | auto axisPos = impl->posToAxisPos(event->pos()); | |||
r960 | ||||
r1044 | // Zoom box and zone drawing | |||
r1420 | if (impl->m_DrawingZoomRect) | |||
{ | ||||
r1044 | impl->m_DrawingZoomRect->bottomRight->setCoords(axisPos); | |||
} | ||||
r1420 | else if (impl->m_DrawingZone) | |||
{ | ||||
r1044 | impl->m_DrawingZone->setEnd(axisPos.x()); | |||
r959 | } | |||
r1044 | // Cursor | |||
r1420 | if (auto parentZone = parentZoneWidget()) | |||
{ | ||||
if (impl->pointIsInAxisRect(axisPos, plot())) | ||||
{ | ||||
r960 | parentZone->notifyMouseMoveInGraph(event->pos(), axisPos, this); | |||
} | ||||
r1420 | else | |||
{ | ||||
r960 | parentZone->notifyMouseLeaveGraph(this); | |||
} | ||||
} | ||||
r1044 | // Search for the selection zone under the mouse | |||
r1364 | auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos()); | |||
r1044 | if (selectionZoneItemUnderCursor && !impl->m_DrawingZone | |||
r1420 | && sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones) | |||
{ | ||||
r1044 | ||||
// Sets the appropriate cursor shape | ||||
auto cursorShape = selectionZoneItemUnderCursor->curshorShapeForPosition(event->pos()); | ||||
setCursor(cursorShape); | ||||
// Manages the hovered zone | ||||
r1420 | if (selectionZoneItemUnderCursor != impl->m_HoveredZone) | |||
{ | ||||
if (impl->m_HoveredZone) | ||||
{ | ||||
r1044 | impl->m_HoveredZone->setHovered(false); | |||
} | ||||
selectionZoneItemUnderCursor->setHovered(true); | ||||
impl->m_HoveredZone = selectionZoneItemUnderCursor; | ||||
plot().replot(QCustomPlot::rpQueuedReplot); | ||||
} | ||||
} | ||||
r1420 | else | |||
{ | ||||
r1044 | // There is no zone under the mouse or the interaction mode is not "selection zones" | |||
r1420 | if (impl->m_HoveredZone) | |||
{ | ||||
r1044 | impl->m_HoveredZone->setHovered(false); | |||
impl->m_HoveredZone = nullptr; | ||||
} | ||||
setCursor(Qt::ArrowCursor); | ||||
} | ||||
r1050 | impl->m_HasMovedMouse = true; | |||
r839 | VisualizationDragWidget::mouseMoveEvent(event); | |||
Alexandre Leroux
|
r481 | } | ||
r1420 | void VisualizationGraphWidget::onMouseWheel(QWheelEvent* event) noexcept | |||
Alexandre Leroux
|
r179 | { | ||
Alexandre Leroux
|
r1330 | // Processes event only if the wheel occurs on axis rect | ||
r1420 | if (!dynamic_cast<QCPAxisRect*>(impl->m_plot->layoutElementAt(event->posF()))) | |||
{ | ||||
Alexandre Leroux
|
r1330 | return; | ||
} | ||||
r958 | auto value = event->angleDelta().x() + event->angleDelta().y(); | |||
r1420 | if (value != 0) | |||
{ | ||||
r958 | ||||
auto direction = value > 0 ? 1.0 : -1.0; | ||||
auto isZoomX = event->modifiers().testFlag(HORIZONTAL_ZOOM_MODIFIER); | ||||
auto isZoomY = event->modifiers().testFlag(VERTICAL_ZOOM_MODIFIER); | ||||
impl->m_IsCalibration = event->modifiers().testFlag(VERTICAL_PAN_MODIFIER); | ||||
r1420 | auto zoomOrientations = QFlags<Qt::Orientation> {}; | |||
r958 | zoomOrientations.setFlag(Qt::Horizontal, isZoomX); | |||
zoomOrientations.setFlag(Qt::Vertical, isZoomY); | ||||
r1364 | impl->m_plot->axisRect()->setRangeZoom(zoomOrientations); | |||
Alexandre Leroux
|
r179 | |||
r1420 | if (!isZoomX && !isZoomY) | |||
{ | ||||
r958 | auto axis = plot().axisRect()->axis(QCPAxis::atBottom); | |||
auto diff = direction * (axis->range().size() * (PAN_SPEED / 100.0)); | ||||
Alexandre Leroux
|
r179 | |||
r958 | axis->setRange(axis->range() + diff); | |||
r1420 | if (plot().noAntialiasingOnDrag()) | |||
{ | ||||
r958 | plot().setNotAntialiasedElements(QCP::aeAll); | |||
} | ||||
r1362 | // plot().replot(QCustomPlot::rpQueuedReplot); | |||
r958 | } | |||
} | ||||
Alexandre Leroux
|
r179 | } | ||
r235 | ||||
r1420 | void VisualizationGraphWidget::onMousePress(QMouseEvent* event) noexcept | |||
r445 | { | |||
Thibaud Rabillard
|
r1052 | auto isDragDropClick = event->modifiers().testFlag(DRAG_DROP_MODIFIER); | ||
r1049 | auto isSelectionZoneMode | |||
= sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones; | ||||
Thibaud Rabillard
|
r1052 | auto isLeftClick = event->buttons().testFlag(Qt::LeftButton); | ||
r1047 | ||||
r1420 | if (!isDragDropClick && isLeftClick) | |||
{ | ||||
if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox) | ||||
{ | ||||
r1047 | // Starts a zoom box | |||
r1364 | impl->startDrawingRect(event->pos()); | |||
r1047 | } | |||
r1420 | else if (isSelectionZoneMode && impl->m_DrawingZone == nullptr) | |||
{ | ||||
r1047 | // Starts a new selection zone | |||
r1364 | auto zoneAtPos = impl->selectionZoneAt(event->pos()); | |||
r1420 | if (!zoneAtPos) | |||
{ | ||||
r1365 | impl->startDrawingZone(event->pos()); | |||
r1047 | } | |||
r1044 | } | |||
} | ||||
r1049 | // Allows zone edition only in selection zone mode without drag&drop | |||
impl->setSelectionZonesEditionEnabled(isSelectionZoneMode && !isDragDropClick); | ||||
r1051 | // Selection / Deselection | |||
r1420 | if (isSelectionZoneMode) | |||
{ | ||||
r1049 | auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER); | |||
r1364 | auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos()); | |||
r1084 | ||||
if (selectionZoneItemUnderCursor && !selectionZoneItemUnderCursor->selected() | ||||
r1420 | && !isMultiSelectionClick) | |||
{ | ||||
r1084 | parentVisualizationWidget()->selectionZoneManager().select( | |||
r1420 | { selectionZoneItemUnderCursor }); | |||
r1049 | } | |||
r1420 | else if (!selectionZoneItemUnderCursor && !isMultiSelectionClick && isLeftClick) | |||
{ | ||||
r1049 | parentVisualizationWidget()->selectionZoneManager().clearSelection(); | |||
} | ||||
r1420 | else | |||
{ | ||||
r1049 | // No selection change | |||
} | ||||
r1084 | ||||
r1420 | if (selectionZoneItemUnderCursor && isLeftClick) | |||
{ | ||||
r1084 | selectionZoneItemUnderCursor->setAssociatedEditedZones( | |||
parentVisualizationWidget()->selectionZoneManager().selectedItems()); | ||||
} | ||||
r1049 | } | |||
r839 | ||||
r1050 | ||||
impl->m_HasMovedMouse = false; | ||||
r839 | VisualizationDragWidget::mousePressEvent(event); | |||
r445 | } | |||
r1420 | void VisualizationGraphWidget::onMouseRelease(QMouseEvent* event) noexcept | |||
r445 | { | |||
r1420 | if (impl->m_DrawingZoomRect) | |||
{ | ||||
r959 | ||||
auto axisX = plot().axisRect()->axis(QCPAxis::atBottom); | ||||
auto axisY = plot().axisRect()->axis(QCPAxis::atLeft); | ||||
r1420 | auto newAxisXRange = QCPRange { impl->m_DrawingZoomRect->topLeft->coords().x(), | |||
impl->m_DrawingZoomRect->bottomRight->coords().x() }; | ||||
r959 | ||||
r1420 | auto newAxisYRange = QCPRange { impl->m_DrawingZoomRect->topLeft->coords().y(), | |||
impl->m_DrawingZoomRect->bottomRight->coords().y() }; | ||||
r959 | ||||
r1364 | impl->removeDrawingRect(); | |||
r959 | ||||
if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0) | ||||
r1420 | && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) | |||
{ | ||||
r1046 | impl->m_ZoomStack.push(qMakePair(axisX->range(), axisY->range())); | |||
r959 | axisX->setRange(newAxisXRange); | |||
axisY->setRange(newAxisYRange); | ||||
plot().replot(QCustomPlot::rpQueuedReplot); | ||||
} | ||||
} | ||||
r1365 | impl->endDrawingZone(); | |||
r1044 | ||||
r1050 | // Selection / Deselection | |||
auto isSelectionZoneMode | ||||
= sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones; | ||||
r1420 | if (isSelectionZoneMode) | |||
{ | ||||
r1050 | auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER); | |||
r1364 | auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos()); | |||
r1085 | if (selectionZoneItemUnderCursor && event->button() == Qt::LeftButton | |||
r1420 | && !impl->m_HasMovedMouse) | |||
{ | ||||
r1085 | ||||
auto zonesUnderCursor = impl->selectionZonesAt(event->pos(), plot()); | ||||
r1420 | if (zonesUnderCursor.count() > 1) | |||
{ | ||||
r1085 | // There are multiple zones under the mouse. | |||
// Performs the selection with a selection dialog. | ||||
r1420 | VisualizationMultiZoneSelectionDialog dialog { this }; | |||
r1085 | dialog.setZones(zonesUnderCursor); | |||
dialog.move(mapToGlobal(event->pos() - QPoint(dialog.width() / 2, 20))); | ||||
dialog.activateWindow(); | ||||
dialog.raise(); | ||||
r1420 | if (dialog.exec() == QDialog::Accepted) | |||
{ | ||||
r1085 | auto selection = dialog.selectedZones(); | |||
r1420 | if (!isMultiSelectionClick) | |||
{ | ||||
r1085 | parentVisualizationWidget()->selectionZoneManager().clearSelection(); | |||
} | ||||
r1420 | for (auto it = selection.cbegin(); it != selection.cend(); ++it) | |||
{ | ||||
r1085 | auto zone = it.key(); | |||
auto isSelected = it.value(); | ||||
r1420 | parentVisualizationWidget()->selectionZoneManager().setSelected( | |||
zone, isSelected); | ||||
r1085 | ||||
r1420 | if (isSelected) | |||
{ | ||||
r1085 | // Puts the zone on top of the stack so it can be moved or resized | |||
impl->moveSelectionZoneOnTop(zone, plot()); | ||||
} | ||||
} | ||||
} | ||||
r1050 | } | |||
r1420 | else | |||
{ | ||||
if (!isMultiSelectionClick) | ||||
{ | ||||
r1085 | parentVisualizationWidget()->selectionZoneManager().select( | |||
r1420 | { selectionZoneItemUnderCursor }); | |||
r1085 | impl->moveSelectionZoneOnTop(selectionZoneItemUnderCursor, plot()); | |||
} | ||||
r1420 | else | |||
{ | ||||
r1085 | parentVisualizationWidget()->selectionZoneManager().setSelected( | |||
r1420 | selectionZoneItemUnderCursor, | |||
!selectionZoneItemUnderCursor->selected() | ||||
|| event->button() == Qt::RightButton); | ||||
r1085 | } | |||
r1050 | } | |||
} | ||||
r1420 | else | |||
{ | ||||
r1050 | // No selection change | |||
} | ||||
} | ||||
r445 | } | |||
r235 | void VisualizationGraphWidget::onDataCacheVariableUpdated() | |||
{ | ||||
r1364 | auto graphRange = impl->m_plot->xAxis->range(); | |||
r1420 | auto dateTime = DateTimeRange { graphRange.lower, graphRange.upper }; | |||
r433 | ||||
r1420 | for (auto& variableEntry : impl->m_VariableToPlotMultiMap) | |||
{ | ||||
Alexandre Leroux
|
r582 | auto variable = variableEntry.first; | ||
r441 | qCDebug(LOG_VisualizationGraphWidget()) | |||
r539 | << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range(); | |||
r441 | qCDebug(LOG_VisualizationGraphWidget()) | |||
r433 | << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime; | |||
r1420 | if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) | |||
{ | ||||
Alexandre Leroux
|
r1280 | impl->updateData(variableEntry.second, variable, variable->range()); | ||
r433 | } | |||
r235 | } | |||
} | ||||
r571 | ||||
r1420 | void VisualizationGraphWidget::onUpdateVarDisplaying( | |||
std::shared_ptr<Variable2> variable, const DateTimeRange& range) | ||||
r571 | { | |||
Alexandre Leroux
|
r582 | auto it = impl->m_VariableToPlotMultiMap.find(variable); | ||
r1420 | if (it != impl->m_VariableToPlotMultiMap.end()) | |||
{ | ||||
Alexandre Leroux
|
r1280 | impl->updateData(it->second, variable, range); | ||
r571 | } | |||
} | ||||
r1352 | ||||
void VisualizationGraphWidget::variableUpdated(QUuid id) | ||||
{ | ||||
r1420 | for (auto& [var, plotables] : impl->m_VariableToPlotMultiMap) | |||
{ | ||||
if (var->ID() == id) | ||||
{ | ||||
r1362 | impl->updateData(plotables, var, this->graphRange()); | |||
r1352 | } | |||
} | ||||
} | ||||
r1381 | ||||
r1420 | void VisualizationGraphWidget::variableDeleted(const std::shared_ptr<Variable2>& variable) | |||
r1381 | { | |||
this->removeVariable(variable); | ||||
} | ||||