Loading...
Searching...
No Matches
baseplotwidget.cpp
Go to the documentation of this file.
/* This code comes right from the msXpertSuite software project.
*
* msXpertSuite - mass spectrometry software suite
* -----------------------------------------------
* Copyright(C) 2009,...,2018 Filippo Rusconi
*
* http://www.msxpertsuite.org
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* END software license
*/
/////////////////////// StdLib includes
#include <vector>
/////////////////////// Qt includes
#include <QVector>
/////////////////////// Local includes
#include "../../core/types.h"
#include "pappsomspp/core/utils.h"
#include "baseplotwidget.h"
#include "pappsomspp/core/pappsoexception.h"
#include "pappsomspp/core/exception/exceptionnotpossible.h"
int basePlotContextMetaTypeId =
qRegisterMetaType<pappso::BasePlotContext>("pappso::BasePlotContext");
int basePlotContextPtrMetaTypeId =
qRegisterMetaType<pappso::BasePlotContext *>("pappso::BasePlotContext *");
namespace pappso
{
BasePlotWidget::BasePlotWidget(QWidget *parent): QCustomPlot(parent)
{
if(parent == nullptr)
qFatal("Programming error.");
// Default settings for the pen used to graph the data.
m_pen.setStyle(Qt::SolidLine);
m_pen.setBrush(Qt::black);
m_pen.setWidth(1);
// qDebug() << "Created new BasePlotWidget with" << layerCount()
//<< "layers before setting up widget.";
// qDebug().noquote() << "All layer names:\n" << allLayerNamesToString();
// As of today 20210313, the QCustomPlot is created with the following 6
// layers:
//
// All layers' name:
//
// Layer index 0 name: background
// Layer index 1 name: grid
// Layer index 2 name: main
// Layer index 3 name: axes
// Layer index 4 name: legend
// Layer index 5 name: overlay
if(!setupWidget())
qFatal("Programming error.");
// Do not call createAllAncillaryItems() in this base class because all the
// items will have been created *before* the addition of plots and then the
// rendering order will hide them to the viewer, since the rendering order is
// according to the order in which the items have been created.
//
// The fact that the ancillary items are created before trace plots is not a
// problem because the trace plots are sparse and do not effectively hide the
// data.
//
// But, in the color map plot widgets, we cannot afford to create the
// ancillary items *before* the plot itself because then, the rendering of the
// plot (created after) would screen off the ancillary items (created before).
//
// So, the createAllAncillaryItems() function needs to be called in the
// derived classes at the most appropriate moment in the setting up of the
// widget.
//
// All this is only a workaround of a bug in QCustomPlot. See
// https://www.qcustomplot.com/index.php/support/forum/2283.
//
// I initially wanted to have a plots layer on top of the default background
// layer and a items layer on top of it. But that setting prevented the
// selection of graphs.
// qDebug() << "Created new BasePlotWidget with" << layerCount()
//<< "layers after setting up widget.";
// qDebug().noquote() << "All layer names:\n" << allLayerNamesToString();
show();
}
BasePlotWidget::BasePlotWidget(QWidget *parent,
const QString &x_axis_label,
const QString &y_axis_label)
: QCustomPlot(parent), m_axisLabelX(x_axis_label), m_axisLabelY(y_axis_label)
{
// qDebug();
if(parent == nullptr)
qFatal("Programming error.");
// Default settings for the pen used to graph the data.
m_pen.setStyle(Qt::SolidLine);
m_pen.setBrush(Qt::black);
m_pen.setWidth(1);
xAxis->setLabel(x_axis_label);
yAxis->setLabel(y_axis_label);
// qDebug() << "Created new BasePlotWidget with" << layerCount()
//<< "layers before setting up widget.";
// qDebug().noquote() << "All layer names:\n" << allLayerNamesToString();
// As of today 20210313, the QCustomPlot is created with the following 6
// layers:
//
// All layers' name:
//
// Layer index 0 name: background
// Layer index 1 name: grid
// Layer index 2 name: main
// Layer index 3 name: axes
// Layer index 4 name: legend
// Layer index 5 name: overlay
if(!setupWidget())
qFatal("Programming error.");
// qDebug() << "Created new BasePlotWidget with" << layerCount()
//<< "layers after setting up widget.";
// qDebug().noquote() << "All layer names:\n" << allLayerNamesToString();
show();
}
//! Destruct \c this BasePlotWidget instance.
/*!
The destruction involves clearing the history, deleting all the axis range
history items for x and y axes.
*/
BasePlotWidget::~BasePlotWidget()
{
// qDebug() << "In the destructor of plot widget:" << this;
m_xAxisRangeHistory.clear();
m_yAxisRangeHistory.clear();
// Note that the QCustomPlot xxxItem objects are allocated with (this) which
// means their destruction is automatically handled upon *this' destruction.
}
QString
BasePlotWidget::allLayerNamesToString() const
{
QString text;
for(int iter = 0; iter < layerCount(); ++iter)
{
text +=
QString("Layer index %1: %2\n").arg(iter).arg(layer(iter)->name());
}
return text;
}
QString
BasePlotWidget::layerableLayerName(QCPLayerable *layerable_p) const
{
if(layerable_p == nullptr)
qFatal("Programming error.");
QCPLayer *layer_p = layerable_p->layer();
return layer_p->name();
}
int
BasePlotWidget::layerableLayerIndex(QCPLayerable *layerable_p) const
{
if(layerable_p == nullptr)
qFatal("Programming error.");
QCPLayer *layer_p = layerable_p->layer();
for(int iter = 0; iter < layerCount(); ++iter)
{
if(layer(iter) == layer_p)
return iter;
}
return -1;
}
void
BasePlotWidget::createAllAncillaryItems()
{
// Make a copy of the pen to just change its color and set that color to
// the tracer line.
QPen pen = m_pen;
// Create the lines that will act as tracers for position and selection of
// regions.
//
// We have the cross hair that serves as the cursor. That crosshair cursor is
// made of a vertical line (green, because when click-dragging the mouse it
// becomes the tracer that is being anchored at the region start. The second
// line i horizontal and is always black.
pen.setColor(QColor("steelblue"));
// The set of tracers (horizontal and vertical) that track the position of the
// mouse cursor.
mp_vPosTracerItem = new QCPItemLine(this);
mp_vPosTracerItem->setLayer("plotsLayer");
mp_vPosTracerItem->setPen(pen);
mp_vPosTracerItem->start->setType(QCPItemPosition::ptPlotCoords);
mp_vPosTracerItem->end->setType(QCPItemPosition::ptPlotCoords);
mp_vPosTracerItem->start->setCoords(0, 0);
mp_vPosTracerItem->end->setCoords(0, 0);
mp_hPosTracerItem = new QCPItemLine(this);
mp_hPosTracerItem->setLayer("plotsLayer");
mp_hPosTracerItem->setPen(pen);
mp_hPosTracerItem->start->setType(QCPItemPosition::ptPlotCoords);
mp_hPosTracerItem->end->setType(QCPItemPosition::ptPlotCoords);
mp_hPosTracerItem->start->setCoords(0, 0);
mp_hPosTracerItem->end->setCoords(0, 0);
// The set of tracers (horizontal only) that track the region
// spanning/selection regions.
//
// The start vertical tracer is colored in greeen.
pen.setColor(QColor("green"));
mp_vStartTracerItem = new QCPItemLine(this);
mp_vStartTracerItem->setLayer("plotsLayer");
mp_vStartTracerItem->setPen(pen);
mp_vStartTracerItem->start->setType(QCPItemPosition::ptPlotCoords);
mp_vStartTracerItem->end->setType(QCPItemPosition::ptPlotCoords);
mp_vStartTracerItem->start->setCoords(0, 0);
mp_vStartTracerItem->end->setCoords(0, 0);
// The end vertical tracer is colored in red.
pen.setColor(QColor("red"));
mp_vEndTracerItem = new QCPItemLine(this);
mp_vEndTracerItem->setLayer("plotsLayer");
mp_vEndTracerItem->setPen(pen);
mp_vEndTracerItem->start->setType(QCPItemPosition::ptPlotCoords);
mp_vEndTracerItem->end->setType(QCPItemPosition::ptPlotCoords);
mp_vEndTracerItem->start->setCoords(0, 0);
mp_vEndTracerItem->end->setCoords(0, 0);
// When the user click-drags the mouse, the X distance between the drag start
// point and the drag end point (current point) is the xDelta.
mp_xDeltaTextItem = new QCPItemText(this);
mp_xDeltaTextItem->setLayer("plotsLayer");
mp_xDeltaTextItem->setColor(QColor("steelblue"));
mp_xDeltaTextItem->setPositionAlignment(Qt::AlignBottom | Qt::AlignCenter);
mp_xDeltaTextItem->position->setType(QCPItemPosition::ptPlotCoords);
mp_xDeltaTextItem->setVisible(false);
// Same for the y delta
mp_yDeltaTextItem = new QCPItemText(this);
mp_yDeltaTextItem->setLayer("plotsLayer");
mp_yDeltaTextItem->setColor(QColor("steelblue"));
mp_yDeltaTextItem->setPositionAlignment(Qt::AlignBottom | Qt::AlignCenter);
mp_yDeltaTextItem->position->setType(QCPItemPosition::ptPlotCoords);
mp_yDeltaTextItem->setVisible(false);
// Make sure we prepare the four lines that will be needed to
// draw the selection rectangle.
pen = m_pen;
pen.setColor("steelblue");
mp_selectionRectangeLine1 = new QCPItemLine(this);
mp_selectionRectangeLine1->setLayer("plotsLayer");
mp_selectionRectangeLine1->setPen(pen);
mp_selectionRectangeLine1->start->setType(QCPItemPosition::ptPlotCoords);
mp_selectionRectangeLine1->end->setType(QCPItemPosition::ptPlotCoords);
mp_selectionRectangeLine1->start->setCoords(0, 0);
mp_selectionRectangeLine1->end->setCoords(0, 0);
mp_selectionRectangeLine1->setVisible(false);
mp_selectionRectangeLine2 = new QCPItemLine(this);
mp_selectionRectangeLine2->setLayer("plotsLayer");
mp_selectionRectangeLine2->setPen(pen);
mp_selectionRectangeLine2->start->setType(QCPItemPosition::ptPlotCoords);
mp_selectionRectangeLine2->end->setType(QCPItemPosition::ptPlotCoords);
mp_selectionRectangeLine2->start->setCoords(0, 0);
mp_selectionRectangeLine2->end->setCoords(0, 0);
mp_selectionRectangeLine2->setVisible(false);
mp_selectionRectangeLine3 = new QCPItemLine(this);
mp_selectionRectangeLine3->setLayer("plotsLayer");
mp_selectionRectangeLine3->setPen(pen);
mp_selectionRectangeLine3->start->setType(QCPItemPosition::ptPlotCoords);
mp_selectionRectangeLine3->end->setType(QCPItemPosition::ptPlotCoords);
mp_selectionRectangeLine3->start->setCoords(0, 0);
mp_selectionRectangeLine3->end->setCoords(0, 0);
mp_selectionRectangeLine3->setVisible(false);
mp_selectionRectangeLine4 = new QCPItemLine(this);
mp_selectionRectangeLine4->setLayer("plotsLayer");
mp_selectionRectangeLine4->setPen(pen);
mp_selectionRectangeLine4->start->setType(QCPItemPosition::ptPlotCoords);
mp_selectionRectangeLine4->end->setType(QCPItemPosition::ptPlotCoords);
mp_selectionRectangeLine4->start->setCoords(0, 0);
mp_selectionRectangeLine4->end->setCoords(0, 0);
mp_selectionRectangeLine4->setVisible(false);
}
bool
BasePlotWidget::setupWidget()
{
// qDebug();
// By default the widget comes with a graph. Remove it.
if(graphCount())
{
// QCPLayer *layer_p = graph(0)->layer();
// qDebug() << "The graph was on layer:" << layer_p->name();
// As of today 20210313, the graph is created on the currentLayer(), that
// is "main".
removeGraph(0);
}
// The general idea is that we do want custom layers for the trace|colormap
// plots.
// qDebug().noquote() << "Right before creating the new layer, layers:\n"
//<< allLayerNamesToString();
// Add the layer that will store all the plots and all the ancillary items.
addLayer(
"plotsLayer", layer("background"), QCustomPlot::LayerInsertMode::limAbove);
// Add the layer that will store the labels.
addLayer("labelsLayer", layer("background"), QCustomPlot::LayerInsertMode::limAbove);
// qDebug().noquote() << "Added new plotsLayer, layers:\n"
//<< allLayerNamesToString();
// This is required so that we get the keyboard events.
setFocusPolicy(Qt::StrongFocus);
setInteractions(QCP::iRangeZoom | QCP::iSelectPlottables | QCP::iMultiSelect);
// We want to capture the signals emitted by the QCustomPlot base class.
connect(
this, &QCustomPlot::mouseMove, this, &BasePlotWidget::mouseMoveHandler);
connect(
this, &QCustomPlot::mousePress, this, &BasePlotWidget::mousePressHandler);
connect(this,
&QCustomPlot::mouseRelease,
this,
&BasePlotWidget::mouseReleaseHandler);
connect(
this, &QCustomPlot::mouseWheel, this, &BasePlotWidget::mouseWheelHandler);
connect(this,
&QCustomPlot::axisDoubleClick,
this,
&BasePlotWidget::axisDoubleClickHandler);
connect(this, &QCustomPlot::beforeReplot, this, [&]() { emit beforeReplotSignal(); });
connect(this, &QCustomPlot::afterLayout, this, [&]() { emit afterLayoutSignal(); });
connect(this, &QCustomPlot::afterReplot, this, [&]() { emit afterReplotSignal(); });
return true;
}
void
BasePlotWidget::setPen(const QPen &pen)
{
m_pen = pen;
}
const QPen &
BasePlotWidget::getPen() const
{
return m_pen;
}
void
BasePlotWidget::setPlottingColor(QCPAbstractPlottable *plottable_p,
const QColor &new_color)
{
if(plottable_p == nullptr)
qFatal("Pointer cannot be nullptr.");
// First this single-graph widget
QPen pen;
pen = plottable_p->pen();
pen.setColor(new_color);
plottable_p->setPen(pen);
replot();
}
void
BasePlotWidget::setPlottingColor(int index, const QColor &new_color)
{
if(!new_color.isValid())
return;
QCPGraph *graph_p = graph(index);
if(graph_p == nullptr)
qFatal("Programming error.");
return setPlottingColor(graph_p, new_color);
}
QColor
BasePlotWidget::getPlottingColor(QCPAbstractPlottable *plottable_p) const
{
if(plottable_p == nullptr)
qFatal("Programming error.");
return plottable_p->pen().color();
}
QColor
BasePlotWidget::getPlottingColor(int index) const
{
QCPGraph *graph_p = graph(index);
if(graph_p == nullptr)
qFatal("Programming error.");
return getPlottingColor(graph_p);
}
void
BasePlotWidget::setAxisLabelX(const QString &label)
{
xAxis->setLabel(label);
}
void
BasePlotWidget::setAxisLabelY(const QString &label)
{
yAxis->setLabel(label);
}
// AXES RANGE HISTORY-related functions
void
BasePlotWidget::resetAxesRangeHistory()
{
m_xAxisRangeHistory.clear();
m_yAxisRangeHistory.clear();
m_xAxisRangeHistory.push_back(new QCPRange(xAxis->range()));
m_yAxisRangeHistory.push_back(new QCPRange(yAxis->range()));
// qDebug() << "size of history:" << m_xAxisRangeHistory.size()
//<< "setting index to 0";
// qDebug() << "resetting axes history to values:" << xAxis->range().lower
//<< "--" << xAxis->range().upper << "and" << yAxis->range().lower
//<< "--" << yAxis->range().upper;
m_lastAxisRangeHistoryIndex = 0;
}
//! Create new axis range history items and append them to the history.
/*!
The plot widget is queried to get the current x/y-axis ranges and the
current ranges are appended to the history for x-axis and for y-axis.
*/
void
BasePlotWidget::updateAxesRangeHistory()
{
m_xAxisRangeHistory.push_back(new QCPRange(xAxis->range()));
m_yAxisRangeHistory.push_back(new QCPRange(yAxis->range()));
m_lastAxisRangeHistoryIndex = m_xAxisRangeHistory.size() - 1;
// qDebug() << "axes history size:" << m_xAxisRangeHistory.size()
//<< "current index:" << m_lastAxisRangeHistoryIndex
//<< xAxis->range().lower << "--" << xAxis->range().upper << "and"
//<< yAxis->range().lower << "--" << yAxis->range().upper;
}
//! Go up one history element in the axis history.
/*!
If possible, back up one history item in the axis histories and update the
plot's x/y-axis ranges to match that history item.
*/
void
BasePlotWidget::restorePreviousAxesRangeHistory()
{
// qDebug() << "axes history size:" << m_xAxisRangeHistory.size()
//<< "current index:" << m_lastAxisRangeHistoryIndex;
if(m_lastAxisRangeHistoryIndex == 0)
{
// qDebug() << "current index is 0 returning doing nothing";
return;
}
// qDebug() << "Setting index to:" << m_lastAxisRangeHistoryIndex - 1
//<< "and restoring axes history to that index";
restoreAxesRangeHistory(--m_lastAxisRangeHistoryIndex);
}
//! Get the axis histories at index \p index and update the plot ranges.
/*!
\param index index at which to select the axis history item.
\sa updateAxesRangeHistory().
*/
void
BasePlotWidget::restoreAxesRangeHistory(std::size_t index)
{
// qDebug() << "Axes history size:" << m_xAxisRangeHistory.size()
//<< "current index:" << m_lastAxisRangeHistoryIndex
//<< "asking to restore index:" << index;
if(index >= m_xAxisRangeHistory.size())
{
// qDebug() << "index >= history size. Returning.";
return;
}
// We want to go back to the range history item at index, which means we want
// to pop back all the items between index+1 and size-1.
while(m_xAxisRangeHistory.size() > index + 1)
m_xAxisRangeHistory.pop_back();
if(m_xAxisRangeHistory.size() - 1 != index)
qFatal("Programming error.");
xAxis->setRange(*(m_xAxisRangeHistory.at(index)));
yAxis->setRange(*(m_yAxisRangeHistory.at(index)));
hideAllPlotItems();
mp_vPosTracerItem->setVisible(false);
mp_hPosTracerItem->setVisible(false);
mp_vStartTracerItem->setVisible(false);
mp_vEndTracerItem->setVisible(false);
// The start tracer will keep beeing represented at the last position and last
// size even if we call this function repetitively. So actually do not show,
// it will reappare as soon as the mouse is moved.
// if(m_shouldTracersBeVisible)
//{
// mp_vStartTracerItem->setVisible(true);
//}
replot();
updateContextXandYAxisRanges();
// qDebug() << "restored axes history to index:" << index
//<< "with values:" << xAxis->range().lower << "--"
//<< xAxis->range().upper << "and" << yAxis->range().lower << "--"
//<< yAxis->range().upper;
emit plotRangesChangedSignal((QMouseEvent *)nullptr, m_context);
}
// AXES RANGE HISTORY-related functions
/// KEYBOARD-related EVENTS
void
BasePlotWidget::keyPressEvent(QKeyEvent *event)
{
// qDebug() << "ENTER";
// We need this because some keys modify our behaviour.
m_context.m_pressedKeyCode = event->key();
m_context.m_pressedKeyCodes.insert(event->key());
m_context.m_releasedKeyCodes.remove(event->key());
m_context.m_pressedKeyText = event->text();
// qDebug() << "Pressed key code:" << m_context.m_pressedKeyCode;
// qDebug() << "Pressed key codes:" << m_context.m_pressedKeyCodes;
// qDebug() << "Pressed key text:" << m_context.m_pressedKeyText;
m_context.m_keyboardModifiers = QGuiApplication::queryKeyboardModifiers();
if(event->key() == Qt::Key_Left || event->key() == Qt::Key_Right ||
event->key() == Qt::Key_Up || event->key() == Qt::Key_Down)
{
return directionKeyPressEvent(event);
}
else if(event->key() == m_leftMousePseudoButtonKey ||
event->key() == m_rightMousePseudoButtonKey)
{
return mousePseudoButtonKeyPressEvent(event);
}
// Do not do anything here, because this function is used by derived classes
// that will emit the signal below. Otherwise there are going to be multiple
// signals sent.
// qDebug() << "Going to emit keyPressEventSignal(m_context);";
// emit keyPressEventSignal(m_context);
}
//! Handle specific key codes and trigger respective actions.
void
BasePlotWidget::keyReleaseEvent(QKeyEvent *event)
{
m_context.m_releasedKeyCode = event->key();
m_context.m_releasedKeyCodes.insert(event->key());
m_context.m_pressedKeyCodes.remove(event->key());
m_context.m_releasedKeyText = event->text();
// qDebug() << "Released code:" << m_context.m_releasedKeyCode;
// qDebug() << "Released codes:" << m_context.m_releasedKeyCodes;
// qDebug() << "Released key:" << m_context.m_releasedKeyText;
// The keyboard key is being released, set the key code to 0.
m_context.m_pressedKeyCode = 0;
m_context.m_pressedKeyText = "";
m_context.m_keyboardModifiers = QGuiApplication::queryKeyboardModifiers();
// Now test if the key that was released is one of the housekeeping keys.
if(event->key() == Qt::Key_Backspace)
{
// qDebug();
// The user wants to iterate back in the x/y axis range history.
restorePreviousAxesRangeHistory();
event->accept();
}
else if(event->key() == Qt::Key_Space)
{
return spaceKeyReleaseEvent(event);
}
else if(event->key() == Qt::Key_Delete)
{
// The user wants to delete a graph. What graph is to be determined
// programmatically:
// If there is a single graph, then that is the graph to be removed.
// If there are more than one graph, then only the ones that are selected
// are to be removed.
// Note that the user of this widget might want to provide the user with
// the ability to specify if all the children graph needs to be removed
// also. This can be coded in key modifiers. So provide the context.
int graph_count = plottableCount();
if(!graph_count)
{
// qDebug() << "Not a single graph in the plot widget. Doing
// nothing.";
event->accept();
return;
}
if(graph_count == 1)
{
// qDebug() << "A single graph is in the plot widget. Emitting a graph
// " "destruction requested signal for it:"
//<< graph();
emit plottableDestructionRequestedSignal(this, graph(), m_context);
}
else
{
// At this point we know there are more than one graph in the plot
// widget. We need to get the selected one (if any).
QList<QCPGraph *> selected_graph_list;
selected_graph_list = selectedGraphs();
if(!selected_graph_list.size())
{
event->accept();
return;
}
// qDebug() << "Number of selected graphs to be destrobyed:"
//<< selected_graph_list.size();
for(int iter = 0; iter < selected_graph_list.size(); ++iter)
{
// qDebug()
//<< "Emitting a graph destruction requested signal for graph:"
//<< selected_graph_list.at(iter);
emit plottableDestructionRequestedSignal(
this, selected_graph_list.at(iter), m_context);
// We do not do this, because we want the slot called by the
// signal above to handle that removal. Remember that it is not
// possible to delete graphs manually.
//
// removeGraph(selected_graph_list.at(iter));
}
event->accept();
}
}
// End of
// else if(event->key() == Qt::Key_Delete)
else if(event->key() == Qt::Key_T)
{
// The user wants to toggle the visibiity of the tracers.
m_shouldTracersBeVisible = !m_shouldTracersBeVisible;
if(!m_shouldTracersBeVisible)
hideTracers();
else
showTracers();
event->accept();
}
else if(event->key() == Qt::Key_Left || event->key() == Qt::Key_Right ||
event->key() == Qt::Key_Up || event->key() == Qt::Key_Down)
{
return directionKeyReleaseEvent(event);
}
else if(event->key() == m_leftMousePseudoButtonKey ||
event->key() == m_rightMousePseudoButtonKey)
{
return mousePseudoButtonKeyReleaseEvent(event);
}
else if(event->key() == Qt::Key_S)
{
// The user is defining the size of the rhomboid fixed side. That could be
// either a vertical side (less intuitive) or a horizontal size (more
// intuitive, first exclusive implementation). But, in order to be able to
// perform identical integrations starting from non-transposed color maps
// and transposed color maps, the ability to define a vertical fixed size
// side of the rhomboid integration scope has become necessary.
// Check if the vertical displacement is significant (>= 10% of the color
// map height.
if(isVerticalDisplacementAboveThreshold())
{
// The user is dragging the cursor vertically in a sufficient delta to
// consider that they are willing to define a vertical fixed size
// of the rhomboid integration scope.
m_context.m_integrationScopeRhombWidth = 0;
m_context.m_integrationScopeRhombHeight = abs(
m_context.m_currentDragPoint.y() - m_context.m_startDragPoint.y());
// qDebug() << "Set m_context.m_integrationScopePolyHeight to"
// << m_context.m_integrationScopeRhombHeight
// << "upon release of S key";
}
else
{
// The user is dragging the cursor horiontally to define a horizontal
// fixed size of the rhomboid integration scope.
m_context.m_integrationScopeRhombWidth = abs(
m_context.m_currentDragPoint.x() - m_context.m_startDragPoint.x());
m_context.m_integrationScopeRhombHeight = 0;
// qDebug() << "Set m_context.m_integrationScopePolyWidth to"
// << m_context.m_integrationScopeRhombWidth
// << "upon release of S key";
}
}
// At this point emit the signal, since we did not treat it. Maybe the
// consumer widget wants to know that the keyboard key was released.
emit keyReleaseEventSignal(event, m_context);
}
void
BasePlotWidget::spaceKeyReleaseEvent([[maybe_unused]] QKeyEvent *event)
{
// qDebug();
}
void
BasePlotWidget::directionKeyPressEvent(QKeyEvent *event)
{
// qDebug() << "event key:" << event->key();
// The user is trying to move the positional cursor/markers. There are
// multiple way they can do that:
//
// 1.a. Hitting the arrow left/right keys alone will search for next pixel.
// 1.b. Hitting the arrow left/right keys with Alt modifier will search for
// a multiple of pixels that might be equivalent to one 20th of the pixel
// width of the plot widget. 1.c Hitting the left/right keys with Alt and
// Shift modifiers will search for a multiple of pixels that might be the
// equivalent to half of the pixel width.
//
// 2. Hitting the Control modifier will move the cursor to the next data
// point of the graph.
int pixel_increment = 0;
if(m_context.m_keyboardModifiers == Qt::NoModifier)
pixel_increment = 1;
else if(m_context.m_keyboardModifiers == Qt::AltModifier)
pixel_increment = 50;
// The user is moving the positional markers. This is equivalent to a
// non-dragging cursor movement to the next pixel. Note that the origin is
// located at the top left, so key down increments and key up decrements.
if(event->key() == Qt::Key_Left)
horizontalMoveMouseCursorCountPixels(-pixel_increment);
else if(event->key() == Qt::Key_Right)
horizontalMoveMouseCursorCountPixels(pixel_increment);
else if(event->key() == Qt::Key_Up)
verticalMoveMouseCursorCountPixels(-pixel_increment);
else if(event->key() == Qt::Key_Down)
verticalMoveMouseCursorCountPixels(pixel_increment);
event->accept();
}
void
BasePlotWidget::directionKeyReleaseEvent(QKeyEvent *event)
{
// qDebug() << "event key:" << event->key();
event->accept();
}
void
BasePlotWidget::mousePseudoButtonKeyPressEvent(
[[maybe_unused]] QKeyEvent *event)
{
// qDebug();
}
void
BasePlotWidget::mousePseudoButtonKeyReleaseEvent(QKeyEvent *event)
{
QPointF pixel_coordinates(
xAxis->coordToPixel(m_context.m_lastCursorHoveredPoint.x()),
yAxis->coordToPixel(m_context.m_lastCursorHoveredPoint.y()));
Qt::MouseButton button = Qt::NoButton;
QEvent::Type q_event_type = QEvent::MouseButtonPress;
if(event->key() == m_leftMousePseudoButtonKey)
{
// Toggles the left mouse button on/off
button = Qt::LeftButton;
m_context.m_isLeftPseudoButtonKeyPressed =
!m_context.m_isLeftPseudoButtonKeyPressed;
if(m_context.m_isLeftPseudoButtonKeyPressed)
q_event_type = QEvent::MouseButtonPress;
else
q_event_type = QEvent::MouseButtonRelease;
}
else if(event->key() == m_rightMousePseudoButtonKey)
{
// Toggles the right mouse button.
button = Qt::RightButton;
m_context.m_isRightPseudoButtonKeyPressed =
!m_context.m_isRightPseudoButtonKeyPressed;
if(m_context.m_isRightPseudoButtonKeyPressed)
q_event_type = QEvent::MouseButtonPress;
else
q_event_type = QEvent::MouseButtonRelease;
}
// qDebug() << "pressed/released pseudo button:" << button
//<< "q_event_type:" << q_event_type;
// Synthesize a QMouseEvent and use it.
QMouseEvent *mouse_event_p =
new QMouseEvent(q_event_type,
pixel_coordinates,
mapToGlobal(pixel_coordinates.toPoint()),
mapToGlobal(pixel_coordinates.toPoint()),
button,
button,
m_context.m_keyboardModifiers,
Qt::MouseEventSynthesizedByApplication);
if(q_event_type == QEvent::MouseButtonPress)
mousePressHandler(mouse_event_p);
else
mouseReleaseHandler(mouse_event_p);
delete mouse_event_p;
// event->accept();
}
/// KEYBOARD-related EVENTS
/// MOUSE-related EVENTS
void
BasePlotWidget::mouseMoveHandler(QMouseEvent *event)
{
// If we have no focus, then get it. See setFocus() to understand why asking
// for focus is cosly and thus why we want to make this decision first.
if(!hasFocus())
setFocus();
// qDebug() << (graph() != nullptr);
// if(graph(0) != nullptr)
// { // check if the widget contains some graphs
// The event->button() must be by Qt instructions considered to be 0.
// Whatever happens, we want to store the plot coordinates of the current
// mouse cursor position (will be useful later for countless needs).
QPointF mousePoint = event->position();
// qDebug() << "local mousePoint position in pixels:" << mousePoint;
m_context.m_lastCursorHoveredPoint.setX(xAxis->pixelToCoord(mousePoint.x()));
m_context.m_lastCursorHoveredPoint.setY(yAxis->pixelToCoord(mousePoint.y()));
// qDebug() << "lastCursorHoveredPoint coord:"
//<< m_context.m_lastCursorHoveredPoint;
// Now, depending on the button(s) (if any) that are pressed or not, we
// have a different processing.
// qDebug();
if(m_context.m_pressedMouseButtons & Qt::LeftButton ||
m_context.m_pressedMouseButtons & Qt::RightButton)
{
mouseMoveHandlerDraggingCursor(event);
// qDebug() << "Emitting mouseMoveDraggingCursorSignal";
emit mouseMoveDraggingCursorSignal(event, m_context);
}
else
mouseMoveHandlerNotDraggingCursor(event);
// }
// qDebug();
event->accept();
}
void
BasePlotWidget::mouseMoveHandlerNotDraggingCursor(QMouseEvent *event)
{
Q_UNUSED(event)
// qDebug();
m_context.m_isMouseDragging = false;
// qDebug();
// We are not dragging the mouse (no button pressed), simply let this
// widget's consumer know the position of the cursor and update the markers.
// The consumer of this widget will update mouse cursor position at
// m_context.m_lastCursorHoveredPoint if so needed.
emit lastCursorHoveredPointSignal(m_context.m_lastCursorHoveredPoint);
// qDebug();
// We are not dragging, so we do not show the region end tracer we only
// show the anchoring start trace that might be of use if the user starts
// using the arrow keys to move the cursor.
if(mp_vEndTracerItem != nullptr)
mp_vEndTracerItem->setVisible(false);
// qDebug();
// Only bother with the tracers if the user wants them to be visible.
// Their crossing point must be exactly at the last cursor-hovered point.
if(m_shouldTracersBeVisible)
{
// We are not dragging, so only show the position markers (v and h);
// qDebug();
if(mp_hPosTracerItem != nullptr)
{
// Horizontal position tracer.
mp_hPosTracerItem->setVisible(true);
mp_hPosTracerItem->start->setCoords(
xAxis->range().lower, m_context.m_lastCursorHoveredPoint.y());
mp_hPosTracerItem->end->setCoords(
xAxis->range().upper, m_context.m_lastCursorHoveredPoint.y());
}
// qDebug();
// Vertical position tracer.
if(mp_vPosTracerItem != nullptr)
{
mp_vPosTracerItem->setVisible(true);
mp_vPosTracerItem->setVisible(true);
mp_vPosTracerItem->start->setCoords(
m_context.m_lastCursorHoveredPoint.x(), yAxis->range().upper);
mp_vPosTracerItem->end->setCoords(
m_context.m_lastCursorHoveredPoint.x(), yAxis->range().lower);
}
// qDebug();
replot();
}
return;
}
void
BasePlotWidget::mouseMoveHandlerDraggingCursor(QMouseEvent *event)
{
// qDebug();
m_context.m_isMouseDragging = true;
// Now store the mouse position data into the the current drag point
// member datum, that will be used in countless occasions later.
m_context.m_currentDragPoint = m_context.m_lastCursorHoveredPoint;
m_context.m_keyboardModifiers = QGuiApplication::queryKeyboardModifiers();
// When we drag (either keyboard or mouse), we hide the position markers
// (black) and we show the start and end vertical markers for the region.
// Then, we draw the horizontal region range marker that delimits
// horizontally the dragged-over region.
if(mp_hPosTracerItem != nullptr)
mp_hPosTracerItem->setVisible(false);
if(mp_vPosTracerItem != nullptr)
mp_vPosTracerItem->setVisible(false);
// Only bother with the tracers if the user wants them to be visible.
if(m_shouldTracersBeVisible && (mp_vEndTracerItem != nullptr))
{
// The vertical end tracer position must be refreshed.
mp_vEndTracerItem->start->setCoords(m_context.m_currentDragPoint.x(),
yAxis->range().upper);
mp_vEndTracerItem->end->setCoords(m_context.m_currentDragPoint.x(),
yAxis->range().lower);
mp_vEndTracerItem->setVisible(true);
}
// Whatever the button, when we are dealing with the axes, we do not
// want to show any of the tracers.
if(m_context.m_wasClickOnXAxis || m_context.m_wasClickOnYAxis)
{
if(mp_hPosTracerItem != nullptr)
mp_hPosTracerItem->setVisible(false);
if(mp_vPosTracerItem != nullptr)
mp_vPosTracerItem->setVisible(false);
if(mp_vStartTracerItem != nullptr)
mp_vStartTracerItem->setVisible(false);
if(mp_vEndTracerItem != nullptr)
mp_vEndTracerItem->setVisible(false);
}
else
{
// qDebug() << "Not moving the mouse cursor over any of the axes.";
// Since we are not dragging the mouse cursor over the axes, make sure
// we store the drag directions in the context, as this might be
// useful for later operations.
// qDebug() << "Recording the drag direction(s).";
m_context.recordDragDirections();
// qDebug() << "Drag direction(s): " <<
// m_context.dragDirectionsToString();
}
// Because when we drag the mouse button (whatever the button) we need to
// know what is the drag delta (distance between start point and current
// point of the drag operation) on both axes, ask that these x|y deltas be
// computed.
calculateDragDeltas();
// Now deal with the BUTTON-SPECIFIC CODE.
if(m_context.m_mouseButtonsAtMousePress & Qt::LeftButton)
{
return mouseMoveHandlerLeftButtonDraggingCursor(event);
}
else if(m_context.m_mouseButtonsAtMousePress & Qt::RightButton)
{
return mouseMoveHandlerRightButtonDraggingCursor(event);
}
}
void
BasePlotWidget::mouseMoveHandlerLeftButtonDraggingCursor(QMouseEvent *event)
{
Q_UNUSED(event)
// qDebug() << "The left button is dragging.";
// Set the context.m_isMeasuringDistance to false, which later might be set
// to true if effectively we are measuring a distance. This is required
// because the derived widget classes might want to know if they have to
// perform some action on the basis that context is measuring a distance,
// for example the mass spectrum-specific widget might want to compute
// deconvolutions.
m_context.m_isMeasuringDistance = false;
// Let's first check if the mouse drag operation originated on either
// axis. In that case, the user is performing axis reframing or rescaling.
if(m_context.m_wasClickOnXAxis || m_context.m_wasClickOnYAxis)
{
// qDebug() << "Click was on one of the axes.";
if(m_context.m_keyboardModifiers & Qt::ControlModifier)
{
// The user is asking a rescale of the plot.
// We know that we do not want the tracers when we perform axis
// rescaling operations.
if(mp_hPosTracerItem != nullptr)
mp_hPosTracerItem->setVisible(false);
if(mp_vPosTracerItem != nullptr)
mp_vPosTracerItem->setVisible(false);
if(mp_vStartTracerItem != nullptr)
mp_vStartTracerItem->setVisible(false);
if(mp_vEndTracerItem != nullptr)
mp_vEndTracerItem->setVisible(false);
// This operation is particularly intensive, thus we want to
// reduce the number of calculations by skipping this calculation
// a number of times. The user can ask for this feature by
// clicking the 'Q' letter.
if(m_context.m_pressedKeyCode == Qt::Key_Q)
{
if(m_mouseMoveHandlerSkipCount < m_mouseMoveHandlerSkipAmount)
{
m_mouseMoveHandlerSkipCount++;
return;
}
else
{
m_mouseMoveHandlerSkipCount = 0;
}
}
// qDebug() << "Asking that the axes be rescaled.";
axisRescale();
}
else
{
// The user was simply dragging the axis. Just pan, that is slide
// the plot in the same direction as the mouse movement and with the
// same amplitude.
// qDebug() << "Asking that the axes be panned.";
axisPan();
}
return;
}
// At this point we understand that the user was not performing any
// panning/rescaling operation by clicking on any one of the axes.. Go on
// with other possibilities.
// Let's check if the user is actually drawing a rectangle (covering a
// real area) or is drawing a line.
// qDebug() << "The mouse dragging did not originate on an axis.";
if(isVerticalDisplacementAboveThreshold())
{
// qDebug() << "Apparently the selection is two-dimensional.";
// When we draw a two-dimensional integration scope, the tracers are of no
// use.
if(mp_hPosTracerItem != nullptr)
mp_hPosTracerItem->setVisible(false);
if(mp_vPosTracerItem != nullptr)
mp_vPosTracerItem->setVisible(false);
if(mp_vStartTracerItem != nullptr)
mp_vStartTracerItem->setVisible(false);
if(mp_vEndTracerItem != nullptr)
mp_vEndTracerItem->setVisible(false);
// Draw the rectangle, false, not as line segment and
// false, not for integration
drawSelectionRectangleAndPrepareZoom(false /*as_line_segment*/,
false /* for_integration*/);
// Draw the selection width/height text
drawXScopeSpanFeatures();
drawYScopeSpanFeatures();
}
else
{
// qDebug() << "Apparently we are measuring a delta.";
// Draw the rectangle, true, as line segment and
// false, not for integration
drawSelectionRectangleAndPrepareZoom(true, false);
// The pure position tracers should be hidden.
if(mp_hPosTracerItem != nullptr)
mp_hPosTracerItem->setVisible(true);
if(mp_vPosTracerItem != nullptr)
mp_vPosTracerItem->setVisible(true);
// Then, make sure the region range vertical tracers are visible.
if(mp_vStartTracerItem != nullptr)
mp_vStartTracerItem->setVisible(true);
if(mp_vEndTracerItem != nullptr)
mp_vEndTracerItem->setVisible(true);
// Draw the selection width text
drawXScopeSpanFeatures();
}
}
void
BasePlotWidget::mouseMoveHandlerRightButtonDraggingCursor(QMouseEvent *event)
{
Q_UNUSED(event)
// qDebug() << "The right button is dragging.";
// Set the context.m_isMeasuringDistance to false, which later might be set
// to true if effectively we are measuring a distance. This is required
// because the derived widgets might want to know if they have to perform
// some action on the basis that context is measuring a distance, for
// example the mass spectrum-specific widget might want to compute
// deconvolutions.
m_context.m_isMeasuringDistance = false;
if(isVerticalDisplacementAboveThreshold())
{
// qDebug() << "Apparently the selection has height.";
// When we draw a rectangle the tracers are of no use.
if(mp_hPosTracerItem != nullptr)
mp_hPosTracerItem->setVisible(false);
if(mp_vPosTracerItem != nullptr)
mp_vPosTracerItem->setVisible(false);
if(mp_vStartTracerItem != nullptr)
mp_vStartTracerItem->setVisible(false);
if(mp_vEndTracerItem != nullptr)
mp_vEndTracerItem->setVisible(false);
// Draw the rectangle, false for as_line_segment and true for
// integration.
drawSelectionRectangleAndPrepareZoom(false, true);
// Draw the selection width/height text
drawXScopeSpanFeatures();
drawYScopeSpanFeatures();
}
else
{
// qDebug() << "Apparently the selection is a not a rectangle.";
// Draw the rectangle, true as line segment and
// true for integration
drawSelectionRectangleAndPrepareZoom(true, true);
// Draw the selection width text
drawXScopeSpanFeatures();
}
}
void
BasePlotWidget::mousePressHandler(QMouseEvent *event)
{
// qDebug() << "Entering";
// When the user clicks this widget it has to take focus.
setFocus();
QPointF mousePoint = event->position();
m_context.m_lastPressedMouseButton = event->button();
m_context.m_mouseButtonsAtMousePress = event->buttons();
// The pressedMouseButtons must continually inform on the status of
// pressed buttons so add the pressed button.
m_context.m_pressedMouseButtons |= event->button();
// qDebug().noquote() << m_context.toString();
// In all the processing of the events, we need to know if the user is
// clicking somewhere with the intent to change the plot ranges (reframing
// or rescaling the plot).
//
// Reframing the plot means that the new x and y axes ranges are modified
// so that they match the region that the user has encompassed by left
// clicking the mouse and dragging it over the plot. That is we reframe
// the plot so that it contains only the "selected" region.
//
// Rescaling the plot means the the new x|y axis range is modified such
// that the lower axis range is constant and the upper axis range is moved
// either left or right by the same amont as the x|y delta encompassed by
// the user moving the mouse. The axis is thus either compressed (mouse
// movement is leftwards) or un-compressed (mouse movement is rightwards).
// There are two ways to perform axis range modifications:
//
// 1. By clicking on any of the axes
// 2. By clicking on the plot region but using keyboard key modifiers,
// like Alt and Ctrl.
//
// We need to know both cases separately which is why we need to perform a
// number of tests below.
// Let's check if the click is on the axes, either X or Y, because that
// will allow us to take proper actions.
if(isClickOntoXAxis(mousePoint))
{
// The X axis was clicked upon, we need to document that:
// qDebug() << __FILE__ << __LINE__
//<< "Layout element is axisRect and actually on an X axis part.";
m_context.m_wasClickOnXAxis = true;
// int currentInteractions = interactions();
// currentInteractions |= QCP::iRangeDrag;
// setInteractions((QCP::Interaction)currentInteractions);
// axisRect()->setRangeDrag(xAxis->orientation());
}
else
m_context.m_wasClickOnXAxis = false;
if(isClickOntoYAxis(mousePoint))
{
// The Y axis was clicked upon, we need to document that:
// qDebug() << __FILE__ << __LINE__
//<< "Layout element is axisRect and actually on an Y axis part.";
m_context.m_wasClickOnYAxis = true;
// int currentInteractions = interactions();
// currentInteractions |= QCP::iRangeDrag;
// setInteractions((QCP::Interaction)currentInteractions);
// axisRect()->setRangeDrag(yAxis->orientation());
}
else
m_context.m_wasClickOnYAxis = false;
// At this point, let's see if we need to remove the QCP::iRangeDrag bit:
if(!m_context.m_wasClickOnXAxis && !m_context.m_wasClickOnYAxis)
{
// qDebug() << __FILE__ << __LINE__
// << "Click outside of axes.";
// int currentInteractions = interactions();
// currentInteractions = currentInteractions & ~QCP::iRangeDrag;
// setInteractions((QCP::Interaction)currentInteractions);
}
m_context.m_startDragPoint.setX(xAxis->pixelToCoord(mousePoint.x()));
m_context.m_startDragPoint.setY(yAxis->pixelToCoord(mousePoint.y()));
// Now install the vertical start tracer at the last cursor hovered
// position.
if((m_shouldTracersBeVisible) && (mp_vStartTracerItem != nullptr))
mp_vStartTracerItem->setVisible(true);
if(mp_vStartTracerItem != nullptr)
{
mp_vStartTracerItem->start->setCoords(
m_context.m_lastCursorHoveredPoint.x(), yAxis->range().upper);
mp_vStartTracerItem->end->setCoords(
m_context.m_lastCursorHoveredPoint.x(), yAxis->range().lower);
}
replot();
emit mousePressEventSignal(event, m_context);
// qDebug() << "Exiting after having emitted mousePressEventSignal with base
// context:"
// << m_context.toString();
}
void
BasePlotWidget::mouseReleaseHandler(QMouseEvent *event)
{
// qDebug() << "Entering";
// Now the real code of this function.
m_context.m_lastReleasedMouseButton = event->button();
// The event->buttons() is the description of the buttons that are pressed
// at the moment the handler is invoked, that is now. If left and right were
// pressed, and left was released, event->buttons() would be right.
m_context.m_mouseButtonsAtMouseRelease = event->buttons();
// The pressedMouseButtons must continually inform on the status of pressed
// buttons so remove the released button.
m_context.m_pressedMouseButtons ^= event->button();
// qDebug().noquote() << m_context.toString();
// We'll need to know if modifiers were pressed a the moment the user
// released the mouse button.
m_context.m_keyboardModifiers = QGuiApplication::keyboardModifiers();
if(!m_context.m_isMouseDragging)
{
// Let the user know that the mouse was *not* being dragged.
m_context.m_wasMouseDragging = false;
event->accept();
return;
}
// Let the user know that the mouse was being dragged.
m_context.m_wasMouseDragging = true;
// We cannot hide all items in one go because we rely on their visibility
// to know what kind of dragging operation we need to perform (line-only
// X-based zoom or rectangle-based X- and Y-based zoom, for example). The
// only thing we know is that we can make the text invisible.
// Same for the x delta text item
mp_xDeltaTextItem->setVisible(false);
mp_yDeltaTextItem->setVisible(false);
// We do not show the end vertical region range marker.
mp_vEndTracerItem->setVisible(false);
// Horizontal position tracer.
mp_hPosTracerItem->setVisible(true);
mp_hPosTracerItem->start->setCoords(xAxis->range().lower,
m_context.m_lastCursorHoveredPoint.y());
mp_hPosTracerItem->end->setCoords(xAxis->range().upper,
m_context.m_lastCursorHoveredPoint.y());
// Vertical position tracer.
mp_vPosTracerItem->setVisible(true);
mp_vPosTracerItem->setVisible(true);
mp_vPosTracerItem->start->setCoords(m_context.m_lastCursorHoveredPoint.x(),
yAxis->range().upper);
mp_vPosTracerItem->end->setCoords(m_context.m_lastCursorHoveredPoint.x(),
yAxis->range().lower);
// Force replot now because later that call might not be performed.
replot();
// If we were using the "quantum" display for the rescale of the axes
// using the Ctrl-modified left button click drag in the axes, then reset
// the count to 0.
m_mouseMoveHandlerSkipCount = 0;
// By definition we are stopping the drag operation by releasing the mouse
// button. Whatever that mouse button was pressed before and if there was
// one pressed before. We cannot set that boolean value to false before
// this place, because we call a number of routines above that need to know
// that dragging was occurring. Like mouseReleaseHandledEvent(event) for
// example.
m_context.m_isMouseDragging = false;
// Now that we have computed the useful ranges, we need to check what to do
// depending on the button that was pressed.
if(m_context.m_lastReleasedMouseButton == Qt::LeftButton)
{
return mouseReleaseHandlerLeftButton(event);
}
else if(m_context.m_lastReleasedMouseButton == Qt::RightButton)
{
return mouseReleaseHandlerRightButton(event);
}
// FIXME: should we really accept ? No, since we pass the event on.
// event->accept();
// Before returning, emit the signal for the user of
// this class consumption.
// qDebug() << "Emitting mouseReleaseEventSignal.";
emit mouseReleaseEventSignal(event, m_context);
// qDebug() << "Exiting after having emitted mouseReleaseEventSignal with base
// context:"
// << m_context.toString();
return;
}
void
BasePlotWidget::mouseReleaseHandlerLeftButton(QMouseEvent *event)
{
Q_UNUSED(event)
// qDebug();
if(m_context.m_wasClickOnXAxis || m_context.m_wasClickOnYAxis)
{
// When the mouse move handler pans the plot, we cannot store each axes
// range history element that would mean store a huge amount of such
// elements, as many element as there are mouse move event handled by
// the Qt event queue. But we can store an axis range history element
// for the last situation of the mouse move: when the button is
// released:
updateAxesRangeHistory();
// qDebug() << "emit plotRangesChangedSignal(m_context);"
emit plotRangesChangedSignal((QMouseEvent *)nullptr, m_context);
replot();
// Nothing else to do.
return;
}
// There are two possibilities:
//
// 1. The full integration scope (four lines) were currently drawn, which
// means the user was willing to perform a zoom operation.
//
// 2. Only the first top line was drawn, which means the user was dragging
// the cursor horizontally. That might have two ends, as shown below.
// So, first check what is drawn of the selection polygon.
SelectionDrawingLines selection_drawing_lines =
whatIsVisibleOfTheSelectionRectangle();
// Now that we know what was currently drawn of the selection polygon, we
// can remove it. true to reset the values to 0.
hideSelectionRectangle(true);
// Force replot now because later that call might not be performed.
replot();
if(selection_drawing_lines == SelectionDrawingLines::FULL_POLYGON)
{
// qDebug() << "Yes, the full polygon was visible";
// If we were dragging with the left button pressed and could draw a
// rectangle, then we were preparing a zoom operation. Let's bring that
// operation to its accomplishment.
axisZoom();
return;
}
else if(selection_drawing_lines == SelectionDrawingLines::TOP_LINE)
{
// qDebug() << "No, only the top line of the full polygon was visible";
// The user was dragging the left mouse cursor and that may mean they
// were measuring a distance or willing to perform a special zoom
// operation if the Ctrl key was down.
// If the user started by clicking in the plot region, dragged the mouse
// cursor with the left button and pressed the Ctrl modifier, then that
// means that they wanted to do a rescale over the x-axis in the form of
// a reframing.
if(m_context.m_keyboardModifiers & Qt::ControlModifier)
{
return axisReframe();
}
}
// else
// qDebug() << "Another possibility.";
}
void
BasePlotWidget::mouseReleaseHandlerRightButton(QMouseEvent *event)
{
Q_UNUSED(event)
// qDebug();
// The right button is used for the integrations. Not for axis range
// operations. So all we have to do is remove the various graphics items and
// send a signal with the context that contains all the data required by the
// user to perform the integrations over the right plot regions.
// Whatever we were doing we need to make the selection line invisible:
if(mp_xDeltaTextItem->visible())
mp_xDeltaTextItem->setVisible(false);
if(mp_yDeltaTextItem->visible())
mp_yDeltaTextItem->setVisible(false);
// Also make the vertical end tracer invisible.
mp_vEndTracerItem->setVisible(false);
// Once the integration is asked for, then the selection rectangle if of no
// more use.
hideSelectionRectangle();
// Force replot now because later that call might not be performed.
replot();
// Note that we only request an integration if the x-axis delta is enough.
double x_delta_pixel =
fabs(xAxis->coordToPixel(m_context.m_currentDragPoint.x()) -
xAxis->coordToPixel(m_context.m_startDragPoint.x()));
if(x_delta_pixel > 3)
{
// qDebug() << "Emitting integrationRequestedSignal(m_context)";
emit integrationRequestedSignal(m_context);
}
// else
// qDebug() << "Not asking for integration.";
}
void
BasePlotWidget::mouseWheelHandler([[maybe_unused]] QWheelEvent *event)
{
// We should record the new range values each time the wheel is used to
// zoom/unzoom.
m_context.m_xRange = QCPRange(xAxis->range());
m_context.m_yRange = QCPRange(yAxis->range());
// qDebug() << "New x range: " << m_context.m_xRange;
// qDebug() << "New y range: " << m_context.m_yRange;
updateAxesRangeHistory();
emit plotRangesChangedWheelEventSignal(event, m_context);
emit mouseWheelEventSignal(event, m_context);
event->accept();
}
void
BasePlotWidget::axisDoubleClickHandler(
QCPAxis *axis,
[[maybe_unused]] QCPAxis::SelectablePart part,
QMouseEvent *event)
{
// qDebug();
m_context.m_keyboardModifiers = QGuiApplication::queryKeyboardModifiers();
if(m_context.m_keyboardModifiers & Qt::ControlModifier)
{
// qDebug();
// If the Ctrl modifiers is active, then both axes are to be reset. Also
// the histories are reset also.
rescaleAxes();
resetAxesRangeHistory();
}
else
{
// qDebug();
// Only the axis passed as parameter is to be rescaled.
// Reset the range of that axis to the max view possible.
axis->rescale();
updateAxesRangeHistory();
event->accept();
}
// The double-click event does not cancel the mouse press event. That is, if
// left-double-clicking, at the end of the operation the button still
// "pressed". We need to remove manually the button from the pressed buttons
// context member.
m_context.m_pressedMouseButtons ^= event->button();
updateContextXandYAxisRanges();
emit plotRangesChangedSignal(event, m_context);
replot();
}
bool
BasePlotWidget::isClickOntoXAxis(const QPointF &mousePoint)
{
QCPLayoutElement *layoutElement = layoutElementAt(mousePoint);
if(layoutElement &&
layoutElement == dynamic_cast<QCPLayoutElement *>(axisRect()))
{
// The graph is *inside* the axisRect that is the outermost envelope of
// the graph. Thus, if we want to know if the click was indeed on an
// axis, we need to check what selectable part of the the axisRect we
// were clicking:
QCPAxis::SelectablePart selectablePart;
selectablePart = xAxis->getPartAt(mousePoint);
if(selectablePart == QCPAxis::spAxisLabel ||
selectablePart == QCPAxis::spAxis ||
selectablePart == QCPAxis::spTickLabels)
return true;
}
return false;
}
bool
BasePlotWidget::isClickOntoYAxis(const QPointF &mousePoint)
{
QCPLayoutElement *layoutElement = layoutElementAt(mousePoint);
if(layoutElement &&
layoutElement == dynamic_cast<QCPLayoutElement *>(axisRect()))
{
// The graph is *inside* the axisRect that is the outermost envelope of
// the graph. Thus, if we want to know if the click was indeed on an
// axis, we need to check what selectable part of the the axisRect we
// were clicking:
QCPAxis::SelectablePart selectablePart;
selectablePart = yAxis->getPartAt(mousePoint);
if(selectablePart == QCPAxis::spAxisLabel ||
selectablePart == QCPAxis::spAxis ||
selectablePart == QCPAxis::spTickLabels)
return true;
}
return false;
}
/// MOUSE-related EVENTS
/// MOUSE MOVEMENTS mouse/keyboard-triggered
int
BasePlotWidget::dragDirection()
{
// The user is dragging the mouse, probably to rescale the axes, but we need
// to sort out in which direction the drag is happening.
// This function should be called after calculateDragDeltas, so that
// m_context has the proper x/y delta values that we'll compare.
// Note that we cannot compare simply x or y deltas because the y axis might
// have a different scale that the x axis. So we first need to convert the
// positions to pixels.
double x_delta_pixel =
fabs(xAxis->coordToPixel(m_context.m_currentDragPoint.x()) -
xAxis->coordToPixel(m_context.m_startDragPoint.x()));
double y_delta_pixel =
fabs(yAxis->coordToPixel(m_context.m_currentDragPoint.y()) -
yAxis->coordToPixel(m_context.m_startDragPoint.y()));
if(x_delta_pixel > y_delta_pixel)
return Qt::Horizontal;
return Qt::Vertical;
}
void
BasePlotWidget::moveMouseCursorGraphCoordToGlobal(QPointF graph_coordinates)
{
// First convert the graph coordinates to pixel coordinates.
QPointF pixels_coordinates(xAxis->coordToPixel(graph_coordinates.x()),
yAxis->coordToPixel(graph_coordinates.y()));
moveMouseCursorPixelCoordToGlobal(pixels_coordinates.toPoint());
}
void
BasePlotWidget::moveMouseCursorPixelCoordToGlobal(QPointF pixel_coordinates)
{
// qDebug() << "Calling set pos with new cursor position.";
QCursor::setPos(mapToGlobal(pixel_coordinates.toPoint()));
}
void
BasePlotWidget::horizontalMoveMouseCursorCountPixels(int pixel_count)
{
QPointF graph_coord = horizontalGetGraphCoordNewPointCountPixels(pixel_count);
QPointF pixel_coord(xAxis->coordToPixel(graph_coord.x()),
yAxis->coordToPixel(graph_coord.y()));
// Now we need ton convert the new coordinates to the global position system
// and to move the cursor to that new position. That will create an event to
// move the mouse cursor.
moveMouseCursorPixelCoordToGlobal(pixel_coord.toPoint());
}
QPointF
BasePlotWidget::horizontalGetGraphCoordNewPointCountPixels(int pixel_count)
{
QPointF pixel_coordinates(
xAxis->coordToPixel(m_context.m_lastCursorHoveredPoint.x()) + pixel_count,
yAxis->coordToPixel(m_context.m_lastCursorHoveredPoint.y()));
// Now convert back to local coordinates.
QPointF graph_coordinates(xAxis->pixelToCoord(pixel_coordinates.x()),
yAxis->pixelToCoord(pixel_coordinates.y()));
return graph_coordinates;
}
void
BasePlotWidget::verticalMoveMouseCursorCountPixels(int pixel_count)
{
QPointF graph_coord = verticalGetGraphCoordNewPointCountPixels(pixel_count);
QPointF pixel_coord(xAxis->coordToPixel(graph_coord.x()),
yAxis->coordToPixel(graph_coord.y()));
// Now we need ton convert the new coordinates to the global position system
// and to move the cursor to that new position. That will create an event to
// move the mouse cursor.
moveMouseCursorPixelCoordToGlobal(pixel_coord.toPoint());
}
QPointF
BasePlotWidget::verticalGetGraphCoordNewPointCountPixels(int pixel_count)
{
QPointF pixel_coordinates(
xAxis->coordToPixel(m_context.m_lastCursorHoveredPoint.x()),
yAxis->coordToPixel(m_context.m_lastCursorHoveredPoint.y()) + pixel_count);
// Now convert back to local coordinates.
QPointF graph_coordinates(xAxis->pixelToCoord(pixel_coordinates.x()),
yAxis->pixelToCoord(pixel_coordinates.y()));
return graph_coordinates;
}
/// MOUSE MOVEMENTS mouse/keyboard-triggered
/// RANGE-related functions
QCPRange
BasePlotWidget::getRangeX(bool &found_range, int index) const
{
QCPGraph *graph_p = graph(index);
if(graph_p == nullptr)
qFatal("Programming error.");
return graph_p->getKeyRange(found_range);
}
QCPRange
BasePlotWidget::getRangeY(bool &found_range, int index) const
{
QCPGraph *graph_p = graph(index);
if(graph_p == nullptr)
qFatal("Programming error.");
return graph_p->getValueRange(found_range);
}
QCPRange
BasePlotWidget::getRange(Enums::Axis axis,
RangeType range_type,
bool &found_range) const
{
// Iterate in all the graphs in this widget and return a QCPRange that has
// its lower member as the greatest lower value of all
// its upper member as the smallest upper value of all
if(!graphCount())
{
found_range = false;
return QCPRange(0, 1);
}
if(graphCount() == 1)
return graph()->getKeyRange(found_range);
bool found_at_least_one_range = false;
// Create an invalid range.
QCPRange result_range(QCPRange::minRange + 1, QCPRange::maxRange + 1);
for(int iter = 0; iter < graphCount(); ++iter)
{
QCPRange temp_range;
bool found_range_for_iter = false;
QCPGraph *graph_p = graph(iter);
// Depending on the axis param, select the key or value range.
if(axis == Enums::Axis::x)
temp_range = graph_p->getKeyRange(found_range_for_iter);
else if(axis == Enums::Axis::y)
temp_range = graph_p->getValueRange(found_range_for_iter);
else
qFatal("Cannot reach this point. Programming error.");
// Was a range found for the iterated graph ? If not skip this
// iteration.
if(!found_range_for_iter)
continue;
// While the innermost_range is invalid, we need to seed it with a good
// one. So check this.
if(!QCPRange::validRange(result_range))
qFatal("The obtained range is invalid !");
// At this point we know the obtained range is OK.
result_range = temp_range;
// We found at least one valid range!
found_at_least_one_range = true;
// At this point we have two valid ranges to compare. Depending on
// range_type, we need to perform distinct comparisons.
if(range_type == RangeType::innermost)
{
if(temp_range.lower > result_range.lower)
result_range.lower = temp_range.lower;
if(temp_range.upper < result_range.upper)
result_range.upper = temp_range.upper;
}
else if(range_type == RangeType::outermost)
{
if(temp_range.lower < result_range.lower)
result_range.lower = temp_range.lower;
if(temp_range.upper > result_range.upper)
result_range.upper = temp_range.upper;
}
else
qFatal("Cannot reach this point. Programming error.");
// Continue to next graph, if any.
}
// End of
// for(int iter = 0; iter < graphCount(); ++iter)
// Let the caller know if we found at least one range.
found_range = found_at_least_one_range;
return result_range;
}
QCPRange
BasePlotWidget::getInnermostRangeX(bool &found_range) const
{
return getRange(Enums::Axis::x, RangeType::innermost, found_range);
}
QCPRange
BasePlotWidget::getOutermostRangeX(bool &found_range) const
{
return getRange(Enums::Axis::x, RangeType::outermost, found_range);
}
QCPRange
BasePlotWidget::getInnermostRangeY(bool &found_range) const
{
return getRange(Enums::Axis::y, RangeType::innermost, found_range);
}
QCPRange
BasePlotWidget::getOutermostRangeY(bool &found_range) const
{
return getRange(Enums::Axis::y, RangeType::outermost, found_range);
}
/// RANGE-related functions
/// PLOTTING / REPLOTTING functions
void
BasePlotWidget::axisRescale()
{
// Get the current x lower/upper range, that is, leftmost/rightmost x
// coordinate.
double xLower = xAxis->range().lower;
double xUpper = xAxis->range().upper;
// Get the current y lower/upper range, that is, bottommost/topmost y
// coordinate.
double yLower = yAxis->range().lower;
double yUpper = yAxis->range().upper;
// This function is called only when the user has clicked on the x/y axis or
// when the user has dragged the left mouse button with the Ctrl key
// modifier. The m_context.m_wasClickOnXAxis is then simulated in the mouse
// move handler. So we need to test which axis was clicked-on.
if(m_context.m_wasClickOnXAxis)
{
// We are changing the range of the X axis.
// If xDelta is < 0, then we were dragging from right to left, we are
// compressing the view on the x axis, by adding new data to the right
// hand size of the graph. So we add xDelta to the upper bound of the
// range. Otherwise we are uncompressing the view on the x axis and
// remove the xDelta from the upper bound of the range. This is why we
// have the
// '-'
// and not '+' below;
xAxis->setRange(xLower, xUpper - m_context.m_xDelta);
}
// End of
// if(m_context.m_wasClickOnXAxis)
else // that is, if(m_context.m_wasClickOnYAxis)
{
// We are changing the range of the Y axis.
// See above for an explanation of the computation (the - sign below).
yAxis->setRange(yLower, yUpper - m_context.m_yDelta);
}
// End of
// else // that is, if(m_context.m_wasClickOnYAxis)
// Update the context with the current axes ranges
updateContextXandYAxisRanges();
emit plotRangesChangedSignal((QMouseEvent *)nullptr, m_context);
replot();
}
void
BasePlotWidget::axisReframe()
{
// double sorted_start_drag_point_x =
// std::min(m_context.m_startDragPoint.x(),
// m_context.m_currentDragPoint.x());
// xAxis->setRange(sorted_start_drag_point_x,
// sorted_start_drag_point_x + fabs(m_context.m_xDelta));
xAxis->setRange(QCPRange(m_context.m_xRegionRangeStart, m_context.m_xRegionRangeStop));
// Note that the y axis should be rescaled from current lower value to new
// upper value matching the y-axis position of the cursor when the mouse
// button was released.
yAxis->setRange(xAxis->range().lower,
std::max<double>(m_context.m_yRegionRangeStart, m_context.m_yRegionRangeStop));
// qDebug() << "xaxis:" << xAxis->range().lower << "-" <<
// xAxis->range().upper
//<< "yaxis:" << yAxis->range().lower << "-" << yAxis->range().upper;
updateContextXandYAxisRanges();
updateAxesRangeHistory();
emit plotRangesChangedSignal((QMouseEvent *)nullptr, m_context);
replot();
}
void
BasePlotWidget::axisZoom()
{
// Use the m_context.m_xRegionRangeStart/End values, but we need to sort the
// values before using them, because now we want to really have the lower x
// value. Simply craft a QCPRange that will swap the values if lower is not
// < than upper QCustomPlot calls this normalization).
xAxis->setRange(QCPRange(m_context.m_xRegionRangeStart, m_context.m_xRegionRangeStop));
yAxis->setRange(QCPRange(m_context.m_yRegionRangeStart, m_context.m_yRegionRangeStop));
updateContextXandYAxisRanges();
updateAxesRangeHistory();
emit plotRangesChangedSignal((QMouseEvent *)nullptr, m_context);
replot();
}
void
BasePlotWidget::axisPan()
{
// Sanity check
if(!m_context.m_wasClickOnXAxis && !m_context.m_wasClickOnYAxis)
qFatal(
"This function can only be called if the mouse click was on one of the "
"axes");
if(m_context.m_wasClickOnXAxis)
{
xAxis->setRange(m_context.m_xRange.lower - m_context.m_xDelta,
m_context.m_xRange.upper - m_context.m_xDelta);
}
if(m_context.m_wasClickOnYAxis)
{
yAxis->setRange(m_context.m_yRange.lower - m_context.m_yDelta,
m_context.m_yRange.upper - m_context.m_yDelta);
}
updateContextXandYAxisRanges();
// qDebug() << "The updated context:" << m_context.toString();
// We cannot store the new ranges in the history, because the pan operation
// involved a huge quantity of micro-movements elicited upon each mouse move
// cursor event so we would have a huge history.
// updateAxesRangeHistory();
// Now that the context has the right range values, we can emit the
// signal that will be used by this plot widget users, typically to
// abide by the x/y range lock required by the user.
emit plotRangesChangedSignal((QMouseEvent *)nullptr, m_context);
replot();
}
void
BasePlotWidget::replotWithAxesRanges(QCPRange xAxisRange,
QCPRange yAxisRange,
Enums::Axis axis)
{
// qDebug() << "With axis:" << (int)axis;
if(static_cast<int>(axis) & static_cast<int>(Enums::Axis::x))
{
xAxis->setRange(xAxisRange.lower, xAxisRange.upper);
}
if(static_cast<int>(axis) & static_cast<int>(Enums::Axis::y))
{
yAxis->setRange(yAxisRange.lower, yAxisRange.upper);
}
// We do not want to update the history, because there would be way too
// much history items, since this function is called upon mouse moving
// handling and not only during mouse release events.
// updateAxesRangeHistory();
replot();
}
void
BasePlotWidget::replotWithAxisRangeX(double lower, double upper)
{
// qDebug();
xAxis->setRange(lower, upper);
replot();
}
void
BasePlotWidget::replotWithAxisRangeY(double lower, double upper)
{
// qDebug();
yAxis->setRange(lower, upper);
replot();
}
/// PLOTTING / REPLOTTING functions
/// PLOT ITEMS : TRACER TEXT ITEMS...
//! Hide the selection line, the xDelta text and the zoom rectangle items.
void
BasePlotWidget::hideAllPlotItems()
{
mp_xDeltaTextItem->setVisible(false);
mp_yDeltaTextItem->setVisible(false);
// mp_zoomRectItem->setVisible(false);
hideSelectionRectangle();
// Force a replot to make sure the action is immediately visible by the
// user, even without moving the mouse.
replot();
}
//! Show the traces (vertical and horizontal).
void
BasePlotWidget::showTracers()
{
m_shouldTracersBeVisible = true;
mp_vPosTracerItem->setVisible(true);
mp_hPosTracerItem->setVisible(true);
mp_vStartTracerItem->setVisible(true);
mp_vEndTracerItem->setVisible(true);
// Force a replot to make sure the action is immediately visible by the
// user, even without moving the mouse.
replot();
}
//! Hide the traces (vertical and horizontal).
void
BasePlotWidget::hideTracers()
{
m_shouldTracersBeVisible = false;
mp_hPosTracerItem->setVisible(false);
mp_vPosTracerItem->setVisible(false);
mp_vStartTracerItem->setVisible(false);
mp_vEndTracerItem->setVisible(false);
// Force a replot to make sure the action is immediately visible by the
// user, even without moving the mouse.
replot();
}
void
BasePlotWidget::drawSelectionRectangleAndPrepareZoom(bool as_line_segment,
bool for_integration)
{
// The user has dragged the mouse left button on the graph, which means he
// is willing to draw a selection rectangle, either for zooming-in or for
// integration.
if(mp_xDeltaTextItem != nullptr)
mp_xDeltaTextItem->setVisible(false);
if(mp_yDeltaTextItem != nullptr)
mp_yDeltaTextItem->setVisible(false);
// Ensure the right selection rectangle is drawn.
updateIntegrationScopeDrawing(as_line_segment, for_integration);
// Note that if we draw a zoom rectangle, then we are certainly not
// measuring anything. So set the boolean value to false so that the user of
// this widget or derived classes know that there is nothing to perform upon
// (like deconvolution, for example).
m_context.m_isMeasuringDistance = false;
// Also remove the delta value from the pipeline by sending a simple
// distance without measurement signal.
emit xAxisMeasurementSignal(m_context, false);
replot();
}
void
BasePlotWidget::drawXScopeSpanFeatures()
{
// Depending on the kind of integration scope, we will have to display
// differently calculated values. We want to provide the user with
// the horizontal span of the integration scope. There are different
// situations.
// 1. The scope is mono-dimensional across the x axis: the span
// is thus simply the width.
// 2. The scope is bi-dimensional and is a rectangle: the span is
// thus simply the width.
// 3. The socpe is bi-dimensional and is a rhomboid: the span is
// the width.
// In the first and second cases above, the width is equal to the
// m_context.m_xDelta.
// In the case of the rhomboid, the span is not m_context.m_xDelta,
// it is more than that if the rhomboid is horizontal because it is
// the m_context.m_xDelta plus the rhomboid's horizontal size.
// FIXME: is this still true?
//
// We do not want to show the position markers because the only horiontal
// line to be visible must be contained between the start and end vertical
// tracer items.
mp_hPosTracerItem->setVisible(false);
mp_vPosTracerItem->setVisible(false);
// We want to draw the text in the middle position of the leftmost-rightmost
// point, even with rhomboid scopes.
QPointF leftmost_point;
if(!m_context.msp_integrationScope->getLeftMostPoint(leftmost_point))
qFatal("Could not get the left-most point.");
double width;
if(!m_context.msp_integrationScope->getWidth(width))
qFatal("Could not get width.");
// qDebug() << "width:" << width;
double x_axis_center_position = leftmost_point.x() + width / 2;
// We want the text to print inside the rectangle, always at the current
// drag point so the eye can follow the delta value while looking where to
// drag the mouse. To position the text inside the rectangle, we need to
// know what is the drag direction.
// What is the distance between the rectangle line at current drag point and
// the text itself. Think of this as a margin distance between the
// point of interest and the actual position of the text.
int pixels_away_from_line = 15;
QPointF reference_point_for_y_axis_label_position;
// ATTENTION: the pixel coordinates for the vertical direction go in reverse
// order with respect to the y axis values !!! That is, pixel(0,0) is top
// left of the graph.
if(static_cast<int>(m_context.m_dragDirections) &
static_cast<int>(DragDirections::BOTTOM_TO_TOP))
{
// We need to print outside the rectangle, that is pixels_away_from_line
// pixels to the top, so with pixel y value decremented of that
// pixels_above_line value (one would have expected to increment that
// value, along the y axis, but the coordinates in pixel go in reverse
// order).
pixels_away_from_line *= -1;
if(!m_context.msp_integrationScope->getTopMostPoint(
reference_point_for_y_axis_label_position))
qFatal("Failed to get top most point.");
}
else
{
if(!m_context.msp_integrationScope->getBottomMostPoint(
reference_point_for_y_axis_label_position))
qFatal("Failed to get bottom most point.");
}
// double y_axis_pixel_coordinate =
// yAxis->coordToPixel(m_context.m_currentDragPoint.y());
double y_axis_pixel_coordinate =
yAxis->coordToPixel(reference_point_for_y_axis_label_position.y());
// Now that we have the coordinate in pixel units, we can correct
// it by the value of the margin we want to give.
double y_axis_modified_pixel_coordinate =
y_axis_pixel_coordinate + pixels_away_from_line;
// Set aside a point instance to store the pixel coordinates of the text.
QPointF pixel_coordinates;
pixel_coordinates.setX(x_axis_center_position);
pixel_coordinates.setY(y_axis_modified_pixel_coordinate);
// Now convert back to graph coordinates.
QPointF graph_coordinates(xAxis->pixelToCoord(pixel_coordinates.x()),
yAxis->pixelToCoord(pixel_coordinates.y()));
// qDebug() << "Should print the label at point:" << graph_coordinates;
if(mp_xDeltaTextItem != nullptr)
{
mp_xDeltaTextItem->position->setCoords(x_axis_center_position,
graph_coordinates.y());
// Dynamically set the number of decimals to ensure we can read
// a meaning full delta value even if it is very very very small.
// That is, allow one to read 0.00333, 0.000333, 1.333 and so on.
// The computation below only works properly when the passed
// value is fabs() (not negative !!!).
int decimals = Utils::zeroDecimalsInValue(width) + 3;
QString label_text = QString("full x span %1 -- x drag delta %2")
.arg(width, 0, 'f', decimals)
.arg(fabs(m_context.m_xDelta), 0, 'f', decimals);
mp_xDeltaTextItem->setText(label_text);
mp_xDeltaTextItem->setFont(QFont(font().family(), 9));
mp_xDeltaTextItem->setVisible(true);
}
// Set the boolean to true so that derived widgets know that something is
// being measured, and they can act accordingly, for example by computing
// deconvolutions in a mass spectrum.
m_context.m_isMeasuringDistance = true;
replot();
// Let the caller know that we were measuring something.
emit xAxisMeasurementSignal(m_context, true);
return;
}
void
BasePlotWidget::drawYScopeSpanFeatures()
{
// See drawXScopeSpanFeatures() for explanations.
// Check right away if there is height!
double height;
if(!m_context.msp_integrationScope->getHeight(height))
qFatal("Could not get height.");
// If there is no height, we have nothing to do here.
if(!height)
return;
// qDebug() << "height:" << height;
// FIXME: is this still true?
//
// We do not want to show the position markers because the only horiontal
// line to be visible must be contained between the start and end vertical
// tracer items.
mp_hPosTracerItem->setVisible(false);
mp_vPosTracerItem->setVisible(false);
// First the easy part: the vertical position: centered on the
// scope Y span.
QPointF bottom_most_point;
if(!m_context.msp_integrationScope->getBottomMostPoint(bottom_most_point))
qFatal("Could not get the bottom-most bottom point.");
double y_axis_center_position = bottom_most_point.y() + height / 2;
// We want to draw the text outside the rectangle (if normal rectangle)
// at a small distance from the vertical limit of the scope at the
// position of the current drag point. We need to check the horizontal
// drag direction to put the text at the right place (left of
// current drag point if dragging right to left, for example).
// What is the distance between the rectangle line at current drag point and
// the text itself.
int pixels_away_from_line = 15;
double x_axis_coordinate;
double x_axis_pixel_coordinate;
if(static_cast<int>(m_context.m_dragDirections) &
static_cast<int>(DragDirections::RIGHT_TO_LEFT))
{
QPointF left_most_point;
if(!m_context.msp_integrationScope->getLeftMostPoint(left_most_point))
qFatal("Failed to get left most point.");
x_axis_coordinate = left_most_point.x();
pixels_away_from_line *= -1;
}
else
{
QPointF right_most_point;
if(!m_context.msp_integrationScope->getRightMostPoint(right_most_point))
qFatal("Failed to get right most point.");
x_axis_coordinate = right_most_point.x();
}
x_axis_pixel_coordinate = xAxis->coordToPixel(x_axis_coordinate);
double x_axis_modified_pixel_coordinate =
x_axis_pixel_coordinate + pixels_away_from_line;
// Set aside a point instance to store the pixel coordinates of the text.
QPointF pixel_coordinates;
pixel_coordinates.setX(x_axis_modified_pixel_coordinate);
pixel_coordinates.setY(y_axis_center_position);
// Now convert back to graph coordinates.
QPointF graph_coordinates(xAxis->pixelToCoord(pixel_coordinates.x()),
yAxis->pixelToCoord(pixel_coordinates.y()));
mp_yDeltaTextItem->position->setCoords(graph_coordinates.x(),
y_axis_center_position);
int decimals = Utils::zeroDecimalsInValue(height) + 3;
QString label_text = QString("full y span %1 -- y drag delta %2")
.arg(height, 0, 'f', decimals)
.arg(fabs(m_context.m_yDelta), 0, 'f', decimals);
mp_yDeltaTextItem->setText(label_text);
mp_yDeltaTextItem->setFont(QFont(font().family(), 9));
mp_yDeltaTextItem->setVisible(true);
mp_yDeltaTextItem->setRotation(90);
// Set the boolean to true so that derived widgets know that something is
// being measured, and they can act accordingly, for example by computing
// deconvolutions in a mass spectrum.
m_context.m_isMeasuringDistance = true;
replot();
// Let the caller know that we were measuring something.
emit xAxisMeasurementSignal(m_context, true);
}
void
BasePlotWidget::calculateDragDeltas()
{
// We compute signed differentials. If the user does not want the sign,
// fabs(double) is their friend.
// Compute the xAxis differential:
m_context.m_xDelta =
m_context.m_currentDragPoint.x() - m_context.m_startDragPoint.x();
// Same with the Y-axis range:
m_context.m_yDelta =
m_context.m_currentDragPoint.y() - m_context.m_startDragPoint.y();
return;
}
bool
BasePlotWidget::isVerticalDisplacementAboveThreshold()
{
// First get the height of the plot.
double plotHeight = yAxis->range().upper - yAxis->range().lower;
double heightDiff =
fabs(m_context.m_startDragPoint.y() - m_context.m_currentDragPoint.y());
double heightDiffRatio = (heightDiff / plotHeight) * 100;
if(heightDiffRatio > 10)
{
return true;
}
return false;
}
void
BasePlotWidget::updateIntegrationScope(bool for_integration)
{
// if(for_integration)
// qDebug() << "for_integration:" << for_integration;
// By essence, the one-dimension IntegrationScope is characterized
// by the left-most point and the width. Using these two data bits
// it is possible to compute the x value of the right-most point.
double x_range_start =
std::min(m_context.m_currentDragPoint.x(), m_context.m_startDragPoint.x());
double x_range_end =
std::max(m_context.m_currentDragPoint.x(), m_context.m_startDragPoint.x());
// qDebug() << "x_range_start:" << x_range_start << "-" << "x_range_end:" <<
// x_range_end;
double y_position = m_context.m_startDragPoint.y();
m_context.updateIntegrationScope();
// Top line
mp_selectionRectangeLine1->start->setCoords(
QPointF(x_range_start, y_position));
mp_selectionRectangeLine1->end->setCoords(QPointF(x_range_end, y_position));
// Only if we are drawing a selection rectangle for integration, do we set
// arrow heads to the line.
if(for_integration)
{
mp_selectionRectangeLine1->setHead(QCPLineEnding::esSpikeArrow);
mp_selectionRectangeLine1->setTail(QCPLineEnding::esSpikeArrow);
}
else
{
mp_selectionRectangeLine1->setHead(QCPLineEnding::esNone);
mp_selectionRectangeLine1->setTail(QCPLineEnding::esNone);
}
mp_selectionRectangeLine1->setVisible(true);
// Right line: does not exist, start and end are the same end point of the
// top line.
mp_selectionRectangeLine2->start->setCoords(QPointF(x_range_end, y_position));
mp_selectionRectangeLine2->end->setCoords(QPointF(x_range_end, y_position));
mp_selectionRectangeLine2->setVisible(false);
// Bottom line: identical to the top line, but invisible
mp_selectionRectangeLine3->start->setCoords(
QPointF(x_range_start, y_position));
mp_selectionRectangeLine3->end->setCoords(QPointF(x_range_end, y_position));
mp_selectionRectangeLine3->setVisible(false);
// Left line: does not exist: start and end are the same end point of the
// top line.
mp_selectionRectangeLine4->start->setCoords(QPointF(x_range_end, y_position));
mp_selectionRectangeLine4->end->setCoords(QPointF(x_range_end, y_position));
mp_selectionRectangeLine4->setVisible(false);
}
void
BasePlotWidget::updateIntegrationScopeRect(bool for_integration)
{
// qDebug();
// if(for_integration)
// qDebug() << "for_integration:" << for_integration;
// We are handling a conventional rectangle. Just create four points
// from top left to bottom right. But we want the top left point to be
// effectively the top left point and the bottom point to be the bottom
// point. So we need to try all four direction combinations, left to right
// or converse versus top to bottom or converse.
m_context.updateIntegrationScopeRect();
// Now that the integration scope has been updated as a rectangle,
// use these newly set data to actually draw the integration
// scope lines.
QPointF bottom_left_point;
if(!m_context.msp_integrationScope->getPoint(bottom_left_point))
qFatal("Failed to get point.");
// qDebug() << "Starting point is left bottom point:" << bottom_left_point;
double width;
if(!m_context.msp_integrationScope->getWidth(width))
qFatal("Failed to get width.");
// qDebug() << "Width:" << width;
double height;
if(!m_context.msp_integrationScope->getHeight(height))
qFatal("Failed to get height.");
// qDebug() << "Height:" << height;
QPointF bottom_right_point(bottom_left_point.x() + width,
bottom_left_point.y());
// qDebug() << "bottom_right_point:" << bottom_right_point;
QPointF top_right_point(bottom_left_point.x() + width,
bottom_left_point.y() + height);
// qDebug() << "top_right_point:" << top_right_point;
QPointF top_left_point(bottom_left_point.x(), bottom_left_point.y() + height);
// qDebug() << "top_left_point:" << top_left_point;
// Start by drawing the bottom line because the IntegrationScopeRect has the
// left bottom point and the width and the height to fully characterize it.
// Bottom line (left to right)
mp_selectionRectangeLine3->start->setCoords(bottom_left_point);
mp_selectionRectangeLine3->end->setCoords(bottom_right_point);
mp_selectionRectangeLine3->setVisible(true);
// Right line (bottom to top)
mp_selectionRectangeLine2->start->setCoords(bottom_right_point);
mp_selectionRectangeLine2->end->setCoords(top_right_point);
mp_selectionRectangeLine2->setVisible(true);
// Top line (right to left)
mp_selectionRectangeLine1->start->setCoords(top_right_point);
mp_selectionRectangeLine1->end->setCoords(top_left_point);
mp_selectionRectangeLine1->setVisible(true);
// Left line (top to bottom)
mp_selectionRectangeLine4->start->setCoords(top_left_point);
mp_selectionRectangeLine4->end->setCoords(bottom_left_point);
mp_selectionRectangeLine4->setVisible(true);
// Only if we are drawing a selection rectangle for integration, do we
// set arrow heads to the line.
if(for_integration)
{
mp_selectionRectangeLine1->setHead(QCPLineEnding::esSpikeArrow);
mp_selectionRectangeLine1->setTail(QCPLineEnding::esSpikeArrow);
}
else
{
mp_selectionRectangeLine1->setHead(QCPLineEnding::esNone);
mp_selectionRectangeLine1->setTail(QCPLineEnding::esNone);
}
}
void
BasePlotWidget::updateIntegrationScopeHorizontalRhomb(bool for_integration)
{
// We are handling a rhomboid scope, that is, a rectangle that
// is tilted either to the left or to the right.
// There are two kinds of rhomboid integration scopes: horizontal and
// vertical.
/*
* +----------+
* | |
* | |
* | |
* | |
* | |
* | |
* | |
* +----------+
* ----width---
*/
// As visible here, the fixed size of the rhomboid (using the S key in the
// plot widget) is the *horizontal* side (this is the plot context's
// m_integrationScopeRhombWidth).
IntegrationScopeFeatures scope_features;
// Top horizontal line
QPointF point_1;
scope_features = m_context.msp_integrationScope->getLeftMostTopPoint(point_1);
// When the user rotates the horizontal rhomboid, at some point, if the
// current drag point has the same y axis value as the start drag point, then
// we say that the rhomboid is flattened on the x axis. In this case, we do
// not draw anything as this is a purely unusable situation.
if(scope_features & IntegrationScopeFeatures::FLAT_ON_X_AXIS)
{
// qDebug() << "The horizontal rhomboid is flattened on the x axis.";
mp_selectionRectangeLine1->setVisible(false);
mp_selectionRectangeLine2->setVisible(false);
mp_selectionRectangeLine3->setVisible(false);
mp_selectionRectangeLine4->setVisible(false);
return;
}
if(scope_features & IntegrationScopeFeatures::RHOMBOID_VERTICAL)
qFatal("The rhomboid should be horizontal!");
// At this point we can draw the rhomboid fine.
if(!m_context.msp_integrationScope->getLeftMostTopPoint(point_1))
qFatal("Failed to getLeftMostTopPoint.");
QPointF point_2;
if(!m_context.msp_integrationScope->getRightMostTopPoint(point_2))
qFatal("Failed to getRightMostTopPoint.");
// qDebug() << "For top line, two points:" << point_1 << "--" << point_2;
mp_selectionRectangeLine1->start->setCoords(point_1);
mp_selectionRectangeLine1->end->setCoords(point_2);
// Only if we are drawing a selection rectangle for integration, do we set
// arrow heads to the line.
if(for_integration)
{
mp_selectionRectangeLine1->setHead(QCPLineEnding::esSpikeArrow);
mp_selectionRectangeLine1->setTail(QCPLineEnding::esSpikeArrow);
}
else
{
mp_selectionRectangeLine1->setHead(QCPLineEnding::esNone);
mp_selectionRectangeLine1->setTail(QCPLineEnding::esNone);
}
mp_selectionRectangeLine1->setVisible(true);
// Right line
if(!m_context.msp_integrationScope->getRightMostBottomPoint(point_1))
qFatal("Failed to getRightMostBottomPoint.");
mp_selectionRectangeLine2->start->setCoords(point_2);
mp_selectionRectangeLine2->end->setCoords(point_1);
mp_selectionRectangeLine2->setVisible(true);
// qDebug() << "For right line, two points:" << point_2 << "--" << point_1;
// Bottom horizontal line
if(!m_context.msp_integrationScope->getLeftMostBottomPoint(point_2))
qFatal("Failed to getLeftMostBottomPoint.");
mp_selectionRectangeLine3->start->setCoords(point_1);
mp_selectionRectangeLine3->end->setCoords(point_2);
mp_selectionRectangeLine3->setVisible(true);
// qDebug() << "For bottom line, two points:" << point_1 << "--" << point_2;
// Left line
if(!m_context.msp_integrationScope->getLeftMostTopPoint(point_1))
qFatal("Failed to getLeftMostTopPoint.");
mp_selectionRectangeLine4->end->setCoords(point_2);
mp_selectionRectangeLine4->start->setCoords(point_1);
mp_selectionRectangeLine4->setVisible(true);
// qDebug() << "For left line, two points:" << point_2 << "--" << point_1;
}
void
BasePlotWidget::updateIntegrationScopeVerticalRhomb(bool for_integration)
{
// We are handling a rhomboid scope, that is, a rectangle that
// is tilted either to the left or to the right.
// There are two kinds of rhomboid integration scopes: horizontal and
// vertical.
/*
* +3
* . |
* . |
* . |
* . +2
* . .
* . .
* . .
* 4+ .
* | | .
* height | | .
* | | .
* 1+
*
*/
// As visible here, the fixed size of the rhomboid (using the S key in the
// plot widget) is the *vertical* side (this is the plot context's
// m_integrationScopeRhombHeight).
IntegrationScopeFeatures scope_features;
// Left vertical line
QPointF point_1;
scope_features = m_context.msp_integrationScope->getLeftMostTopPoint(point_1);
// When the user rotates the vertical rhomboid, at some point, if the current
// drag point is on the same x axis value as the start drag point, then we say
// that the rhomboid is flattened on the y axis. In this case, we do not draw
// anything as this is a purely unusable situation.
if(scope_features & IntegrationScopeFeatures::FLAT_ON_Y_AXIS)
{
// qDebug() << "The vertical rhomboid is flattened on the y axis.";
mp_selectionRectangeLine1->setVisible(false);
mp_selectionRectangeLine2->setVisible(false);
mp_selectionRectangeLine3->setVisible(false);
mp_selectionRectangeLine4->setVisible(false);
return;
}
if(scope_features & IntegrationScopeFeatures::RHOMBOID_HORIZONTAL)
qFatal("The rhomboid should be vertical!");
// At this point we can draw the rhomboid fine.
QPointF point_2;
if(!m_context.msp_integrationScope->getLeftMostBottomPoint(point_2))
qFatal("Failed to getLeftMostBottomPoint.");
// qDebug() << "For left vertical line, two points:" << point_1 << "--"
// << point_2;
mp_selectionRectangeLine1->start->setCoords(point_1);
mp_selectionRectangeLine1->end->setCoords(point_2);
// Only if we are drawing a selection rectangle for integration, do we set
// arrow heads to the line.
if(for_integration)
{
mp_selectionRectangeLine1->setHead(QCPLineEnding::esSpikeArrow);
mp_selectionRectangeLine1->setTail(QCPLineEnding::esSpikeArrow);
}
else
{
mp_selectionRectangeLine1->setHead(QCPLineEnding::esNone);
mp_selectionRectangeLine1->setTail(QCPLineEnding::esNone);
}
mp_selectionRectangeLine1->setVisible(true);
// Lower oblique line
if(!m_context.msp_integrationScope->getRightMostBottomPoint(point_1))
qFatal("Failed to getRightMostBottomPoint.");
mp_selectionRectangeLine2->start->setCoords(point_2);
mp_selectionRectangeLine2->end->setCoords(point_1);
mp_selectionRectangeLine2->setVisible(true);
// qDebug() << "For lower oblique line, two points:" << point_2 << "--"
// << point_1;
// Right vertical line
if(!m_context.msp_integrationScope->getRightMostTopPoint(point_2))
qFatal("Failed to getRightMostTopPoint.");
mp_selectionRectangeLine3->start->setCoords(point_1);
mp_selectionRectangeLine3->end->setCoords(point_2);
mp_selectionRectangeLine3->setVisible(true);
// qDebug() << "For right vertical line, two points:" << point_1 << "--"
// << point_2;
// Upper oblique line
if(!m_context.msp_integrationScope->getLeftMostTopPoint(point_1))
qFatal("Failed to get the LeftMostTopPoint.");
mp_selectionRectangeLine4->end->setCoords(point_2);
mp_selectionRectangeLine4->start->setCoords(point_1);
mp_selectionRectangeLine4->setVisible(true);
// qDebug() << "For upper oblique line, two points:" << point_2 << "--"
// << point_1;
}
void
BasePlotWidget::updateIntegrationScopeRhomb(bool for_integration)
{
// qDebug();
// if(for_integration)
// qDebug() << "for_integration:" << for_integration;
// We are handling a skewed rectangle (rhomboid), that is a rectangle that
// is tilted either to the left or to the right.
// There are two kinds of rhomboid integration scopes:
/*
4+----------+3
| |
| |
| |
| |
| |
| |
| |
1+----------+2
----width---
*/
// As visible here, the fixed size of the rhomboid (using the S key in the
// plot widget) is the *horizontal* side (this is the plot context's
// m_integrationScopeRhombWidth).
// and
/*
* +3
* . |
* . |
* . |
* . +2
* . .
* . .
* . .
* 4+ .
* | | .
* height | | .
* | | .
* 1+
*
*/
// As visible here, the fixed size of the rhomboid (using the S key in the
// plot widget) is the *vertical* side (this is the plot context's
// m_integrationScopeRhombHeight).
// qDebug() << "Before calling updateIntegrationScopeRhomb(), "
// "m_integrationScopeRhombWidth:"
// << m_context.m_integrationScopeRhombWidth
// << "and m_integrationScopeRhombHeight:"
// << m_context.m_integrationScopeRhombHeight;
m_context.updateIntegrationScopeRhomb();
// qDebug() << "After, m_integrationScopeRhombWidth:"
// << m_context.m_integrationScopeRhombWidth
// << "and m_integrationScopeRhombHeight:"
// << m_context.m_integrationScopeRhombHeight;
// Now that the integration scope has been updated as a rhomboid,
// use these newly set data to actually draw the integration
// scope lines.
// We thus need to first establish if we have a horiontal or a vertical
// rhomboid scope. This information is located in
// m_context.m_integrationScopeRhombWidth and
// m_context.m_integrationScopeRhombHeight. If width > 0, height *has to be
// 0*, which indicates a horizontal rhomb.Conversely, if height is > 0, then
// the rhomb is vertical.
if(m_context.m_integrationScopeRhombWidth > 0)
// We are dealing with a horizontal scope.
updateIntegrationScopeHorizontalRhomb(for_integration);
else if(m_context.m_integrationScopeRhombHeight > 0)
// We are dealing with a vertical scope.
updateIntegrationScopeVerticalRhomb(for_integration);
else
qFatal("Cannot be both the width or height of rhomboid scope be 0.");
}
void
BasePlotWidget::updateIntegrationScopeDrawing(bool as_line_segment,
bool for_integration)
{
// qDebug() << "as_line_segment:" << as_line_segment;
// qDebug() << "for_integration:" << for_integration;
// We now need to construct the selection rectangle, either for zoom or for
// integration.
// There are two situations :
//
// 1. if the rectangle should look like a line segment
//
// 2. if the rectangle should actually look like a rectangle. In this case,
// there are two sub-situations:
//
// a. if the Alt modifier key is down, then the rectangle is rhomboid.
//
// b. otherwise the rectangle is conventional.
if(as_line_segment)
{
// qDebug() << "Updating the integration scope to an IntegrationScope.";
updateIntegrationScope(for_integration);
}
else
{
if(!(m_context.m_keyboardModifiers & Qt::AltModifier))
{
// qDebug()
// << "Updating the integration scope to an IntegrationScopeRect.";
updateIntegrationScopeRect(for_integration);
}
else if(m_context.m_keyboardModifiers & Qt::AltModifier)
{
// The user might use the Alt modifier, but if no rhomboid side has
// been defined using the S key, then we do not do any rhomboid
// selection because we do not know the side size of that rhomboid.
if(!m_context.m_integrationScopeRhombHeight &&
!m_context.m_integrationScopeRhombWidth)
updateIntegrationScopeRect(for_integration);
else
// qDebug()
// << "Updating the integration scope to an
// IntegrationScopeRhomb.";
updateIntegrationScopeRhomb(for_integration);
}
}
// Depending on the kind of IntegrationScope, (normal, rect or rhomb)
// we have to measure things in different ways. We now set in the context
// a number of parameters that will be used by its user.
QPointF point;
double height;
std::vector<QPointF> points;
// Integration scope values are sorted:
// Line scope: point is left and width is right.x - left.x
// Rect scope: point is bottom left.
// Rhomb scope: points 1->4 are bottom left->bottom right->top right->top left
// width is 2.x - 1.x.
if(m_context.msp_integrationScope->getPoints(points))
{
// We have defined a IntegrationScopeRhomb.
if(!m_context.msp_integrationScope->getLeftMostPoint(point))
qFatal("Failed to get LeftMost point.");
m_context.m_xRegionRangeStart = point.x();
if(!m_context.msp_integrationScope->getRightMostPoint(point))
qFatal("Failed to get RightMost point.");
m_context.m_xRegionRangeStop = point.x();
}
else if(m_context.msp_integrationScope->getHeight(height))
{
// We have defined a IntegrationScopeRect.
if(!m_context.msp_integrationScope->getPoint(point))
qFatal("Failed to get point.");
m_context.m_xRegionRangeStart = point.x();
double width;
if(!m_context.msp_integrationScope->getWidth(width))
qFatal("Failed to get width.");
m_context.m_xRegionRangeStop = m_context.m_xRegionRangeStart + width;
m_context.m_yRegionRangeStart = point.y();
m_context.m_yRegionRangeStop = point.y() + height;
}
else
{
// We have defined a IntegrationScope.
if(!m_context.msp_integrationScope->getPoint(point))
qFatal("Failed to get point.");
m_context.m_xRegionRangeStart = point.x();
double width;
if(!m_context.msp_integrationScope->getWidth(width))
qFatal("Failed to get width.");
m_context.m_xRegionRangeStop = m_context.m_xRegionRangeStart + width;
}
// At this point, draw the text describing the widths.
// We want the x-delta on the bottom of the rectangle, inside it
// and the y-delta on the vertical side of the rectangle, inside it.
// Draw the selection width text
drawXScopeSpanFeatures();
}
void
BasePlotWidget::hideSelectionRectangle(bool reset_values)
{
mp_selectionRectangeLine1->setVisible(false);
mp_selectionRectangeLine2->setVisible(false);
mp_selectionRectangeLine3->setVisible(false);
mp_selectionRectangeLine4->setVisible(false);
if(reset_values)
{
resetSelectionRectangle();
}
}
void
BasePlotWidget::resetSelectionRectangle()
{
std::const_pointer_cast<IntegrationScopeBase>(m_context.msp_integrationScope)
->reset();
}
SelectionDrawingLines
BasePlotWidget::whatIsVisibleOfTheSelectionRectangle()
{
// There are four lines that make the selection polygon. We want to know
// which lines are visible.
int current_selection_polygon =
static_cast<int>(SelectionDrawingLines::NOT_SET);
if(mp_selectionRectangeLine1->visible())
{
current_selection_polygon |=
static_cast<int>(SelectionDrawingLines::TOP_LINE);
// qDebug() << "current_selection_polygon:" <<
// current_selection_polygon;
}
if(mp_selectionRectangeLine2->visible())
{
current_selection_polygon |=
static_cast<int>(SelectionDrawingLines::RIGHT_LINE);
// qDebug() << "current_selection_polygon:" <<
// current_selection_polygon;
}
if(mp_selectionRectangeLine3->visible())
{
current_selection_polygon |=
static_cast<int>(SelectionDrawingLines::BOTTOM_LINE);
// qDebug() << "current_selection_polygon:" <<
// current_selection_polygon;
}
if(mp_selectionRectangeLine4->visible())
{
current_selection_polygon |=
static_cast<int>(SelectionDrawingLines::LEFT_LINE);
// qDebug() << "current_selection_polygon:" <<
// current_selection_polygon;
}
// qDebug() << "returning visibility:" << current_selection_polygon;
return static_cast<SelectionDrawingLines>(current_selection_polygon);
}
bool
BasePlotWidget::isSelectionRectangleVisible()
{
// Sanity check
int check = 0;
check += mp_selectionRectangeLine1->visible();
check += mp_selectionRectangeLine2->visible();
check += mp_selectionRectangeLine3->visible();
check += mp_selectionRectangeLine4->visible();
if(check > 0)
return true;
return false;
}
void
BasePlotWidget::setFocus()
{
// qDebug() << "Setting focus to the QCustomPlot:" << this;
QCustomPlot::setFocus();
// qDebug() << "Emitting setFocusSignal().";
emit setFocusSignal();
}
//! Redraw the background of the \p focusedPlotWidget plot widget.
void
BasePlotWidget::redrawPlotBackground(QWidget *focusedPlotWidget)
{
if(focusedPlotWidget == nullptr)
throw ExceptionNotPossible(
"baseplotwidget.cpp @ redrawPlotBackground(QWidget *focusedPlotWidget "
"-- "
"ERROR focusedPlotWidget cannot be nullptr.");
if(dynamic_cast<QWidget *>(this) != focusedPlotWidget)
{
// The focused widget is not *this widget. We should make sure that
// we were not the one that had the focus, because in this case we
// need to redraw an unfocused background.
axisRect()->setBackground(m_unfocusedBrush);
}
else
{
axisRect()->setBackground(m_focusedBrush);
}
replot();
}
void
BasePlotWidget::updateContextXandYAxisRanges()
{
m_context.m_xRange = QCPRange(xAxis->range().lower, xAxis->range().upper);
m_context.m_yRange = QCPRange(yAxis->range().lower, yAxis->range().upper);
// qDebug() << "The new updated context: " << m_context.toString();
}
const BasePlotContext &
BasePlotWidget::getContext() const
{
return m_context;
}
} // namespace pappso
basePlotContextPtrMetaTypeId
int basePlotContextPtrMetaTypeId
Definition baseplotwidget.cpp:44
basePlotContextMetaTypeId
int basePlotContextMetaTypeId
Definition baseplotwidget.cpp:42
baseplotwidget.h
pappso::BasePlotContext
Definition baseplotcontext.h:51
pappso::BasePlotWidget::axisPan
virtual void axisPan()
Definition baseplotwidget.cpp:2164
pappso::BasePlotWidget::updateIntegrationScopeRect
virtual void updateIntegrationScopeRect(bool for_integration=false)
Definition baseplotwidget.cpp:2680
pappso::BasePlotWidget::m_mouseMoveHandlerSkipAmount
int m_mouseMoveHandlerSkipAmount
How many mouse move events must be skipped *‍/.
Definition baseplotwidget.h:343
pappso::BasePlotWidget::m_lastAxisRangeHistoryIndex
std::size_t m_lastAxisRangeHistoryIndex
Index of the last axis range history item.
Definition baseplotwidget.h:325
pappso::BasePlotWidget::replotWithAxesRanges
virtual void replotWithAxesRanges(QCPRange xAxisRange, QCPRange yAxisRange, Enums::Axis axis)
Definition baseplotwidget.cpp:2203
pappso::BasePlotWidget::updateAxesRangeHistory
virtual void updateAxesRangeHistory()
Create new axis range history items and append them to the history.
Definition baseplotwidget.cpp:501
pappso::BasePlotWidget::mouseWheelHandler
virtual void mouseWheelHandler(QWheelEvent *event)
Definition baseplotwidget.cpp:1678
pappso::BasePlotWidget::plottableDestructionRequestedSignal
void plottableDestructionRequestedSignal(BasePlotWidget *base_plot_widget_p, QCPAbstractPlottable *plottable_p, const pappso::BasePlotContext &context)
pappso::BasePlotWidget::m_shouldTracersBeVisible
bool m_shouldTracersBeVisible
Tells if the tracers should be visible.
Definition baseplotwidget.h:302
pappso::BasePlotWidget::hideSelectionRectangle
virtual void hideSelectionRectangle(bool reset_values=false)
Definition baseplotwidget.cpp:3194
pappso::BasePlotWidget::updateIntegrationScopeDrawing
virtual void updateIntegrationScopeDrawing(bool as_line_segment=false, bool for_integration=false)
Definition baseplotwidget.cpp:3074
pappso::BasePlotWidget::directionKeyReleaseEvent
virtual void directionKeyReleaseEvent(QKeyEvent *event)
Definition baseplotwidget.cpp:864
pappso::BasePlotWidget::mp_yDeltaTextItem
QCPItemText * mp_yDeltaTextItem
Definition baseplotwidget.h:299
pappso::BasePlotWidget::mousePressEventSignal
void mousePressEventSignal(QMouseEvent *event, const pappso::BasePlotContext &context)
pappso::BasePlotWidget::mp_selectionRectangeLine1
QCPItemLine * mp_selectionRectangeLine1
Rectangle defining the borders of zoomed-in/out data.
Definition baseplotwidget.h:292
pappso::BasePlotWidget::getOutermostRangeX
virtual QCPRange getOutermostRangeX(bool &found_range) const
Definition baseplotwidget.cpp:2032
pappso::BasePlotWidget::lastCursorHoveredPointSignal
void lastCursorHoveredPointSignal(const QPointF &pointf)
pappso::BasePlotWidget::getContext
virtual const BasePlotContext & getContext() const
Definition baseplotwidget.cpp:3322
pappso::BasePlotWidget::drawSelectionRectangleAndPrepareZoom
virtual void drawSelectionRectangleAndPrepareZoom(bool as_line_segment=false, bool for_integration=false)
Definition baseplotwidget.cpp:2301
pappso::BasePlotWidget::getRangeY
virtual QCPRange getRangeY(bool &found_range, int index) const
Definition baseplotwidget.cpp:1923
pappso::BasePlotWidget::keyPressEvent
virtual void keyPressEvent(QKeyEvent *event)
KEYBOARD-related EVENTS.
Definition baseplotwidget.cpp:607
pappso::BasePlotWidget::~BasePlotWidget
virtual ~BasePlotWidget()
Destruct this BasePlotWidget instance.
Definition baseplotwidget.cpp:160
pappso::BasePlotWidget::xAxisMeasurementSignal
void xAxisMeasurementSignal(const pappso::BasePlotContext &context, bool with_delta)
pappso::BasePlotWidget::updateIntegrationScope
virtual void updateIntegrationScope(bool for_integration=false)
Definition baseplotwidget.cpp:2619
pappso::BasePlotWidget::beforeReplotSignal
void beforeReplotSignal()
pappso::BasePlotWidget::axisZoom
virtual void axisZoom()
Definition baseplotwidget.cpp:2143
pappso::BasePlotWidget::mouseMoveHandlerLeftButtonDraggingCursor
virtual void mouseMoveHandlerLeftButtonDraggingCursor(QMouseEvent *event)
Definition baseplotwidget.cpp:1142
pappso::BasePlotWidget::mp_selectionRectangeLine2
QCPItemLine * mp_selectionRectangeLine2
Definition baseplotwidget.h:293
pappso::BasePlotWidget::plotRangesChangedSignal
void plotRangesChangedSignal(QMouseEvent *event, const pappso::BasePlotContext &context)
pappso::BasePlotWidget::mp_xDeltaTextItem
QCPItemText * mp_xDeltaTextItem
Text describing the x-axis delta value during a drag operation.
Definition baseplotwidget.h:298
pappso::BasePlotWidget::setAxisLabelX
virtual void setAxisLabelX(const QString &label)
Definition baseplotwidget.cpp:462
pappso::BasePlotWidget::m_mouseMoveHandlerSkipCount
int m_mouseMoveHandlerSkipCount
Counter to handle the "fat data" mouse move event handling.
Definition baseplotwidget.h:351
pappso::BasePlotWidget::getOutermostRangeY
virtual QCPRange getOutermostRangeY(bool &found_range) const
Definition baseplotwidget.cpp:2045
pappso::BasePlotWidget::mouseMoveHandlerRightButtonDraggingCursor
virtual void mouseMoveHandlerRightButtonDraggingCursor(QMouseEvent *event)
Definition baseplotwidget.cpp:1278
pappso::BasePlotWidget::dragDirection
int dragDirection()
MOUSE-related EVENTS.
Definition baseplotwidget.cpp:1801
pappso::BasePlotWidget::isClickOntoYAxis
bool isClickOntoYAxis(const QPointF &mousePoint)
Definition baseplotwidget.cpp:1771
pappso::BasePlotWidget::moveMouseCursorPixelCoordToGlobal
virtual void moveMouseCursorPixelCoordToGlobal(QPointF local_coordinates)
Definition baseplotwidget.cpp:1839
pappso::BasePlotWidget::mp_hPosTracerItem
QCPItemLine * mp_hPosTracerItem
Horizontal position tracer.
Definition baseplotwidget.h:305
pappso::BasePlotWidget::mp_vPosTracerItem
QCPItemLine * mp_vPosTracerItem
Vertical position tracer.
Definition baseplotwidget.h:308
pappso::BasePlotWidget::setupWidget
virtual bool setupWidget()
Definition baseplotwidget.cpp:336
pappso::BasePlotWidget::setPen
virtual void setPen(const QPen &pen)
Definition baseplotwidget.cpp:399
pappso::BasePlotWidget::mouseMoveHandlerDraggingCursor
virtual void mouseMoveHandlerDraggingCursor(QMouseEvent *event)
Definition baseplotwidget.cpp:1058
pappso::BasePlotWidget::getInnermostRangeX
virtual QCPRange getInnermostRangeX(bool &found_range) const
Definition baseplotwidget.cpp:2025
pappso::BasePlotWidget::redrawPlotBackground
virtual void redrawPlotBackground(QWidget *focusedPlotWidget)
Redraw the background of the focusedPlotWidget plot widget.
Definition baseplotwidget.cpp:3288
pappso::BasePlotWidget::mouseReleaseHandlerLeftButton
virtual void mouseReleaseHandlerLeftButton(QMouseEvent *event)
Definition baseplotwidget.cpp:1554
pappso::BasePlotWidget::mouseMoveHandlerNotDraggingCursor
virtual void mouseMoveHandlerNotDraggingCursor(QMouseEvent *event)
Definition baseplotwidget.cpp:994
pappso::BasePlotWidget::isClickOntoXAxis
bool isClickOntoXAxis(const QPointF &mousePoint)
Definition baseplotwidget.cpp:1746
pappso::BasePlotWidget::setAxisLabelY
virtual void setAxisLabelY(const QString &label)
Definition baseplotwidget.cpp:468
pappso::BasePlotWidget::restoreAxesRangeHistory
virtual void restoreAxesRangeHistory(std::size_t index)
Get the axis histories at index index and update the plot ranges.
Definition baseplotwidget.cpp:549
pappso::BasePlotWidget::drawXScopeSpanFeatures
virtual void drawXScopeSpanFeatures()
Definition baseplotwidget.cpp:2333
pappso::BasePlotWidget::spaceKeyReleaseEvent
virtual void spaceKeyReleaseEvent(QKeyEvent *event)
Definition baseplotwidget.cpp:817
pappso::BasePlotWidget::setFocusSignal
void setFocusSignal()
pappso::BasePlotWidget::replotWithAxisRangeX
virtual void replotWithAxisRangeX(double lower, double upper)
Definition baseplotwidget.cpp:2228
pappso::BasePlotWidget::createAllAncillaryItems
virtual void createAllAncillaryItems()
Definition baseplotwidget.cpp:215
pappso::BasePlotWidget::getPlottingColor
virtual QColor getPlottingColor(QCPAbstractPlottable *plottable_p) const
Definition baseplotwidget.cpp:442
pappso::BasePlotWidget::m_rightMousePseudoButtonKey
int m_rightMousePseudoButtonKey
Definition baseplotwidget.h:286
pappso::BasePlotWidget::m_focusedBrush
QBrush m_focusedBrush
Color used for the background of focused plot.
Definition baseplotwidget.h:364
pappso::BasePlotWidget::m_pen
QPen m_pen
Pen used to draw the graph and textual elements in the plot widget.
Definition baseplotwidget.h:367
pappso::BasePlotWidget::isSelectionRectangleVisible
virtual bool isSelectionRectangleVisible()
Definition baseplotwidget.cpp:3258
pappso::BasePlotWidget::isVerticalDisplacementAboveThreshold
virtual bool isVerticalDisplacementAboveThreshold()
Definition baseplotwidget.cpp:2600
pappso::BasePlotWidget::m_leftMousePseudoButtonKey
int m_leftMousePseudoButtonKey
Definition baseplotwidget.h:285
pappso::BasePlotWidget::mousePressHandler
virtual void mousePressHandler(QMouseEvent *event)
KEYBOARD-related EVENTS.
Definition baseplotwidget.cpp:1330
pappso::BasePlotWidget::verticalMoveMouseCursorCountPixels
virtual void verticalMoveMouseCursorCountPixels(int pixel_count)
Definition baseplotwidget.cpp:1876
pappso::BasePlotWidget::axisReframe
virtual void axisReframe()
Definition baseplotwidget.cpp:2111
pappso::BasePlotWidget::updateIntegrationScopeRhomb
virtual void updateIntegrationScopeRhomb(bool for_integration=false)
Definition baseplotwidget.cpp:2986
pappso::BasePlotWidget::mouseWheelEventSignal
void mouseWheelEventSignal(QWheelEvent *event, const pappso::BasePlotContext &context)
pappso::BasePlotWidget::resetAxesRangeHistory
virtual void resetAxesRangeHistory()
Definition baseplotwidget.cpp:475
pappso::BasePlotWidget::whatIsVisibleOfTheSelectionRectangle
virtual SelectionDrawingLines whatIsVisibleOfTheSelectionRectangle()
Definition baseplotwidget.cpp:3215
pappso::BasePlotWidget::showTracers
virtual void showTracers()
Show the traces (vertical and horizontal).
Definition baseplotwidget.cpp:2269
pappso::BasePlotWidget::horizontalGetGraphCoordNewPointCountPixels
virtual QPointF horizontalGetGraphCoordNewPointCountPixels(int pixel_count)
Definition baseplotwidget.cpp:1861
pappso::BasePlotWidget::afterReplotSignal
void afterReplotSignal()
pappso::BasePlotWidget::mp_selectionRectangeLine4
QCPItemLine * mp_selectionRectangeLine4
Definition baseplotwidget.h:295
pappso::BasePlotWidget::m_axisLabelX
QString m_axisLabelX
Definition baseplotwidget.h:280
pappso::BasePlotWidget::horizontalMoveMouseCursorCountPixels
virtual void horizontalMoveMouseCursorCountPixels(int pixel_count)
Definition baseplotwidget.cpp:1846
pappso::BasePlotWidget::BasePlotWidget
BasePlotWidget(QWidget *parent)
Definition baseplotwidget.cpp:49
pappso::BasePlotWidget::m_yAxisRangeHistory
std::vector< QCPRange * > m_yAxisRangeHistory
List of y axis ranges occurring during the panning zooming actions.
Definition baseplotwidget.h:331
pappso::BasePlotWidget::getInnermostRangeY
virtual QCPRange getInnermostRangeY(bool &found_range) const
Definition baseplotwidget.cpp:2038
pappso::BasePlotWidget::setFocus
virtual void setFocus()
PLOT ITEMS : TRACER TEXT ITEMS...
Definition baseplotwidget.cpp:3275
pappso::BasePlotWidget::drawYScopeSpanFeatures
virtual void drawYScopeSpanFeatures()
Definition baseplotwidget.cpp:2475
pappso::BasePlotWidget::getPen
virtual const QPen & getPen() const
Definition baseplotwidget.cpp:405
pappso::BasePlotWidget::updateContextXandYAxisRanges
virtual void updateContextXandYAxisRanges()
Definition baseplotwidget.cpp:3313
pappso::BasePlotWidget::updateIntegrationScopeHorizontalRhomb
virtual void updateIntegrationScopeHorizontalRhomb(bool for_integration=false)
Definition baseplotwidget.cpp:2764
pappso::BasePlotWidget::mousePseudoButtonKeyPressEvent
virtual void mousePseudoButtonKeyPressEvent(QKeyEvent *event)
Definition baseplotwidget.cpp:871
pappso::BasePlotWidget::setPlottingColor
virtual void setPlottingColor(QCPAbstractPlottable *plottable_p, const QColor &new_color)
Definition baseplotwidget.cpp:411
pappso::BasePlotWidget::calculateDragDeltas
virtual void calculateDragDeltas()
Definition baseplotwidget.cpp:2580
pappso::BasePlotWidget::getRange
QCPRange getRange(Enums::Axis axis, RangeType range_type, bool &found_range) const
Definition baseplotwidget.cpp:1934
pappso::BasePlotWidget::verticalGetGraphCoordNewPointCountPixels
virtual QPointF verticalGetGraphCoordNewPointCountPixels(int pixel_count)
Definition baseplotwidget.cpp:1892
pappso::BasePlotWidget::mp_vStartTracerItem
QCPItemLine * mp_vStartTracerItem
Vertical selection start tracer (typically in green).
Definition baseplotwidget.h:311
pappso::BasePlotWidget::mouseReleaseHandler
virtual void mouseReleaseHandler(QMouseEvent *event)
Definition baseplotwidget.cpp:1445
pappso::BasePlotWidget::m_unfocusedBrush
QBrush m_unfocusedBrush
Color used for the background of unfocused plot.
Definition baseplotwidget.h:359
pappso::BasePlotWidget::axisRescale
virtual void axisRescale()
RANGE-related functions.
Definition baseplotwidget.cpp:2056
pappso::BasePlotWidget::mouseMoveDraggingCursorSignal
void mouseMoveDraggingCursorSignal(QMouseEvent *event, const pappso::BasePlotContext &context)
pappso::BasePlotWidget::moveMouseCursorGraphCoordToGlobal
virtual void moveMouseCursorGraphCoordToGlobal(QPointF plot_coordinates)
Definition baseplotwidget.cpp:1828
pappso::BasePlotWidget::allLayerNamesToString
virtual QString allLayerNamesToString() const
Definition baseplotwidget.cpp:172
pappso::BasePlotWidget::mp_selectionRectangeLine3
QCPItemLine * mp_selectionRectangeLine3
Definition baseplotwidget.h:294
pappso::BasePlotWidget::mouseReleaseEventSignal
void mouseReleaseEventSignal(QMouseEvent *event, const pappso::BasePlotContext &context)
pappso::BasePlotWidget::axisDoubleClickHandler
virtual void axisDoubleClickHandler(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event)
Definition baseplotwidget.cpp:1698
pappso::BasePlotWidget::mp_vEndTracerItem
QCPItemLine * mp_vEndTracerItem
Vertical selection end tracer (typically in red).
Definition baseplotwidget.h:314
pappso::BasePlotWidget::plotRangesChangedWheelEventSignal
void plotRangesChangedWheelEventSignal(QWheelEvent *event, const pappso::BasePlotContext &context)
pappso::BasePlotWidget::mouseMoveHandler
virtual void mouseMoveHandler(QMouseEvent *event)
KEYBOARD-related EVENTS.
Definition baseplotwidget.cpp:947
pappso::BasePlotWidget::directionKeyPressEvent
virtual void directionKeyPressEvent(QKeyEvent *event)
Definition baseplotwidget.cpp:823
pappso::BasePlotWidget::afterLayoutSignal
void afterLayoutSignal()
pappso::BasePlotWidget::layerableLayerName
virtual QString layerableLayerName(QCPLayerable *layerable_p) const
Definition baseplotwidget.cpp:187
pappso::BasePlotWidget::m_axisLabelY
QString m_axisLabelY
Definition baseplotwidget.h:281
pappso::BasePlotWidget::keyReleaseEvent
virtual void keyReleaseEvent(QKeyEvent *event)
Handle specific key codes and trigger respective actions.
Definition baseplotwidget.cpp:646
pappso::BasePlotWidget::resetSelectionRectangle
virtual void resetSelectionRectangle()
Definition baseplotwidget.cpp:3208
pappso::BasePlotWidget::restorePreviousAxesRangeHistory
virtual void restorePreviousAxesRangeHistory()
Go up one history element in the axis history.
Definition baseplotwidget.cpp:522
pappso::BasePlotWidget::layerableLayerIndex
virtual int layerableLayerIndex(QCPLayerable *layerable_p) const
Definition baseplotwidget.cpp:198
pappso::BasePlotWidget::integrationRequestedSignal
void integrationRequestedSignal(const BasePlotContext &context)
pappso::BasePlotWidget::keyReleaseEventSignal
void keyReleaseEventSignal(QKeyEvent *event, const pappso::BasePlotContext &context)
pappso::BasePlotWidget::replotWithAxisRangeY
virtual void replotWithAxisRangeY(double lower, double upper)
Definition baseplotwidget.cpp:2238
pappso::BasePlotWidget::hideTracers
virtual void hideTracers()
Hide the traces (vertical and horizontal).
Definition baseplotwidget.cpp:2286
pappso::BasePlotWidget::mouseReleaseHandlerRightButton
virtual void mouseReleaseHandlerRightButton(QMouseEvent *event)
Definition baseplotwidget.cpp:1636
pappso::BasePlotWidget::updateIntegrationScopeVerticalRhomb
virtual void updateIntegrationScopeVerticalRhomb(bool for_integration=false)
Definition baseplotwidget.cpp:2872
pappso::BasePlotWidget::mousePseudoButtonKeyReleaseEvent
virtual void mousePseudoButtonKeyReleaseEvent(QKeyEvent *event)
Definition baseplotwidget.cpp:878
pappso::BasePlotWidget::hideAllPlotItems
virtual void hideAllPlotItems()
PLOTTING / REPLOTTING functions.
Definition baseplotwidget.cpp:2254
pappso::BasePlotWidget::getRangeX
virtual QCPRange getRangeX(bool &found_range, int index) const
MOUSE MOVEMENTS mouse/keyboard-triggered.
Definition baseplotwidget.cpp:1912
pappso::BasePlotWidget::m_xAxisRangeHistory
std::vector< QCPRange * > m_xAxisRangeHistory
List of x axis ranges occurring during the panning zooming actions.
Definition baseplotwidget.h:328
pappso::BasePlotWidget::m_context
BasePlotContext m_context
Definition baseplotwidget.h:283
pappso::ExceptionNotPossible
Definition exceptionnotpossible.h:29
pappso::Utils::zeroDecimalsInValue
static int zeroDecimalsInValue(pappso_double value)
Determine the number of zero decimals between the decimal point and the first non-zero decimal.
Definition utils.cpp:102
exceptionnotpossible.h
pappso::Enums::Axis
Axis
Definition types.h:258
pappso::Enums::Axis::y
@ y
Definition types.h:261
pappso::Enums::Axis::x
@ x
Definition types.h:260
pappso
tries to keep as much as possible monoisotopes, removing any possible C13 peaks and changes multichar...
Definition aa.cpp:39
pappso::IntegrationScopeFeatures
IntegrationScopeFeatures
Definition integrationscopebase.h:47
pappso::FLAT_ON_X_AXIS
@ FLAT_ON_X_AXIS
Definition integrationscopebase.h:52
pappso::RHOMBOID_VERTICAL
@ RHOMBOID_VERTICAL
Definition integrationscopebase.h:54
pappso::RHOMBOID_HORIZONTAL
@ RHOMBOID_HORIZONTAL
Definition integrationscopebase.h:55
pappso::FLAT_ON_Y_AXIS
@ FLAT_ON_Y_AXIS
Definition integrationscopebase.h:53
pappso::DragDirections::RIGHT_TO_LEFT
@ RIGHT_TO_LEFT
Definition baseplotcontext.h:34
pappso::DragDirections::BOTTOM_TO_TOP
@ BOTTOM_TO_TOP
Definition baseplotcontext.h:36
pappso::RangeType
RangeType
Definition baseplotwidget.h:73
pappso::RangeType::innermost
@ innermost
Definition baseplotwidget.h:75
pappso::RangeType::outermost
@ outermost
Definition baseplotwidget.h:74
pappso::SelectionDrawingLines
SelectionDrawingLines
Definition baseplotwidget.h:56
pappso::SelectionDrawingLines::RIGHT_LINE
@ RIGHT_LINE
Definition baseplotwidget.h:64
pappso::SelectionDrawingLines::NOT_SET
@ NOT_SET
Definition baseplotwidget.h:57
pappso::SelectionDrawingLines::FULL_POLYGON
@ FULL_POLYGON
Definition baseplotwidget.h:69
pappso::SelectionDrawingLines::TOP_LINE
@ TOP_LINE
Definition baseplotwidget.h:59
pappso::SelectionDrawingLines::BOTTOM_LINE
@ BOTTOM_LINE
Definition baseplotwidget.h:60
pappso::SelectionDrawingLines::LEFT_LINE
@ LEFT_LINE
Definition baseplotwidget.h:65
pappsoexception.h
types.h
utils.h