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 "../../types.h"
#include "baseplotwidget.h"
#include "../../pappsoexception.h"
#include "../../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);
//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::axisDoubleClick,
this,
&BasePlotWidget::axisDoubleClickHandler);
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;
}
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 trace 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(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_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();
// The keyboard key is being released, set the key code to 0.
m_context.m_pressedKeyCode = 0;
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)
{
// 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 has asked to measure the horizontal size of the rectangle and
// to start making a skewed selection rectangle.
m_context.m_selectRectangleWidth =
abs(m_context.m_currentDragPoint.x() - m_context.m_startDragPoint.x());
// qDebug() << "Set m_context.selectRectangleWidth to"
//<< m_context.m_selectRectangleWidth << "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(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);
// 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();
// 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->localPos();
// 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.lastCursorHoveredPoint;
// Now, depending on the button(s) (if any) that are pressed or not, we have
// a different processing.
if(m_context.m_pressedMouseButtons & Qt::LeftButton ||
m_context.m_pressedMouseButtons & Qt::RightButton)
mouseMoveHandlerDraggingCursor();
else
mouseMoveHandlerNotDraggingCursor();
event->accept();
}
void
BasePlotWidget::mouseMoveHandlerNotDraggingCursor()
{
m_context.m_isMouseDragging = false;
// 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);
// 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.
mp_vEndTracerItem->setVisible(false);
// 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);
// 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);
replot();
}
return;
}
void
BasePlotWidget::mouseMoveHandlerDraggingCursor()
{
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.
mp_hPosTracerItem->setVisible(false);
mp_vPosTracerItem->setVisible(false);
// Only bother with the tracers if the user wants them to be visible.
if(m_shouldTracersBeVisible)
{
// 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)
{
mp_hPosTracerItem->setVisible(false);
mp_vPosTracerItem->setVisible(false);
mp_vStartTracerItem->setVisible(false);
mp_vEndTracerItem->setVisible(false);
}
else
{
// 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.
m_context.recordDragDirections();
// qDebug() << m_context.toString();
}
// 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();
}
else if(m_context.m_mouseButtonsAtMousePress & Qt::RightButton)
{
return mouseMoveHandlerRightButtonDraggingCursor();
}
}
void
BasePlotWidget::mouseMoveHandlerLeftButtonDraggingCursor()
{
// 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() << __FILE__ << __LINE__ << "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.
mp_hPosTracerItem->setVisible(false);
mp_vPosTracerItem->setVisible(false);
mp_vStartTracerItem->setVisible(false);
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 a real rectangle.";
// When we draw a rectangle the tracers are of no use.
mp_hPosTracerItem->setVisible(false);
mp_vPosTracerItem->setVisible(false);
mp_vStartTracerItem->setVisible(false);
mp_vEndTracerItem->setVisible(false);
// Draw the rectangle, false, not as line segment and
// false, not for integration
drawSelectionRectangleAndPrepareZoom(false, false);
// Draw the selection width/height text
drawXDeltaFeatures();
drawYDeltaFeatures();
// qDebug() << "The selection polygon:"
//<< m_context.m_selectionPolygon.toString();
}
else
{
// qDebug() << "Apparently we are measuring a delta.";
// Draw the rectangle, true, as line segment and
// false, not for integration
drawSelectionRectangleAndPrepareZoom(true, false);
// qDebug() << "The selection polygon:"
//<< m_context.m_selectionPolygon.toString();
// The pure position tracers should be hidden.
mp_hPosTracerItem->setVisible(true);
mp_vPosTracerItem->setVisible(true);
// Then, make sure the region range vertical tracers are visible.
mp_vStartTracerItem->setVisible(true);
mp_vEndTracerItem->setVisible(true);
// Draw the selection width text
drawXDeltaFeatures();
}
}
void
BasePlotWidget::mouseMoveHandlerRightButtonDraggingCursor()
{
// 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 is a real rectangle.";
// When we draw a rectangle the tracers are of no use.
mp_hPosTracerItem->setVisible(false);
mp_vPosTracerItem->setVisible(false);
mp_vStartTracerItem->setVisible(false);
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
drawXDeltaFeatures();
drawYDeltaFeatures();
}
else
{
// Draw the rectangle, true, as line segment and
// false, true for integration
drawSelectionRectangleAndPrepareZoom(true, true);
// Draw the selection width text
drawXDeltaFeatures();
}
// Draw the selection width text
drawXDeltaFeatures();
}
void
BasePlotWidget::mousePressHandler(QMouseEvent *event)
{
// When the user clicks this widget it has to take focus.
setFocus();
QPointF mousePoint = event->localPos();
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->setVisible(true);
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();
}
void
BasePlotWidget::mouseReleaseHandler(QMouseEvent *event)
{
// 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;
// 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();
}
else if(m_context.m_lastReleasedMouseButton == Qt::RightButton)
{
return mouseReleaseHandlerRightButton();
}
// 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;
event->accept();
return;
}
void
BasePlotWidget::mouseReleaseHandlerLeftButton()
{
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();
emit plotRangesChangedSignal(m_context);
replot();
// Nothing else to do.
return;
}
// There are two possibilities:
//
// 1. The full selection polygon (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.
PolygonType current_selection_polygon_type =
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(current_selection_polygon_type == PolygonType::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();
// qDebug() << "The selection polygon:"
//<< m_context.m_selectionPolygon.toString();
return;
}
else if(current_selection_polygon_type == PolygonType::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();
// qDebug() << "The selection polygon:"
//<< m_context.m_selectionPolygon.toString();
}
}
else
qDebug() << "Another possibility.";
}
void
BasePlotWidget::mouseReleaseHandlerRightButton()
{
// 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)
emit integrationRequestedSignal(m_context);
// else
// qDebug() << "Not asking for integration.";
}
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(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(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 == Axis::x)
temp_range = graph_p->getKeyRange(found_range_for_iter);
else if(axis == 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(Axis::x, RangeType::innermost, found_range);
}
QCPRange
BasePlotWidget::getOutermostRangeX(bool &found_range) const
{
return getRange(Axis::x, RangeType::outermost, found_range);
}
QCPRange
BasePlotWidget::getInnermostRangeY(bool &found_range) const
{
return getRange(Axis::y, RangeType::innermost, found_range);
}
QCPRange
BasePlotWidget::getOutermostRangeY(bool &found_range) const
{
return getRange(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.
// What is the x delta ?
double xDelta =
m_context.m_currentDragPoint.x() - m_context.m_startDragPoint.x();
// If xDelta is < 0, the 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;
// qDebug() << "Setting xaxis:" << xLower << "--" << xUpper - xDelta;
xAxis->setRange(xLower, xUpper - 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.
// What is the y delta ?
double yDelta =
m_context.m_currentDragPoint.y() - m_context.m_startDragPoint.y();
// See above for an explanation of the computation.
yAxis->setRange(yLower, yUpper - yDelta);
// Old version
// if(yDelta < 0)
//{
//// The dragging operation was from top to bottom, we are enlarging
//// the range (thus, we are unzooming the view, since the widget
//// always has the same size).
// yAxis->setRange(yLower, yUpper + fabs(yDelta));
//}
// else
//{
//// The dragging operation was from bottom to top, we are reducing
//// the range (thus, we are zooming the view, since the widget
//// always has the same size).
// yAxis->setRange(yLower, yUpper - fabs(yDelta));
//}
}
// End of
// else // that is, if(m_context.m_wasClickOnYAxis)
// Update the context with the current axes ranges
updateContextXandYAxisRanges();
emit plotRangesChangedSignal(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_xRegionRangeEnd));
// 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_yRegionRangeEnd));
// qDebug() << "xaxis:" << xAxis->range().lower << "-" <<
// xAxis->range().upper
//<< "yaxis:" << yAxis->range().lower << "-" << yAxis->range().upper;
updateContextXandYAxisRanges();
updateAxesRangeHistory();
emit plotRangesChangedSignal(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_xRegionRangeEnd));
yAxis->setRange(
QCPRange(m_context.m_yRegionRangeStart, m_context.m_yRegionRangeEnd));
updateContextXandYAxisRanges();
updateAxesRangeHistory();
emit plotRangesChangedSignal(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");
// First update the x&y axis ranges that we'll need for panning.
updateContextXandYAxisRanges();
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);
}
// 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 contex 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(m_context);
replot();
}
void
BasePlotWidget::replotWithAxesRanges(QCPRange xAxisRange,
QCPRange yAxisRange,
Axis axis)
{
if(static_cast<int>(axis) & static_cast<int>(Axis::x))
{
xAxis->setRange(xAxisRange.lower, xAxisRange.upper);
}
if(static_cast<int>(axis) & static_cast<int>(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.
mp_xDeltaTextItem->setVisible(false);
mp_yDeltaTextItem->setVisible(false);
// Ensure the right selection rectangle is drawn.
updateSelectionRectangle(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::drawXDeltaFeatures()
{
// The user is dragging the mouse over the graph and we want them to know what
// is the x delta value, that is the span between the point at the start of
// the drag and the current drag position.
// 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 vertiacal
// 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 skewed rectangle selection.
QPointF leftmost_point = m_context.m_selectionPolygon.getLeftMostPoint();
// qDebug() << "leftmost_point:" << leftmost_point;
QPointF rightmost_point = m_context.m_selectionPolygon.getRightMostPoint();
// qDebug() << "rightmost_point:" << rightmost_point;
double x_axis_center_position =
leftmost_point.x() + (rightmost_point.x() - leftmost_point.x()) / 2;
// qDebug() << "x_axis_center_position:" << x_axis_center_position;
// 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.
// Set aside a point instance to store the pixel coordinates of the text.
QPointF pixel_coordinates;
// What is the distance between the rectangle line at current drag point and
// the text itself.
int pixels_away_from_line = 15;
// 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::TOP_TO_BOTTOM))
{
// We need to print inside the rectangle, that is pixels_above_line pixels
// to the bottom, 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;
}
double y_axis_pixel_coordinate =
yAxis->coordToPixel(m_context.m_currentDragPoint.y());
double y_axis_modified_pixel_coordinate =
y_axis_pixel_coordinate + pixels_away_from_line;
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()));
mp_xDeltaTextItem->position->setCoords(x_axis_center_position,
graph_coordinates.y());
mp_xDeltaTextItem->setText(QString("%1").arg(m_context.m_xDelta, 0, 'f', 3));
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::drawYDeltaFeatures()
{
if(m_context.m_selectionPolygon.is1D())
return;
// The user is dragging the mouse over the graph and we want them to know what
// is the y delta value, that is the span between the point at the top of
// the selection polygon and the point at its bottom.
// 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 vertiacal
// 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 skewed rectangle selection.
QPointF leftmost_point = m_context.m_selectionPolygon.getLeftMostPoint();
QPointF topmost_point = m_context.m_selectionPolygon.getTopMostPoint();
// qDebug() << "leftmost_point:" << leftmost_point;
QPointF rightmost_point = m_context.m_selectionPolygon.getRightMostPoint();
QPointF bottommost_point = m_context.m_selectionPolygon.getBottomMostPoint();
// qDebug() << "rightmost_point:" << rightmost_point;
double x_axis_center_position =
leftmost_point.x() + (rightmost_point.x() - leftmost_point.x()) / 2;
double y_axis_center_position =
bottommost_point.y() + (topmost_point.y() - bottommost_point.y()) / 2;
// qDebug() << "x_axis_center_position:" << x_axis_center_position;
mp_yDeltaTextItem->position->setCoords(x_axis_center_position,
y_axis_center_position);
mp_yDeltaTextItem->setText(QString("%1").arg(m_context.m_yDelta, 0, 'f', 3));
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();
// qDebug() << "xDelta:" << m_context.m_xDelta
//<< "and yDelta:" << m_context.m_yDelta;
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)
{
// qDebug() << "isVerticalDisplacementAboveThreshold: true";
return true;
}
// qDebug() << "isVerticalDisplacementAboveThreshold: false";
return false;
}
void
BasePlotWidget::update1DSelectionRectangle(bool for_integration)
{
// if(for_integration)
// qDebug() << "for_integration:" << for_integration;
// When we make a linear selection, the selection polygon is a polygon that
// has the following characteristics:
//
// the x range is the linear selection span
//
// the y range is the widest std::min -> std::max possible.
// This is how the selection polygon logic knows if its is mono-
// two-dimensional.
// We want the top left point to effectively be the top left point, so check
// the direction of the mouse cursor drag.
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());
double y_position = m_context.m_startDragPoint.y();
m_context.m_selectionPolygon.set1D(x_range_start, x_range_end);
// 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::update2DSelectionRectangleSquare(bool for_integration)
{
// 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.m_selectionPolygon.resetPoints();
if(m_context.m_currentDragPoint.x() < m_context.m_startDragPoint.x())
{
// qDebug() << "Dragging from right to left";
if(m_context.m_currentDragPoint.y() < m_context.m_startDragPoint.y())
{
// qDebug() << "Dragging from top to bottom";
// TOP_LEFT_POINT
m_context.m_selectionPolygon.setPoint(
PointSpecs::TOP_LEFT_POINT,
m_context.m_currentDragPoint.x(),
m_context.m_startDragPoint.y());
// TOP_RIGHT_POINT
m_context.m_selectionPolygon.setPoint(PointSpecs::TOP_RIGHT_POINT,
m_context.m_startDragPoint.x(),
m_context.m_startDragPoint.y());
// BOTTOM_RIGHT_POINT
m_context.m_selectionPolygon.setPoint(
PointSpecs::BOTTOM_RIGHT_POINT,
m_context.m_startDragPoint.x(),
m_context.m_currentDragPoint.y());
// BOTTOM_LEFT_POINT
m_context.m_selectionPolygon.setPoint(
PointSpecs::BOTTOM_LEFT_POINT,
m_context.m_currentDragPoint.x(),
m_context.m_currentDragPoint.y());
}
// End of
// if(m_context.m_currentDragPoint.y() < m_context.m_startDragPoint.y())
else
{
// qDebug() << "Dragging from bottom to top";
// TOP_LEFT_POINT
m_context.m_selectionPolygon.setPoint(
PointSpecs::TOP_LEFT_POINT,
m_context.m_currentDragPoint.x(),
m_context.m_currentDragPoint.y());
// TOP_RIGHT_POINT
m_context.m_selectionPolygon.setPoint(
PointSpecs::TOP_RIGHT_POINT,
m_context.m_startDragPoint.x(),
m_context.m_currentDragPoint.y());
// BOTTOM_RIGHT_POINT
m_context.m_selectionPolygon.setPoint(PointSpecs::BOTTOM_RIGHT_POINT,
m_context.m_startDragPoint.x(),
m_context.m_startDragPoint.y());
// BOTTOM_LEFT_POINT
m_context.m_selectionPolygon.setPoint(
PointSpecs::BOTTOM_LEFT_POINT,
m_context.m_currentDragPoint.x(),
m_context.m_startDragPoint.y());
}
}
// End of
// if(m_context.m_currentDragPoint.x() < m_context.m_startDragPoint.x())
else
{
// qDebug() << "Dragging from left to right";
if(m_context.m_currentDragPoint.y() < m_context.m_startDragPoint.y())
{
// qDebug() << "Dragging from top to bottom";
// TOP_LEFT_POINT
m_context.m_selectionPolygon.setPoint(PointSpecs::TOP_LEFT_POINT,
m_context.m_startDragPoint.x(),
m_context.m_startDragPoint.y());
// TOP_RIGHT_POINT
m_context.m_selectionPolygon.setPoint(
PointSpecs::TOP_RIGHT_POINT,
m_context.m_currentDragPoint.x(),
m_context.m_startDragPoint.y());
// BOTTOM_RIGHT_POINT
m_context.m_selectionPolygon.setPoint(
PointSpecs::BOTTOM_RIGHT_POINT,
m_context.m_currentDragPoint.x(),
m_context.m_currentDragPoint.y());
// BOTTOM_LEFT_POINT
m_context.m_selectionPolygon.setPoint(
PointSpecs::BOTTOM_LEFT_POINT,
m_context.m_startDragPoint.x(),
m_context.m_currentDragPoint.y());
}
else
{
// qDebug() << "Dragging from bottom to top";
// TOP_LEFT_POINT
m_context.m_selectionPolygon.setPoint(
PointSpecs::TOP_LEFT_POINT,
m_context.m_startDragPoint.x(),
m_context.m_currentDragPoint.y());
// TOP_RIGHT_POINT
m_context.m_selectionPolygon.setPoint(
PointSpecs::TOP_RIGHT_POINT,
m_context.m_currentDragPoint.x(),
m_context.m_currentDragPoint.y());
// BOTTOM_RIGHT_POINT
m_context.m_selectionPolygon.setPoint(
PointSpecs::BOTTOM_RIGHT_POINT,
m_context.m_currentDragPoint.x(),
m_context.m_startDragPoint.y());
// BOTTOM_LEFT_POINT
m_context.m_selectionPolygon.setPoint(PointSpecs::BOTTOM_LEFT_POINT,
m_context.m_startDragPoint.x(),
m_context.m_startDragPoint.y());
}
}
// qDebug() << "Now draw the lines with points:"
//<< m_context.m_selectionPolygon.toString();
// Top line
mp_selectionRectangeLine1->start->setCoords(
m_context.m_selectionPolygon.getPoint(PointSpecs::TOP_LEFT_POINT));
mp_selectionRectangeLine1->end->setCoords(
m_context.m_selectionPolygon.getPoint(PointSpecs::TOP_RIGHT_POINT));
// 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
mp_selectionRectangeLine2->start->setCoords(
m_context.m_selectionPolygon.getPoint(PointSpecs::TOP_RIGHT_POINT));
mp_selectionRectangeLine2->end->setCoords(
m_context.m_selectionPolygon.getPoint(PointSpecs::BOTTOM_RIGHT_POINT));
mp_selectionRectangeLine2->setVisible(true);
// Bottom line
mp_selectionRectangeLine3->start->setCoords(
m_context.m_selectionPolygon.getPoint(PointSpecs::BOTTOM_RIGHT_POINT));
mp_selectionRectangeLine3->end->setCoords(
m_context.m_selectionPolygon.getPoint(PointSpecs::BOTTOM_LEFT_POINT));
mp_selectionRectangeLine3->setVisible(true);
// Left line
mp_selectionRectangeLine4->start->setCoords(
m_context.m_selectionPolygon.getPoint(PointSpecs::BOTTOM_LEFT_POINT));
mp_selectionRectangeLine4->end->setCoords(
m_context.m_selectionPolygon.getPoint(PointSpecs::TOP_LEFT_POINT));
mp_selectionRectangeLine4->setVisible(true);
}
void
BasePlotWidget::update2DSelectionRectangleSkewed(bool for_integration)
{
// if(for_integration)
// qDebug() << "for_integration:" << for_integration;
// We are handling a skewed rectangle, that is a rectangle that is
// tilted either to the left or to the right.
// qDebug() << "m_context.m_selectRectangleWidth: "
//<< m_context.m_selectRectangleWidth;
// Top line
// start
// qDebug() << "m_context.m_startDragPoint: " <<
// m_context.m_startDragPoint.x()
//<< "-" << m_context.m_startDragPoint.y();
// qDebug() << "m_context.m_currentDragPoint: "
//<< m_context.m_currentDragPoint.x() << "-"
//<< m_context.m_currentDragPoint.y();
m_context.m_selectionPolygon.resetPoints();
if(m_context.m_currentDragPoint.x() < m_context.m_startDragPoint.x())
{
// qDebug() << "Dragging from right to left";
if(m_context.m_currentDragPoint.y() < m_context.m_startDragPoint.y())
{
// qDebug() << "Dragging from top to bottom";
m_context.m_selectionPolygon.setPoint(
PointSpecs::TOP_LEFT_POINT,
m_context.m_startDragPoint.x() - m_context.m_selectRectangleWidth,
m_context.m_startDragPoint.y());
// m_context.m_selRectTopLeftPoint.setX(
// m_context.m_startDragPoint.x() -
// m_context.m_selectRectangleWidth);
// m_context.m_selRectTopLeftPoint.setY(m_context.m_startDragPoint.y());
m_context.m_selectionPolygon.setPoint(PointSpecs::TOP_RIGHT_POINT,
m_context.m_startDragPoint.x(),
m_context.m_startDragPoint.y());
// m_context.m_selRectTopRightPoint.setX(m_context.m_startDragPoint.x());
// m_context.m_selRectTopRightPoint.setY(m_context.m_startDragPoint.y());
m_context.m_selectionPolygon.setPoint(
PointSpecs::BOTTOM_RIGHT_POINT,
m_context.m_currentDragPoint.x() + m_context.m_selectRectangleWidth,
m_context.m_currentDragPoint.y());
// m_context.m_selRectBottomRightPoint.setX(
// m_context.m_currentDragPoint.x() +
// m_context.m_selectRectangleWidth);
// m_context.m_selRectBottomRightPoint.setY(
// m_context.m_currentDragPoint.y());
m_context.m_selectionPolygon.setPoint(
PointSpecs::BOTTOM_LEFT_POINT,
m_context.m_currentDragPoint.x(),
m_context.m_currentDragPoint.y());
// m_context.m_selRectBottomLeftPoint.setX(
// m_context.m_currentDragPoint.x());
// m_context.m_selRectBottomLeftPoint.setY(
// m_context.m_currentDragPoint.y());
}
else
{
// qDebug() << "Dragging from bottom to top";
m_context.m_selectionPolygon.setPoint(
PointSpecs::TOP_LEFT_POINT,
m_context.m_currentDragPoint.x(),
m_context.m_currentDragPoint.y());
// m_context.m_selRectTopLeftPoint.setX(
// m_context.m_currentDragPoint.x());
// m_context.m_selRectTopLeftPoint.setY(
// m_context.m_currentDragPoint.y());
m_context.m_selectionPolygon.setPoint(
PointSpecs::TOP_RIGHT_POINT,
m_context.m_currentDragPoint.x() + m_context.m_selectRectangleWidth,
m_context.m_currentDragPoint.y());
// m_context.m_selRectTopRightPoint.setX(
// m_context.m_currentDragPoint.x() +
// m_context.m_selectRectangleWidth);
// m_context.m_selRectTopRightPoint.setY(
// m_context.m_currentDragPoint.y());
m_context.m_selectionPolygon.setPoint(PointSpecs::BOTTOM_RIGHT_POINT,
m_context.m_startDragPoint.x(),
m_context.m_startDragPoint.y());
// m_context.m_selRectBottomRightPoint.setX(
// m_context.m_startDragPoint.x());
// m_context.m_selRectBottomRightPoint.setY(
// m_context.m_startDragPoint.y());
m_context.m_selectionPolygon.setPoint(
PointSpecs::BOTTOM_LEFT_POINT,
m_context.m_startDragPoint.x() - m_context.m_selectRectangleWidth,
m_context.m_startDragPoint.y());
// m_context.m_selRectBottomLeftPoint.setX(
// m_context.m_startDragPoint.x() -
// m_context.m_selectRectangleWidth);
// m_context.m_selRectBottomLeftPoint.setY(
// m_context.m_startDragPoint.y());
}
}
// End of
// Dragging from right to left.
else
{
// qDebug() << "Dragging from left to right";
if(m_context.m_currentDragPoint.y() < m_context.m_startDragPoint.y())
{
// qDebug() << "Dragging from top to bottom";
m_context.m_selectionPolygon.setPoint(PointSpecs::TOP_LEFT_POINT,
m_context.m_startDragPoint.x(),
m_context.m_startDragPoint.y());
// m_context.m_selRectTopLeftPoint.setX(m_context.m_startDragPoint.x());
// m_context.m_selRectTopLeftPoint.setY(m_context.m_startDragPoint.y());
m_context.m_selectionPolygon.setPoint(
PointSpecs::TOP_RIGHT_POINT,
m_context.m_startDragPoint.x() + m_context.m_selectRectangleWidth,
m_context.m_startDragPoint.y());
// m_context.m_selRectTopRightPoint.setX(
// m_context.m_startDragPoint.x() +
// m_context.m_selectRectangleWidth);
// m_context.m_selRectTopRightPoint.setY(m_context.m_startDragPoint.y());
m_context.m_selectionPolygon.setPoint(
PointSpecs::BOTTOM_RIGHT_POINT,
m_context.m_currentDragPoint.x(),
m_context.m_currentDragPoint.y());
// m_context.m_selRectBottomRightPoint.setX(
// m_context.m_currentDragPoint.x());
// m_context.m_selRectBottomRightPoint.setY(
// m_context.m_currentDragPoint.y());
m_context.m_selectionPolygon.setPoint(
PointSpecs::BOTTOM_LEFT_POINT,
m_context.m_currentDragPoint.x() - m_context.m_selectRectangleWidth,
m_context.m_currentDragPoint.y());
// m_context.m_selRectBottomLeftPoint.setX(
// m_context.m_currentDragPoint.x() -
// m_context.m_selectRectangleWidth);
// m_context.m_selRectBottomLeftPoint.setY(
// m_context.m_currentDragPoint.y());
}
else
{
// qDebug() << "Dragging from bottom to top";
m_context.m_selectionPolygon.setPoint(
PointSpecs::TOP_LEFT_POINT,
m_context.m_currentDragPoint.x() - m_context.m_selectRectangleWidth,
m_context.m_currentDragPoint.y());
// m_context.m_selRectTopLeftPoint.setX(
// m_context.m_currentDragPoint.x() -
// m_context.m_selectRectangleWidth);
// m_context.m_selRectTopLeftPoint.setY(
// m_context.m_currentDragPoint.y());
m_context.m_selectionPolygon.setPoint(
PointSpecs::TOP_RIGHT_POINT,
m_context.m_currentDragPoint.x(),
m_context.m_currentDragPoint.y());
// m_context.m_selRectTopRightPoint.setX(
// m_context.m_currentDragPoint.x());
// m_context.m_selRectTopRightPoint.setY(
// m_context.m_currentDragPoint.y());
m_context.m_selectionPolygon.setPoint(
PointSpecs::BOTTOM_RIGHT_POINT,
m_context.m_startDragPoint.x() + m_context.m_selectRectangleWidth,
m_context.m_startDragPoint.y());
// m_context.m_selRectBottomRightPoint.setX(
// m_context.m_startDragPoint.x() +
// m_context.m_selectRectangleWidth);
// m_context.m_selRectBottomRightPoint.setY(
// m_context.m_startDragPoint.y());
m_context.m_selectionPolygon.setPoint(PointSpecs::BOTTOM_LEFT_POINT,
m_context.m_startDragPoint.x(),
m_context.m_startDragPoint.y());
// m_context.m_selRectBottomLeftPoint.setX(
// m_context.m_startDragPoint.x());
// m_context.m_selRectBottomLeftPoint.setY(
// m_context.m_startDragPoint.y());
}
}
// End of Dragging from left to right.
// qDebug() << "Now draw the lines with points:"
//<< m_context.m_selectionPolygon.toString();
// Top line
mp_selectionRectangeLine1->start->setCoords(
m_context.m_selectionPolygon.getPoint(PointSpecs::TOP_LEFT_POINT));
mp_selectionRectangeLine1->end->setCoords(
m_context.m_selectionPolygon.getPoint(PointSpecs::TOP_RIGHT_POINT));
// 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
mp_selectionRectangeLine2->start->setCoords(
m_context.m_selectionPolygon.getPoint(PointSpecs::TOP_RIGHT_POINT));
mp_selectionRectangeLine2->end->setCoords(
m_context.m_selectionPolygon.getPoint(PointSpecs::BOTTOM_RIGHT_POINT));
mp_selectionRectangeLine2->setVisible(true);
// Bottom line
mp_selectionRectangeLine3->start->setCoords(
m_context.m_selectionPolygon.getPoint(PointSpecs::BOTTOM_RIGHT_POINT));
mp_selectionRectangeLine3->end->setCoords(
m_context.m_selectionPolygon.getPoint(PointSpecs::BOTTOM_LEFT_POINT));
mp_selectionRectangeLine3->setVisible(true);
// Left line
mp_selectionRectangeLine4->end->setCoords(
m_context.m_selectionPolygon.getPoint(PointSpecs::BOTTOM_LEFT_POINT));
mp_selectionRectangeLine4->start->setCoords(
m_context.m_selectionPolygon.getPoint(PointSpecs::TOP_LEFT_POINT));
mp_selectionRectangeLine4->setVisible(true);
}
void
BasePlotWidget::updateSelectionRectangle(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 S key is down, then the rectangle is
// skewed, that is its vertical sides are not parallel to the y axis.
//
// b. otherwise the rectangle is conventional.
if(as_line_segment)
{
update1DSelectionRectangle(for_integration);
}
else
{
if(!(m_context.m_keyboardModifiers & Qt::AltModifier))
{
update2DSelectionRectangleSquare(for_integration);
}
else if(m_context.m_keyboardModifiers & Qt::AltModifier)
{
update2DSelectionRectangleSkewed(for_integration);
}
}
// This code automatically sorts the ranges (range start is always less than
// range end) even if the user actually selects from high to low (right to
// left or bottom to top). This has implications in code that uses the
// m_context data to perform some computations. This is why it is important
// that m_dragDirections be set correctly to establish where the current drag
// point is actually located (at which point).
m_context.m_xRegionRangeStart =
m_context.m_selectionPolygon.getLeftMostPoint().x();
m_context.m_xRegionRangeEnd =
m_context.m_selectionPolygon.getRightMostPoint().x();
m_context.m_yRegionRangeStart =
m_context.m_selectionPolygon.getBottomMostPoint().y();
m_context.m_yRegionRangeEnd =
m_context.m_selectionPolygon.getTopMostPoint().y();
// 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
drawXDeltaFeatures();
}
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()
{
m_context.m_selectionPolygon.resetPoints();
}
PolygonType
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>(PolygonType::NOT_SET);
if(mp_selectionRectangeLine1->visible())
{
current_selection_polygon |= static_cast<int>(PolygonType::TOP_LINE);
// qDebug() << "current_selection_polygon:" << current_selection_polygon;
}
if(mp_selectionRectangeLine2->visible())
{
current_selection_polygon |= static_cast<int>(PolygonType::RIGHT_LINE);
// qDebug() << "current_selection_polygon:" << current_selection_polygon;
}
if(mp_selectionRectangeLine3->visible())
{
current_selection_polygon |= static_cast<int>(PolygonType::BOTTOM_LINE);
// qDebug() << "current_selection_polygon:" << current_selection_polygon;
}
if(mp_selectionRectangeLine4->visible())
{
current_selection_polygon |= static_cast<int>(PolygonType::LEFT_LINE);
// qDebug() << "current_selection_polygon:" << current_selection_polygon;
}
// qDebug() << "returning visibility:" << current_selection_polygon;
return static_cast<PolygonType>(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);
}
const BasePlotContext &
BasePlotWidget::getContext() const
{
return m_context;
}
} // namespace pappso
basePlotContextPtrMetaTypeId
int basePlotContextPtrMetaTypeId
Definition: baseplotwidget.cpp:43
basePlotContextMetaTypeId
int basePlotContextMetaTypeId
Definition: baseplotwidget.cpp:41
baseplotwidget.h
pappso::BasePlotContext
Definition: baseplotcontext.h:36
pappso::BasePlotContext::m_xRegionRangeStart
double m_xRegionRangeStart
Definition: baseplotcontext.h:81
pappso::BasePlotContext::m_startDragPoint
QPointF m_startDragPoint
Definition: baseplotcontext.h:54
pappso::BasePlotContext::m_xDelta
double m_xDelta
Definition: baseplotcontext.h:87
pappso::BasePlotContext::m_mouseButtonsAtMousePress
Qt::MouseButtons m_mouseButtonsAtMousePress
Definition: baseplotcontext.h:100
pappso::BasePlotContext::m_selectionPolygon
SelectionPolygon m_selectionPolygon
Definition: baseplotcontext.h:59
pappso::BasePlotContext::recordDragDirections
DragDirections recordDragDirections()
Definition: baseplotcontext.cpp:150
pappso::BasePlotContext::m_wasClickOnXAxis
bool m_wasClickOnXAxis
Definition: baseplotcontext.h:70
pappso::BasePlotContext::m_keyboardModifiers
Qt::KeyboardModifiers m_keyboardModifiers
Definition: baseplotcontext.h:93
pappso::BasePlotContext::m_pressedKeyCode
int m_pressedKeyCode
Definition: baseplotcontext.h:90
pappso::BasePlotContext::m_lastPressedMouseButton
Qt::MouseButtons m_lastPressedMouseButton
Definition: baseplotcontext.h:95
pappso::BasePlotContext::m_isMouseDragging
bool m_isMouseDragging
Definition: baseplotcontext.h:46
pappso::BasePlotContext::m_xRange
QCPRange m_xRange
Definition: baseplotcontext.h:63
pappso::BasePlotContext::m_isMeasuringDistance
bool m_isMeasuringDistance
Definition: baseplotcontext.h:73
pappso::BasePlotContext::m_dragDirections
DragDirections m_dragDirections
Definition: baseplotcontext.h:57
pappso::BasePlotContext::m_lastCursorHoveredPoint
QPointF m_lastCursorHoveredPoint
Definition: baseplotcontext.h:56
pappso::BasePlotContext::m_currentDragPoint
QPointF m_currentDragPoint
Definition: baseplotcontext.h:55
pappso::BasePlotContext::m_yDelta
double m_yDelta
Definition: baseplotcontext.h:88
pappso::BasePlotContext::m_yRegionRangeStart
double m_yRegionRangeStart
Definition: baseplotcontext.h:84
pappso::BasePlotContext::m_isRightPseudoButtonKeyPressed
bool m_isRightPseudoButtonKeyPressed
Definition: baseplotcontext.h:51
pappso::BasePlotContext::m_pressedMouseButtons
Qt::MouseButtons m_pressedMouseButtons
Definition: baseplotcontext.h:98
pappso::BasePlotContext::m_yRange
QCPRange m_yRange
Definition: baseplotcontext.h:64
pappso::BasePlotContext::m_mouseButtonsAtMouseRelease
Qt::MouseButtons m_mouseButtonsAtMouseRelease
Definition: baseplotcontext.h:101
pappso::BasePlotContext::m_releasedKeyCode
int m_releasedKeyCode
Definition: baseplotcontext.h:91
pappso::BasePlotContext::m_isLeftPseudoButtonKeyPressed
bool m_isLeftPseudoButtonKeyPressed
Definition: baseplotcontext.h:50
pappso::BasePlotContext::m_lastReleasedMouseButton
Qt::MouseButtons m_lastReleasedMouseButton
Definition: baseplotcontext.h:96
pappso::BasePlotContext::m_wasMouseDragging
bool m_wasMouseDragging
Definition: baseplotcontext.h:47
pappso::BasePlotContext::m_yRegionRangeEnd
double m_yRegionRangeEnd
Definition: baseplotcontext.h:85
pappso::BasePlotContext::m_xRegionRangeEnd
double m_xRegionRangeEnd
Definition: baseplotcontext.h:82
pappso::BasePlotContext::m_selectRectangleWidth
double m_selectRectangleWidth
Definition: baseplotcontext.h:60
pappso::BasePlotContext::m_wasClickOnYAxis
bool m_wasClickOnYAxis
Definition: baseplotcontext.h:71
pappso::BasePlotWidget::axisPan
virtual void axisPan()
Definition: baseplotwidget.cpp:2078
pappso::BasePlotWidget::m_mouseMoveHandlerSkipAmount
int m_mouseMoveHandlerSkipAmount
How many mouse move events must be skipped *‍/.
Definition: baseplotwidget.h:312
pappso::BasePlotWidget::m_lastAxisRangeHistoryIndex
std::size_t m_lastAxisRangeHistoryIndex
Index of the last axis range history item.
Definition: baseplotwidget.h:294
pappso::BasePlotWidget::updateAxesRangeHistory
virtual void updateAxesRangeHistory()
Create new axis range history items and append them to the history.
Definition: baseplotwidget.cpp:509
pappso::BasePlotWidget::m_shouldTracersBeVisible
bool m_shouldTracersBeVisible
Tells if the tracers should be visible.
Definition: baseplotwidget.h:271
pappso::BasePlotWidget::hideSelectionRectangle
virtual void hideSelectionRectangle(bool reset_values=false)
Definition: baseplotwidget.cpp:3024
pappso::BasePlotWidget::mouseMoveHandlerDraggingCursor
virtual void mouseMoveHandlerDraggingCursor()
Definition: baseplotwidget.cpp:987
pappso::BasePlotWidget::directionKeyReleaseEvent
virtual void directionKeyReleaseEvent(QKeyEvent *event)
Definition: baseplotwidget.cpp:819
pappso::BasePlotWidget::mp_yDeltaTextItem
QCPItemText * mp_yDeltaTextItem
Definition: baseplotwidget.h:268
pappso::BasePlotWidget::mp_selectionRectangeLine1
QCPItemLine * mp_selectionRectangeLine1
Rectangle defining the borders of zoomed-in/out data.
Definition: baseplotwidget.h:261
pappso::BasePlotWidget::getOutermostRangeX
virtual QCPRange getOutermostRangeX(bool &found_range) const
Definition: baseplotwidget.cpp:1908
pappso::BasePlotWidget::lastCursorHoveredPointSignal
void lastCursorHoveredPointSignal(const QPointF &pointf)
pappso::BasePlotWidget::plottableDestructionRequestedSignal
void plottableDestructionRequestedSignal(BasePlotWidget *base_plot_widget_p, QCPAbstractPlottable *plottable_p, const BasePlotContext &context)
pappso::BasePlotWidget::update2DSelectionRectangleSquare
virtual void update2DSelectionRectangleSquare(bool for_integration=false)
Definition: baseplotwidget.cpp:2512
pappso::BasePlotWidget::getContext
virtual const BasePlotContext & getContext() const
Definition: baseplotwidget.cpp:3147
pappso::BasePlotWidget::drawSelectionRectangleAndPrepareZoom
virtual void drawSelectionRectangleAndPrepareZoom(bool as_line_segment=false, bool for_integration=false)
Definition: baseplotwidget.cpp:2218
pappso::BasePlotWidget::getRangeY
virtual QCPRange getRangeY(bool &found_range, int index) const
Definition: baseplotwidget.cpp:1796
pappso::BasePlotWidget::keyPressEvent
virtual void keyPressEvent(QKeyEvent *event)
KEYBOARD-related EVENTS.
Definition: baseplotwidget.cpp:608
pappso::BasePlotWidget::~BasePlotWidget
virtual ~BasePlotWidget()
Destruct this BasePlotWidget instance.
Definition: baseplotwidget.cpp:162
pappso::BasePlotWidget::axisZoom
virtual void axisZoom()
Definition: baseplotwidget.cpp:2054
pappso::BasePlotWidget::mp_selectionRectangeLine2
QCPItemLine * mp_selectionRectangeLine2
Definition: baseplotwidget.h:262
pappso::BasePlotWidget::mp_xDeltaTextItem
QCPItemText * mp_xDeltaTextItem
Text describing the x-axis delta value during a drag operation.
Definition: baseplotwidget.h:267
pappso::BasePlotWidget::updateSelectionRectangle
virtual void updateSelectionRectangle(bool as_line_segment=false, bool for_integration=false)
Definition: baseplotwidget.cpp:2959
pappso::BasePlotWidget::setAxisLabelX
virtual void setAxisLabelX(const QString &label)
Definition: baseplotwidget.cpp:467
pappso::BasePlotWidget::mouseMoveHandlerLeftButtonDraggingCursor
virtual void mouseMoveHandlerLeftButtonDraggingCursor()
Definition: baseplotwidget.cpp:1059
pappso::BasePlotWidget::m_mouseMoveHandlerSkipCount
int m_mouseMoveHandlerSkipCount
Counter to handle the "fat data" mouse move event handling.
Definition: baseplotwidget.h:320
pappso::BasePlotWidget::getOutermostRangeY
virtual QCPRange getOutermostRangeY(bool &found_range) const
Definition: baseplotwidget.cpp:1923
pappso::BasePlotWidget::dragDirection
int dragDirection()
MOUSE-related EVENTS.
Definition: baseplotwidget.cpp:1667
pappso::BasePlotWidget::isClickOntoYAxis
bool isClickOntoYAxis(const QPointF &mousePoint)
Definition: baseplotwidget.cpp:1636
pappso::BasePlotWidget::moveMouseCursorPixelCoordToGlobal
virtual void moveMouseCursorPixelCoordToGlobal(QPointF local_coordinates)
Definition: baseplotwidget.cpp:1707
pappso::BasePlotWidget::mp_hPosTracerItem
QCPItemLine * mp_hPosTracerItem
Horizontal position tracer.
Definition: baseplotwidget.h:274
pappso::BasePlotWidget::mp_vPosTracerItem
QCPItemLine * mp_vPosTracerItem
Vertical position tracer.
Definition: baseplotwidget.h:277
pappso::BasePlotWidget::setupWidget
virtual bool setupWidget()
Definition: baseplotwidget.cpp:343
pappso::BasePlotWidget::setPen
virtual void setPen(const QPen &pen)
Definition: baseplotwidget.cpp:398
pappso::BasePlotWidget::mouseReleaseHandlerRightButton
virtual void mouseReleaseHandlerRightButton()
Definition: baseplotwidget.cpp:1522
pappso::BasePlotWidget::getInnermostRangeX
virtual QCPRange getInnermostRangeX(bool &found_range) const
Definition: baseplotwidget.cpp:1900
pappso::BasePlotWidget::mouseMoveHandlerNotDraggingCursor
virtual void mouseMoveHandlerNotDraggingCursor()
Definition: baseplotwidget.cpp:939
pappso::BasePlotWidget::redrawPlotBackground
virtual void redrawPlotBackground(QWidget *focusedPlotWidget)
Redraw the background of the focusedPlotWidget plot widget.
Definition: baseplotwidget.cpp:3113
pappso::BasePlotWidget::isClickOntoXAxis
bool isClickOntoXAxis(const QPointF &mousePoint)
Definition: baseplotwidget.cpp:1609
pappso::BasePlotWidget::setAxisLabelY
virtual void setAxisLabelY(const QString &label)
Definition: baseplotwidget.cpp:474
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:560
pappso::BasePlotWidget::spaceKeyReleaseEvent
virtual void spaceKeyReleaseEvent(QKeyEvent *event)
Definition: baseplotwidget.cpp:769
pappso::BasePlotWidget::setFocusSignal
void setFocusSignal()
pappso::BasePlotWidget::replotWithAxisRangeX
virtual void replotWithAxisRangeX(double lower, double upper)
Definition: baseplotwidget.cpp:2141
pappso::BasePlotWidget::createAllAncillaryItems
virtual void createAllAncillaryItems()
Definition: baseplotwidget.cpp:221
pappso::BasePlotWidget::getPlottingColor
virtual QColor getPlottingColor(QCPAbstractPlottable *plottable_p) const
Definition: baseplotwidget.cpp:445
pappso::BasePlotWidget::mouseReleaseHandlerLeftButton
virtual void mouseReleaseHandlerLeftButton()
Definition: baseplotwidget.cpp:1437
pappso::BasePlotWidget::m_rightMousePseudoButtonKey
int m_rightMousePseudoButtonKey
Definition: baseplotwidget.h:255
pappso::BasePlotWidget::m_focusedBrush
QBrush m_focusedBrush
Color used for the background of focused plot.
Definition: baseplotwidget.h:333
pappso::BasePlotWidget::m_pen
QPen m_pen
Pen used to draw the graph and textual elements in the plot widget.
Definition: baseplotwidget.h:336
pappso::BasePlotWidget::isSelectionRectangleVisible
virtual bool isSelectionRectangleVisible()
Definition: baseplotwidget.cpp:3081
pappso::BasePlotWidget::drawYDeltaFeatures
virtual void drawYDeltaFeatures()
Definition: baseplotwidget.cpp:2340
pappso::BasePlotWidget::isVerticalDisplacementAboveThreshold
virtual bool isVerticalDisplacementAboveThreshold()
Definition: baseplotwidget.cpp:2422
pappso::BasePlotWidget::m_leftMousePseudoButtonKey
int m_leftMousePseudoButtonKey
Definition: baseplotwidget.h:254
pappso::BasePlotWidget::mousePressHandler
virtual void mousePressHandler(QMouseEvent *event)
KEYBOARD-related EVENTS.
Definition: baseplotwidget.cpp:1234
pappso::BasePlotWidget::verticalMoveMouseCursorCountPixels
virtual void verticalMoveMouseCursorCountPixels(int pixel_count)
Definition: baseplotwidget.cpp:1747
pappso::BasePlotWidget::axisReframe
virtual void axisReframe()
Definition: baseplotwidget.cpp:2020
pappso::BasePlotWidget::resetAxesRangeHistory
virtual void resetAxesRangeHistory()
Definition: baseplotwidget.cpp:482
pappso::BasePlotWidget::showTracers
virtual void showTracers()
Show the traces (vertical and horizontal).
Definition: baseplotwidget.cpp:2184
pappso::BasePlotWidget::horizontalGetGraphCoordNewPointCountPixels
virtual QPointF horizontalGetGraphCoordNewPointCountPixels(int pixel_count)
Definition: baseplotwidget.cpp:1731
pappso::BasePlotWidget::mp_selectionRectangeLine4
QCPItemLine * mp_selectionRectangeLine4
Definition: baseplotwidget.h:264
pappso::BasePlotWidget::horizontalMoveMouseCursorCountPixels
virtual void horizontalMoveMouseCursorCountPixels(int pixel_count)
Definition: baseplotwidget.cpp:1715
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:300
pappso::BasePlotWidget::getInnermostRangeY
virtual QCPRange getInnermostRangeY(bool &found_range) const
Definition: baseplotwidget.cpp:1915
pappso::BasePlotWidget::setFocus
virtual void setFocus()
PLOT ITEMS : TRACER TEXT ITEMS...
Definition: baseplotwidget.cpp:3099
pappso::BasePlotWidget::keyReleaseEventSignal
void keyReleaseEventSignal(const BasePlotContext &context)
pappso::BasePlotWidget::getPen
virtual const QPen & getPen() const
Definition: baseplotwidget.cpp:405
pappso::BasePlotWidget::updateContextXandYAxisRanges
virtual void updateContextXandYAxisRanges()
Definition: baseplotwidget.cpp:3139
pappso::BasePlotWidget::update1DSelectionRectangle
virtual void update1DSelectionRectangle(bool for_integration=false)
Definition: baseplotwidget.cpp:2444
pappso::BasePlotWidget::whatIsVisibleOfTheSelectionRectangle
virtual PolygonType whatIsVisibleOfTheSelectionRectangle()
Definition: baseplotwidget.cpp:3046
pappso::BasePlotWidget::replotWithAxesRanges
virtual void replotWithAxesRanges(QCPRange xAxisRange, QCPRange yAxisRange, Axis whichAxis)
Definition: baseplotwidget.cpp:2117
pappso::BasePlotWidget::mousePseudoButtonKeyPressEvent
virtual void mousePseudoButtonKeyPressEvent(QKeyEvent *event)
Definition: baseplotwidget.cpp:827
pappso::BasePlotWidget::setPlottingColor
virtual void setPlottingColor(QCPAbstractPlottable *plottable_p, const QColor &new_color)
Definition: baseplotwidget.cpp:412
pappso::BasePlotWidget::calculateDragDeltas
virtual void calculateDragDeltas()
Definition: baseplotwidget.cpp:2398
pappso::BasePlotWidget::verticalGetGraphCoordNewPointCountPixels
virtual QPointF verticalGetGraphCoordNewPointCountPixels(int pixel_count)
Definition: baseplotwidget.cpp:1764
pappso::BasePlotWidget::plotRangesChangedSignal
void plotRangesChangedSignal(const BasePlotContext &context)
pappso::BasePlotWidget::mp_vStartTracerItem
QCPItemLine * mp_vStartTracerItem
Vertical selection start tracer (typically in green).
Definition: baseplotwidget.h:280
pappso::BasePlotWidget::mouseReleaseHandler
virtual void mouseReleaseHandler(QMouseEvent *event)
Definition: baseplotwidget.cpp:1339
pappso::BasePlotWidget::m_unfocusedBrush
QBrush m_unfocusedBrush
Color used for the background of unfocused plot.
Definition: baseplotwidget.h:328
pappso::BasePlotWidget::drawXDeltaFeatures
virtual void drawXDeltaFeatures()
Definition: baseplotwidget.cpp:2249
pappso::BasePlotWidget::axisRescale
virtual void axisRescale()
RANGE-related functions.
Definition: baseplotwidget.cpp:1935
pappso::BasePlotWidget::moveMouseCursorGraphCoordToGlobal
virtual void moveMouseCursorGraphCoordToGlobal(QPointF plot_coordinates)
Definition: baseplotwidget.cpp:1695
pappso::BasePlotWidget::allLayerNamesToString
virtual QString allLayerNamesToString() const
Definition: baseplotwidget.cpp:175
pappso::BasePlotWidget::mp_selectionRectangeLine3
QCPItemLine * mp_selectionRectangeLine3
Definition: baseplotwidget.h:263
pappso::BasePlotWidget::axisDoubleClickHandler
virtual void axisDoubleClickHandler(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event)
Definition: baseplotwidget.cpp:1561
pappso::BasePlotWidget::mouseMoveHandlerRightButtonDraggingCursor
virtual void mouseMoveHandlerRightButtonDraggingCursor()
Definition: baseplotwidget.cpp:1186
pappso::BasePlotWidget::mp_vEndTracerItem
QCPItemLine * mp_vEndTracerItem
Vertical selection end tracer (typically in red).
Definition: baseplotwidget.h:283
pappso::BasePlotWidget::mouseMoveHandler
virtual void mouseMoveHandler(QMouseEvent *event)
KEYBOARD-related EVENTS.
Definition: baseplotwidget.cpp:902
pappso::BasePlotWidget::directionKeyPressEvent
virtual void directionKeyPressEvent(QKeyEvent *event)
Definition: baseplotwidget.cpp:776
pappso::BasePlotWidget::layerableLayerName
virtual QString layerableLayerName(QCPLayerable *layerable_p) const
Definition: baseplotwidget.cpp:191
pappso::BasePlotWidget::keyReleaseEvent
virtual void keyReleaseEvent(QKeyEvent *event)
Handle specific key codes and trigger respective actions.
Definition: baseplotwidget.cpp:637
pappso::BasePlotWidget::resetSelectionRectangle
virtual void resetSelectionRectangle()
Definition: baseplotwidget.cpp:3039
pappso::BasePlotWidget::restorePreviousAxesRangeHistory
virtual void restorePreviousAxesRangeHistory()
Go up one history element in the axis history.
Definition: baseplotwidget.cpp:532
pappso::BasePlotWidget::layerableLayerIndex
virtual int layerableLayerIndex(QCPLayerable *layerable_p) const
Definition: baseplotwidget.cpp:203
pappso::BasePlotWidget::integrationRequestedSignal
void integrationRequestedSignal(const BasePlotContext &context)
pappso::BasePlotWidget::xAxisMeasurementSignal
void xAxisMeasurementSignal(const BasePlotContext &context, bool with_delta)
pappso::BasePlotWidget::getRange
QCPRange getRange(Axis axis, RangeType range_type, bool &found_range) const
Definition: baseplotwidget.cpp:1808
pappso::BasePlotWidget::replotWithAxisRangeY
virtual void replotWithAxisRangeY(double lower, double upper)
Definition: baseplotwidget.cpp:2152
pappso::BasePlotWidget::hideTracers
virtual void hideTracers()
Hide the traces (vertical and horizontal).
Definition: baseplotwidget.cpp:2202
pappso::BasePlotWidget::update2DSelectionRectangleSkewed
virtual void update2DSelectionRectangleSkewed(bool for_integration=false)
Definition: baseplotwidget.cpp:2697
pappso::BasePlotWidget::mousePseudoButtonKeyReleaseEvent
virtual void mousePseudoButtonKeyReleaseEvent(QKeyEvent *event)
Definition: baseplotwidget.cpp:835
pappso::BasePlotWidget::hideAllPlotItems
virtual void hideAllPlotItems()
PLOTTING / REPLOTTING functions.
Definition: baseplotwidget.cpp:2168
pappso::BasePlotWidget::getRangeX
virtual QCPRange getRangeX(bool &found_range, int index) const
MOUSE MOVEMENTS mouse/keyboard-triggered.
Definition: baseplotwidget.cpp:1784
pappso::BasePlotWidget::m_xAxisRangeHistory
std::vector< QCPRange * > m_xAxisRangeHistory
List of x axis ranges occurring during the panning zooming actions.
Definition: baseplotwidget.h:297
pappso::BasePlotWidget::m_context
BasePlotContext m_context
Definition: baseplotwidget.h:252
pappso::ExceptionNotPossible
Definition: exceptionnotpossible.h:29
pappso::SelectionPolygon::setPoint
void setPoint(PointSpecs point_spec, double x, double y)
Definition: selectionpolygon.cpp:116
pappso::SelectionPolygon::is1D
bool is1D() const
Definition: selectionpolygon.cpp:631
pappso::SelectionPolygon::getTopMostPoint
QPointF getTopMostPoint() const
Definition: selectionpolygon.cpp:266
pappso::SelectionPolygon::getRightMostPoint
QPointF getRightMostPoint() const
Definition: selectionpolygon.cpp:246
pappso::SelectionPolygon::getLeftMostPoint
QPointF getLeftMostPoint() const
Definition: selectionpolygon.cpp:226
pappso::SelectionPolygon::getBottomMostPoint
QPointF getBottomMostPoint() const
Definition: selectionpolygon.cpp:286
pappso::SelectionPolygon::set1D
void set1D(double x_range_start, double x_range_end)
Definition: selectionpolygon.cpp:146
pappso::SelectionPolygon::resetPoints
void resetPoints()
Definition: selectionpolygon.cpp:614
pappso::SelectionPolygon::getPoint
QPointF getPoint(PointSpecs point_spec) const
Definition: selectionpolygon.cpp:313
pappso
tries to keep as much as possible monoisotopes, removing any possible C13 peaks and changes multichar...
Definition: aa.cpp:39
pappso::Axis
Axis
Definition: types.h:180
pappso::Axis::y
@ y
pappso::Axis::x
@ x
pappso::PointSpecs::BOTTOM_RIGHT_POINT
@ BOTTOM_RIGHT_POINT
pappso::PointSpecs::TOP_LEFT_POINT
@ TOP_LEFT_POINT
pappso::PointSpecs::BOTTOM_LEFT_POINT
@ BOTTOM_LEFT_POINT
pappso::PointSpecs::TOP_RIGHT_POINT
@ TOP_RIGHT_POINT
pappso::DragDirections::TOP_TO_BOTTOM
@ TOP_TO_BOTTOM
pappso::RangeType
RangeType
Definition: baseplotwidget.h:56
pappso::RangeType::innermost
@ innermost
pappso::RangeType::outermost
@ outermost
pappso::PolygonType
PolygonType
Definition: selectionpolygon.h:43
pappso::PolygonType::RIGHT_LINE
@ RIGHT_LINE
pappso::PolygonType::NOT_SET
@ NOT_SET
pappso::PolygonType::FULL_POLYGON
@ FULL_POLYGON
pappso::PolygonType::TOP_LINE
@ TOP_LINE
pappso::PolygonType::BOTTOM_LINE
@ BOTTOM_LINE
pappso::PolygonType::LEFT_LINE
@ LEFT_LINE