##// END OF EJS Templates
Corrects the problem of refreshing synchronized graphs from TimeWidget (1)...
Corrects the problem of refreshing synchronized graphs from TimeWidget (1) Introduces graph flags to set options for the widget

File last commit:

r1094:f391b1d9fb19
r1325:87a145505c37
Show More
VisualizationGraphRenderingDelegate.cpp
332 lines | 12.0 KiB | text/x-c | CppLexer
/ gui / src / Visualization / VisualizationGraphRenderingDelegate.cpp
Alexandre Leroux
Creates a delegate offering methods for rendering a graph
r480 #include "Visualization/VisualizationGraphRenderingDelegate.h"
Alexandre Leroux
Refactoring handling of axes properties (2)...
r917 #include "Visualization/AxisRenderingUtils.h"
Alexandre Leroux
Handles double click on color scale...
r1043 #include "Visualization/ColorScaleEditor.h"
Alexandre Leroux
Handles rendering of plottables (3)...
r920 #include "Visualization/PlottablesRenderingUtils.h"
Alexandre Leroux
Uses SciQlop color scale in graphs
r1050 #include "Visualization/SqpColorScale.h"
Alexandre Leroux
Passes directly GraphWidget in delegate...
r725 #include "Visualization/VisualizationGraphWidget.h"
Alexandre Leroux
Creates a delegate offering methods for rendering a graph
r480 #include "Visualization/qcustomplot.h"
Alexandre Leroux
Uses the same time spec (UTC) as axis for tooltips on graph
r490 #include <Common/DateUtils.h>
Alexandre Leroux
Adds button on plot overlay to show/hide x-axis properties
r729 #include <Data/IDataSeries.h>
Alexandre Leroux
Adds title and close items in plot overlay
r726 #include <SqpApplication.h>
Alexandre Leroux
Inits tracers and timer...
r482 namespace {
Alexandre Leroux
Adds button on plot overlay to show/hide x-axis properties
r729 /// Name of the axes layer in QCustomPlot
const auto AXES_LAYER = QStringLiteral("axes");
/// Icon used to show x-axis properties
const auto HIDE_AXIS_ICON_PATH = QStringLiteral(":/icones/down.png");
Alexandre Leroux
Adds title and close items in plot overlay
r726 /// Name of the overlay layer in QCustomPlot
const auto OVERLAY_LAYER = QStringLiteral("overlay");
Alexandre Leroux
Adds button on plot overlay to show/hide x-axis properties
r729 /// Pixmap used to show x-axis properties
const auto SHOW_AXIS_ICON_PATH = QStringLiteral(":/icones/up.png");
Alexandre Leroux
Tooltip for spectrograms (1)...
r1065 /// Tooltip format for graphs
const auto GRAPH_TOOLTIP_FORMAT = QStringLiteral("key: %1\nvalue: %2");
Alexandre Leroux
Tooltip for spectrograms (4)...
r1068 /// Tooltip format for colormaps
const auto COLORMAP_TOOLTIP_FORMAT = QStringLiteral("x: %1\ny: %2\nvalue: %3");
Alexandre Leroux
Show tracers method (1)...
r483
Alexandre Leroux
Shifts tooltip to mouse
r578 /// Offset used to shift the tooltip of the mouse
const auto TOOLTIP_OFFSET = QPoint{20, 20};
Alexandre Leroux
Uses Qt tooltips instead of QCPTextItem...
r576 /// Tooltip display rectangle (the tooltip is hidden when the mouse leaves this rectangle)
const auto TOOLTIP_RECT = QRect{10, 10, 10, 10};
/// Timeout after which the tooltip is displayed
const auto TOOLTIP_TIMEOUT = 500;
Alexandre Leroux
Inits tracers and timer...
r482
Alexandre Leroux
Sets tracers' styles
r485 void initPointTracerStyle(QCPItemTracer &tracer) noexcept
{
tracer.setInterpolating(false);
Alexandre Leroux
Replaces "Plus" cursor by "Circle" cursor
r577 tracer.setStyle(QCPItemTracer::tsCircle);
tracer.setSize(3);
Alexandre Leroux
Sets tracers' styles
r485 tracer.setPen(QPen(Qt::black));
tracer.setBrush(Qt::black);
Display of selection zones on a graph
r1084 tracer.setSelectable(false);
Alexandre Leroux
Sets tracers' styles
r485 }
Alexandre Leroux
Adds button on plot overlay to show/hide x-axis properties
r729 QPixmap pixmap(const QString &iconPath) noexcept
{
return QIcon{iconPath}.pixmap(QSize{16, 16});
}
Alexandre Leroux
Adds title and close items in plot overlay
r726 void initClosePixmapStyle(QCPItemPixmap &pixmap) noexcept
{
// Icon
pixmap.setPixmap(
sqpApp->style()->standardIcon(QStyle::SP_TitleBarCloseButton).pixmap(QSize{16, 16}));
// Position
pixmap.topLeft->setType(QCPItemPosition::ptAxisRectRatio);
pixmap.topLeft->setCoords(1, 0);
pixmap.setClipToAxisRect(false);
// Can be selected
pixmap.setSelectable(true);
}
Alexandre Leroux
Adds button on plot overlay to show/hide x-axis properties
r729 void initXAxisPixmapStyle(QCPItemPixmap &itemPixmap) noexcept
{
// Icon
itemPixmap.setPixmap(pixmap(HIDE_AXIS_ICON_PATH));
// Position
itemPixmap.topLeft->setType(QCPItemPosition::ptAxisRectRatio);
itemPixmap.topLeft->setCoords(0, 1);
itemPixmap.setClipToAxisRect(false);
// Can be selected
itemPixmap.setSelectable(true);
}
Alexandre Leroux
Adds title and close items in plot overlay
r726 void initTitleTextStyle(QCPItemText &text) noexcept
{
// Font and background styles
text.setColor(Qt::gray);
text.setBrush(Qt::white);
// Position
text.setPositionAlignment(Qt::AlignTop | Qt::AlignLeft);
text.position->setType(QCPItemPosition::ptAxisRectRatio);
text.position->setCoords(0.5, 0);
Display of selection zones on a graph
r1084 text.setSelectable(false);
Alexandre Leroux
Adds title and close items in plot overlay
r726 }
Alexandre Leroux
Tooltip for spectrograms (3)...
r1067 /**
* Returns the cell index (x or y) of a colormap according to the coordinate passed in parameter.
* This method handles the fact that a colormap axis can be logarithmic or linear.
* @param colormap the colormap for which to calculate the index
* @param coord the coord to convert to cell index
* @param xCoord calculates the x index if true, calculates y index if false
* @return the cell index
*/
int colorMapCellIndex(const QCPColorMap &colormap, double coord, bool xCoord)
{
// Determines the axis of the colormap according to xCoord, and whether it is logarithmic or not
auto isLogarithmic = (xCoord ? colormap.keyAxis() : colormap.valueAxis())->scaleType()
== QCPAxis::stLogarithmic;
if (isLogarithmic) {
// For a logarithmic axis we can't use the conversion method of colormap, so we calculate
// the index manually based on the position of the coordinate on the axis
// Gets the axis range and the number of values between range bounds to calculate the step
// between each value of the range
auto range = xCoord ? colormap.data()->keyRange() : colormap.data()->valueRange();
auto nbValues = (xCoord ? colormap.data()->keySize() : colormap.data()->valueSize()) - 1;
auto valueStep
= (std::log10(range.upper) - std::log10(range.lower)) / static_cast<double>(nbValues);
// According to the coord position, calculates the closest index in the range
return std::round((std::log10(coord) - std::log10(range.lower)) / valueStep);
}
else {
// For a linear axis, we use the conversion method of colormap
int index;
if (xCoord) {
colormap.data()->coordToCell(coord, 0., &index, nullptr);
}
else {
colormap.data()->coordToCell(0., coord, nullptr, &index);
}
return index;
}
}
Alexandre Leroux
Inits tracers and timer...
r482 } // namespace
Alexandre Leroux
Creates a delegate offering methods for rendering a graph
r480 struct VisualizationGraphRenderingDelegate::VisualizationGraphRenderingDelegatePrivate {
Alexandre Leroux
Passes directly GraphWidget in delegate...
r725 explicit VisualizationGraphRenderingDelegatePrivate(VisualizationGraphWidget &graphWidget)
: m_Plot{graphWidget.plot()},
m_PointTracer{new QCPItemTracer{&m_Plot}},
m_TracerTimer{},
Alexandre Leroux
Adds title and close items in plot overlay
r726 m_ClosePixmap{new QCPItemPixmap{&m_Plot}},
Alexandre Leroux
Adds button on plot overlay to show/hide x-axis properties
r729 m_TitleText{new QCPItemText{&m_Plot}},
m_XAxisPixmap{new QCPItemPixmap{&m_Plot}},
m_ShowXAxis{true},
Alexandre Leroux
Handles axes properties for spectrograms...
r921 m_XAxisLabel{},
Alexandre Leroux
Uses SciQlop color scale in graphs
r1050 m_ColorScale{SqpColorScale{m_Plot}}
Alexandre Leroux
Inits tracers and timer...
r482 {
Alexandre Leroux
Sets tracers' styles
r485 initPointTracerStyle(*m_PointTracer);
Alexandre Leroux
Inits tracers and timer...
r482
Alexandre Leroux
Uses Qt tooltips instead of QCPTextItem...
r576 m_TracerTimer.setInterval(TOOLTIP_TIMEOUT);
Alexandre Leroux
Inits tracers and timer...
r482 m_TracerTimer.setSingleShot(true);
Alexandre Leroux
Adds title and close items in plot overlay
r726
// Inits "close button" in plot overlay
m_ClosePixmap->setLayer(OVERLAY_LAYER);
initClosePixmapStyle(*m_ClosePixmap);
Alexandre Leroux
Calls 'close graph' action when selecting close item in overlay
r727
// Connects pixmap selection to graph widget closing
Fix the close button on the graphs and multi selection in the same graph
r1094 QObject::connect(&m_Plot, &QCustomPlot::itemClick,
[&graphWidget, this](auto item, auto mouseEvent) {
if (item == m_ClosePixmap) {
Alexandre Leroux
Calls 'close graph' action when selecting close item in overlay
r727 graphWidget.close();
}
});
Alexandre Leroux
Adds title and close items in plot overlay
r726 // Inits graph name in plot overlay
m_TitleText->setLayer(OVERLAY_LAYER);
m_TitleText->setText(graphWidget.name());
initTitleTextStyle(*m_TitleText);
Alexandre Leroux
Adds button on plot overlay to show/hide x-axis properties
r729
// Inits "show x-axis button" in plot overlay
m_XAxisPixmap->setLayer(OVERLAY_LAYER);
initXAxisPixmapStyle(*m_XAxisPixmap);
// Connects pixmap selection to graph x-axis showing/hiding
Fix the close button on the graphs and multi selection in the same graph
r1094 QObject::connect(&m_Plot, &QCustomPlot::itemClick, [this](auto item, auto mouseEvent) {
if (m_XAxisPixmap == item) {
Alexandre Leroux
Adds button on plot overlay to show/hide x-axis properties
r729 // Changes the selection state and refreshes the x-axis
m_ShowXAxis = !m_ShowXAxis;
Fix the close button on the graphs and multi selection in the same graph
r1094 this->updateXAxisState();
Alexandre Leroux
Adds button on plot overlay to show/hide x-axis properties
r729 m_Plot.layer(AXES_LAYER)->replot();
// Deselects the x-axis pixmap and updates icon
m_XAxisPixmap->setPixmap(
pixmap(m_ShowXAxis ? HIDE_AXIS_ICON_PATH : SHOW_AXIS_ICON_PATH));
m_Plot.layer(OVERLAY_LAYER)->replot();
}
});
}
/// Updates state of x-axis according to the current selection of x-axis pixmap
/// @remarks the method doesn't call plot refresh
void updateXAxisState() noexcept
{
m_Plot.xAxis->setTickLabels(m_ShowXAxis);
m_Plot.xAxis->setLabel(m_ShowXAxis ? m_XAxisLabel : QString{});
Alexandre Leroux
Inits tracers and timer...
r482 }
Alexandre Leroux
Creates a delegate offering methods for rendering a graph
r480
QCustomPlot &m_Plot;
Alexandre Leroux
Inits tracers and timer...
r482 QCPItemTracer *m_PointTracer;
QTimer m_TracerTimer;
Alexandre Leroux
Adds title and close items in plot overlay
r726 QCPItemPixmap *m_ClosePixmap; /// Graph's close button
QCPItemText *m_TitleText; /// Graph's title
Alexandre Leroux
Adds button on plot overlay to show/hide x-axis properties
r729 QCPItemPixmap *m_XAxisPixmap;
bool m_ShowXAxis; /// X-axis properties are shown or hidden
QString m_XAxisLabel;
Alexandre Leroux
Uses SciQlop color scale in graphs
r1050 SqpColorScale m_ColorScale; /// Color scale used for some types of graphs (as spectrograms)
Alexandre Leroux
Creates a delegate offering methods for rendering a graph
r480 };
Alexandre Leroux
Passes directly GraphWidget in delegate...
r725 VisualizationGraphRenderingDelegate::VisualizationGraphRenderingDelegate(
VisualizationGraphWidget &graphWidget)
: impl{spimpl::make_unique_impl<VisualizationGraphRenderingDelegatePrivate>(graphWidget)}
Alexandre Leroux
Creates a delegate offering methods for rendering a graph
r480 {
}
Alexandre Leroux
Creates method that will display a tooltip and a tracer with data point information after a while
r481
Alexandre Leroux
Handles double click on color scale...
r1043 void VisualizationGraphRenderingDelegate::onMouseDoubleClick(QMouseEvent *event) noexcept
{
// Opens color scale editor if color scale is double clicked
Alexandre Leroux
Uses static_cast instead of dynamic_cast
r1070 auto colorScale = static_cast<QCPColorScale *>(impl->m_Plot.layoutElementAt(event->pos()));
Alexandre Leroux
Calls plot refresh when the editor is closed
r1054 if (impl->m_ColorScale.m_Scale == colorScale) {
if (ColorScaleEditor{impl->m_ColorScale}.exec() == QDialog::Accepted) {
impl->m_Plot.replot();
}
Alexandre Leroux
Handles double click on color scale...
r1043 }
}
Alexandre Leroux
Creates method that will display a tooltip and a tracer with data point information after a while
r481 void VisualizationGraphRenderingDelegate::onMouseMove(QMouseEvent *event) noexcept
{
Alexandre Leroux
Inits tracers and timer...
r482 // Cancels pending refresh
impl->m_TracerTimer.disconnect();
Alexandre Leroux
Uses Qt tooltips instead of QCPTextItem...
r576 // Reinits tracers
impl->m_PointTracer->setGraph(nullptr);
impl->m_PointTracer->setVisible(false);
impl->m_Plot.replot();
Alexandre Leroux
Tooltip for spectrograms (1)...
r1065 QString tooltip{};
Alexandre Leroux
Uses Qt tooltips instead of QCPTextItem...
r576 // Gets the graph under the mouse position
auto eventPos = event->pos();
if (auto graph = qobject_cast<QCPGraph *>(impl->m_Plot.plottableAt(eventPos))) {
auto mouseKey = graph->keyAxis()->pixelToCoord(eventPos.x());
auto graphData = graph->data();
// Gets the closest data point to the mouse
auto graphDataIt = graphData->findBegin(mouseKey);
if (graphDataIt != graphData->constEnd()) {
Alexandre Leroux
Tooltip for spectrograms (1)...
r1065 // Sets tooltip
Alexandre Leroux
Uses Qt tooltips instead of QCPTextItem...
r576 auto key = formatValue(graphDataIt->key, *graph->keyAxis());
auto value = formatValue(graphDataIt->value, *graph->valueAxis());
Alexandre Leroux
Tooltip for spectrograms (1)...
r1065 tooltip = GRAPH_TOOLTIP_FORMAT.arg(key, value);
Alexandre Leroux
Uses Qt tooltips instead of QCPTextItem...
r576
// Displays point tracer
impl->m_PointTracer->setGraph(graph);
impl->m_PointTracer->setGraphKey(graphDataIt->key);
impl->m_PointTracer->setLayer(
impl->m_Plot.layer("main")); // Tracer is set on top of the plot's main layer
impl->m_PointTracer->setVisible(true);
Alexandre Leroux
Show tracers method (2)...
r484 impl->m_Plot.replot();
Alexandre Leroux
Tooltip for spectrograms (1)...
r1065 }
}
else if (auto colorMap = qobject_cast<QCPColorMap *>(impl->m_Plot.plottableAt(eventPos))) {
Alexandre Leroux
Tooltip for spectrograms (2)...
r1066 // Gets x and y coords
auto x = colorMap->keyAxis()->pixelToCoord(eventPos.x());
auto y = colorMap->valueAxis()->pixelToCoord(eventPos.y());
Alexandre Leroux
Tooltip for spectrograms (3)...
r1067
// Calculates x and y cell indexes, and retrieves the underlying value
auto xCellIndex = colorMapCellIndex(*colorMap, x, true);
auto yCellIndex = colorMapCellIndex(*colorMap, y, false);
auto value = colorMap->data()->cell(xCellIndex, yCellIndex);
Alexandre Leroux
Tooltip for spectrograms (4)...
r1068 // Sets tooltips
tooltip = COLORMAP_TOOLTIP_FORMAT.arg(formatValue(x, *colorMap->keyAxis()),
formatValue(y, *colorMap->valueAxis()),
formatValue(value, *colorMap->colorScale()->axis()));
Alexandre Leroux
Tooltip for spectrograms (1)...
r1065 }
Alexandre Leroux
Inits tracers and timer...
r482
Alexandre Leroux
Tooltip for spectrograms (1)...
r1065 if (!tooltip.isEmpty()) {
// Starts timer to show tooltip after timeout
auto showTooltip = [tooltip, eventPos, this]() {
QToolTip::showText(impl->m_Plot.mapToGlobal(eventPos) + TOOLTIP_OFFSET, tooltip,
&impl->m_Plot, TOOLTIP_RECT);
};
Alexandre Leroux
Uses Qt tooltips instead of QCPTextItem...
r576
Alexandre Leroux
Tooltip for spectrograms (1)...
r1065 QObject::connect(&impl->m_TracerTimer, &QTimer::timeout, showTooltip);
impl->m_TracerTimer.start();
Alexandre Leroux
Uses Qt tooltips instead of QCPTextItem...
r576 }
Alexandre Leroux
Creates method that will display a tooltip and a tracer with data point information after a while
r481 }
Alexandre Leroux
Shows/hides plot overlay when entering/leaving graph
r728
Alexandre Leroux
Updates sqp color scale thresholds (2)...
r1061 void VisualizationGraphRenderingDelegate::onPlotUpdated() noexcept
{
// Updates color scale bounds
impl->m_ColorScale.updateDataRange();
impl->m_Plot.replot();
}
Alexandre Leroux
Refactoring handling of axes properties (2)...
r917 void VisualizationGraphRenderingDelegate::setAxesProperties(
std::shared_ptr<IDataSeries> dataSeries) noexcept
Alexandre Leroux
Adds button on plot overlay to show/hide x-axis properties
r729 {
// Stores x-axis label to be able to retrieve it when x-axis pixmap is unselected
Alexandre Leroux
Refactoring handling of axes properties (2)...
r917 impl->m_XAxisLabel = dataSeries->xAxisUnit().m_Name;
Alexandre Leroux
Adds button on plot overlay to show/hide x-axis properties
r729
Alexandre Leroux
Refactoring handling of axes properties (2)...
r917 auto axisHelper = IAxisHelperFactory::create(dataSeries);
Alexandre Leroux
Uses SciQlop color scale in graphs
r1050 axisHelper->setProperties(impl->m_Plot, impl->m_ColorScale);
Alexandre Leroux
Adds button on plot overlay to show/hide x-axis properties
r729
// Updates x-axis state
impl->updateXAxisState();
impl->m_Plot.layer(AXES_LAYER)->replot();
}
Alexandre Leroux
Handles rendering of plottables (3)...
r920 void VisualizationGraphRenderingDelegate::setPlottablesProperties(
std::shared_ptr<IDataSeries> dataSeries, PlottablesMap &plottables) noexcept
{
auto plottablesHelper = IPlottablesHelperFactory::create(dataSeries);
plottablesHelper->setProperties(plottables);
}
Alexandre Leroux
Shows/hides plot overlay when entering/leaving graph
r728 void VisualizationGraphRenderingDelegate::showGraphOverlay(bool show) noexcept
{
auto overlay = impl->m_Plot.layer(OVERLAY_LAYER);
overlay->setVisible(show);
overlay->replot();
}