libpappsomspp
Library for mass spectrometry
baseplotwidget.cpp
Go to the documentation of this file.
1 /* This code comes right from the msXpertSuite software project.
2  *
3  * msXpertSuite - mass spectrometry software suite
4  * -----------------------------------------------
5  * Copyright(C) 2009,...,2018 Filippo Rusconi
6  *
7  * http://www.msxpertsuite.org
8  *
9  * This program is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program. If not, see <http://www.gnu.org/licenses/>.
21  *
22  * END software license
23  */
24 
25 
26 /////////////////////// StdLib includes
27 #include <vector>
28 
29 
30 /////////////////////// Qt includes
31 #include <QVector>
32 
33 
34 /////////////////////// Local includes
35 #include "baseplotwidget.h"
36 #include "../../pappsoexception.h"
37 #include "../../exception/exceptionnotpossible.h"
38 
39 
41  qRegisterMetaType<pappso::BasePlotContext>("pappso::BasePlotContext");
43  qRegisterMetaType<pappso::BasePlotContext *>("pappso::BasePlotContext *");
44 
45 
46 namespace pappso
47 {
48 
49 QString
51 {
52  QString text("Context:");
53 
54  text += QString("data kind: %1").arg(static_cast<int>(dataKind));
55 
56  text += QString(" isMouseDragging: %1 -- wasMouseDragging: %2")
57  .arg(isMouseDragging ? "true" : "false")
58  .arg(wasMouseDragging ? "true" : "false");
59 
60  text += QString(" -- startDragPoint : (%1, %2)")
61  .arg(startDragPoint.x())
62  .arg(startDragPoint.y());
63 
64  text += QString(" -- currentDragPoint : (%1, %2)")
65  .arg(currentDragPoint.x())
66  .arg(currentDragPoint.y());
67 
68  text += QString(" -- lastCursorHoveredPoint : (%1, %2)")
69  .arg(lastCursorHoveredPoint.x())
70  .arg(lastCursorHoveredPoint.y());
71 
72  text += QString(" -- xRange: (%1, %2)").arg(xRange.lower).arg(xRange.upper);
73 
74  text += QString(" -- yRange: (%1, %2)").arg(yRange.lower).arg(yRange.upper);
75 
76  text +=
77  QString(" -- wasClickOnXAxis: %1").arg(wasClickOnXAxis ? "true" : "false");
78  text +=
79  QString(" -- wasClickOnYAxis: %1").arg(wasClickOnYAxis ? "true" : "false");
80  text += QString(" -- isMeasuringDistance: %1")
81  .arg(isMeasuringDistance ? "true" : "false");
82 
83  text += QString(" -- xRegionRangeStart: %1 -- xRegionRangeEnd: %2")
84  .arg(xRegionRangeStart)
85  .arg(xRegionRangeEnd);
86 
87  text += QString(" -- yRegionRangeStart: %1 -- yRegionRangeEnd: %2")
88  .arg(yRegionRangeStart)
89  .arg(yRegionRangeEnd);
90 
91  text += QString(" -- xDelta: %1 -- yDelta: %2").arg(xDelta).arg(yDelta);
92 
93  text += QString(" -- pressedKeyCode: %1").arg(pressedKeyCode);
94 
95  text += QString(" -- keyboardModifiers: %1").arg(keyboardModifiers);
96 
97  text += QString(" -- lastPressedMouseButton: %1").arg(lastPressedMouseButton);
98 
99  text +=
100  QString(" -- lastReleasedMouseButton: %1").arg(lastReleasedMouseButton);
101 
102  text += QString(" -- pressedMouseButtons: %1").arg(pressedMouseButtons);
103 
104  text +=
105  QString(" -- mouseButtonsAtMousePress: %1").arg(mouseButtonsAtMousePress);
106 
107  text += QString(" -- mouseButtonsAtMouseRelease: %1")
109 
110  return text;
111 }
112 
113 
114 BasePlotWidget::BasePlotWidget(QWidget *parent) : QCustomPlot(parent)
115 {
116  // qDebug();
117 
118  if(parent == nullptr)
119  qFatal("Programming error.");
120 
121  // Default settings for the pen used to graph the data.
122  m_pen.setStyle(Qt::SolidLine);
123  m_pen.setBrush(Qt::black);
124  m_pen.setWidth(1);
125 
126  if(!setupWidget())
127  qFatal("Programming error.");
128 
129  show();
130 }
131 
132 
134  const QString &x_axis_label,
135  const QString &y_axis_label)
136  : QCustomPlot(parent), m_axisLabelX(x_axis_label), m_axisLabelY(y_axis_label)
137 {
138  // qDebug();
139 
140  if(parent == nullptr)
141  qFatal("Programming error.");
142 
143  // Default settings for the pen used to graph the data.
144  m_pen.setStyle(Qt::SolidLine);
145  m_pen.setBrush(Qt::black);
146  m_pen.setWidth(1);
147 
148  xAxis->setLabel(x_axis_label);
149  yAxis->setLabel(y_axis_label);
150 
151  if(!setupWidget())
152  qFatal("Programming error.");
153 
154  show();
155 }
156 
157 
158 //! Destruct \c this BasePlotWidget instance.
159 /*!
160 
161  The destruction involves clearing the history, deleting all the axis range
162  history items for x and y axes.
163 
164 */
166 {
167  // qDebug() << "In the destructor of plot widget:" << this;
168 
169  m_xAxisRangeHistory.clear();
170  m_yAxisRangeHistory.clear();
171 }
172 
173 
174 bool
176 {
177  // qDebug();
178 
179  // By default the widget comes with a graph. Remove it.
180 
181  if(graphCount())
182  removeGraph(0);
183 
184  // This is required so that we get the keyboard events.
185  setFocusPolicy(Qt::StrongFocus);
186  setInteractions(QCP::iRangeZoom | QCP::iSelectPlottables | QCP::iMultiSelect);
187 
188  // Make a copy of the pen to just change its color and set that color to
189  // the tracer line.
190  QPen pen = m_pen;
191 
192  // Create the lines that will act as tracers for position and selection of
193  // regions.
194  //
195  // We have the cross hair that serves as the cursor. That crosshair cursor is
196  // made of a vertical line (green, because when click-dragging the mouse it
197  // becomes the tracer that is being anchored at the region start. The second
198  // line i horizontal and is always black.
199 
200  pen.setColor(QColor("black"));
201 
202  // The set of tracers (horizontal and vertical) that track the position of the
203  // mouse cursor.
204  mp_vPosTracerItem = new QCPItemLine(this);
205  mp_vPosTracerItem->setPen(m_pen);
206  mp_vPosTracerItem->start->setType(QCPItemPosition::ptPlotCoords);
207  mp_vPosTracerItem->end->setType(QCPItemPosition::ptPlotCoords);
208  mp_vPosTracerItem->start->setCoords(0, 0);
209  mp_vPosTracerItem->end->setCoords(0, 0);
210 
211  mp_hPosTracerItem = new QCPItemLine(this);
212  mp_hPosTracerItem->setPen(m_pen);
213  mp_hPosTracerItem->start->setType(QCPItemPosition::ptPlotCoords);
214  mp_hPosTracerItem->end->setType(QCPItemPosition::ptPlotCoords);
215  mp_hPosTracerItem->start->setCoords(0, 0);
216  mp_hPosTracerItem->end->setCoords(0, 0);
217 
218  // The set of tracers (horizontal only) that track the region
219  // spanning/selection regions.
220  //
221  // The start vertical tracer is colored in greeen.
222  pen.setColor(QColor("green"));
223 
224  mp_vStartTracerItem = new QCPItemLine(this);
225  mp_vStartTracerItem->setPen(pen);
226  mp_vStartTracerItem->start->setType(QCPItemPosition::ptPlotCoords);
227  mp_vStartTracerItem->end->setType(QCPItemPosition::ptPlotCoords);
228  mp_vStartTracerItem->start->setCoords(0, 0);
229  mp_vStartTracerItem->end->setCoords(0, 0);
230 
231  // The end vertical tracer is colored in red.
232  pen.setColor(QColor("red"));
233 
234  mp_vEndTracerItem = new QCPItemLine(this);
235  mp_vEndTracerItem->setPen(pen);
236  mp_vEndTracerItem->start->setType(QCPItemPosition::ptPlotCoords);
237  mp_vEndTracerItem->end->setType(QCPItemPosition::ptPlotCoords);
238  mp_vEndTracerItem->start->setCoords(0, 0);
239  mp_vEndTracerItem->end->setCoords(0, 0);
240 
241  mp_zoomRectItem = new QCPItemRect(this);
242  mp_zoomRectItem->setPen(m_pen);
243  mp_zoomRectItem->topLeft->setType(QCPItemPosition::ptPlotCoords);
244  mp_zoomRectItem->bottomRight->setType(QCPItemPosition::ptPlotCoords);
245  mp_zoomRectItem->setVisible(false);
246 
247  mp_selectLineItem = new QCPItemLine(this);
248  mp_selectLineItem->setPen(m_pen);
249  mp_selectLineItem->start->setType(QCPItemPosition::ptPlotCoords);
250  mp_selectLineItem->end->setType(QCPItemPosition::ptPlotCoords);
251  mp_selectLineItem->setVisible(false);
252 
253  // When the user click-drags the mouse, the X distance between the drag start
254  // point and the drag end point (current point) is the xDelta.
255  mp_xDeltaTextItem = new QCPItemText(this);
256  mp_xDeltaTextItem->setPositionAlignment(Qt::AlignBottom | Qt::AlignCenter);
257  mp_xDeltaTextItem->position->setType(QCPItemPosition::ptPlotCoords);
258  mp_xDeltaTextItem->setVisible(false);
259 
260  // We want to capture the signals emitted by the QCustomPlot base class.
261  connect(
262  this, &QCustomPlot::mouseMove, this, &BasePlotWidget::mouseMoveHandler);
263 
264  connect(
265  this, &QCustomPlot::mousePress, this, &BasePlotWidget::mousePressHandler);
266 
267  connect(this,
268  &QCustomPlot::mouseRelease,
269  this,
271 
272  connect(this,
273  &QCustomPlot::axisDoubleClick,
274  this,
276 
277  return true;
278 }
279 
280 
281 void
282 BasePlotWidget::setPen(const QPen &pen)
283 {
284  m_pen = pen;
285 }
286 
287 
288 const QPen &
290 {
291  return m_pen;
292 }
293 
294 
295 void
296 BasePlotWidget::setPlottingColor(QCPAbstractPlottable *plottable_p,
297  const QColor &new_color)
298 {
299  if(plottable_p == nullptr)
300  qFatal("Pointer cannot be nullptr.");
301 
302  // First this single-graph widget
303  QPen pen;
304 
305  pen = plottable_p->pen();
306  pen.setColor(new_color);
307  plottable_p->setPen(pen);
308 
309  replot();
310 }
311 
312 
313 void
314 BasePlotWidget::setPlottingColor(int index, const QColor &new_color)
315 {
316  if(!new_color.isValid())
317  return;
318 
319  QCPGraph *graph_p = graph(index);
320 
321  if(graph_p == nullptr)
322  qFatal("Programming error.");
323 
324  return setPlottingColor(graph_p, new_color);
325 }
326 
327 
328 QColor
329 BasePlotWidget::getPlottingColor(QCPAbstractPlottable *plottable_p) const
330 {
331  if(plottable_p == nullptr)
332  qFatal("Programming error.");
333 
334  return plottable_p->pen().color();
335 }
336 
337 
338 QColor
340 {
341  QCPGraph *graph_p = graph(index);
342 
343  if(graph_p == nullptr)
344  qFatal("Programming error.");
345 
346  return getPlottingColor(graph_p);
347 }
348 
349 
350 void
351 BasePlotWidget::setAxisLabelX(const QString &label)
352 {
353  xAxis->setLabel(label);
354 }
355 
356 
357 void
358 BasePlotWidget::setAxisLabelY(const QString &label)
359 {
360  yAxis->setLabel(label);
361 }
362 
363 
364 // AXES RANGE HISTORY-related functions
365 void
367 {
368  m_xAxisRangeHistory.clear();
369  m_yAxisRangeHistory.clear();
370 
371  m_xAxisRangeHistory.push_back(new QCPRange(xAxis->range()));
372  m_yAxisRangeHistory.push_back(new QCPRange(yAxis->range()));
373 
374  // qDebug() << "size of history:" << m_xAxisRangeHistory.size()
375  //<< "setting index to 0";
376 
377  // qDebug() << "resetting axes history to values:" << xAxis->range().lower
378  //<< "--" << xAxis->range().upper << "and" << yAxis->range().lower
379  //<< "--" << yAxis->range().upper;
380 
382 }
383 
384 
385 //! Create new axis range history items and append them to the history.
386 /*!
387 
388  The plot widget is queried to get the current x/y-axis ranges and the
389  current ranges are appended to the history for x-axis and for y-axis.
390 
391 */
392 void
394 {
395  m_xAxisRangeHistory.push_back(new QCPRange(xAxis->range()));
396  m_yAxisRangeHistory.push_back(new QCPRange(yAxis->range()));
397 
399 
400  // qDebug() << "axes history size:" << m_xAxisRangeHistory.size()
401  //<< "current index:" << m_lastAxisRangeHistoryIndex
402  //<< xAxis->range().lower << "--" << xAxis->range().upper
403  //<< "and"
404  //<< yAxis->range().lower << "--" << yAxis->range().upper;
405 }
406 
407 
408 //! Go up one history element in the axis history.
409 /*!
410 
411  If possible, back up one history item in the axis histories and update the
412  plot's x/y-axis ranges to match that history item.
413 
414 */
415 void
417 {
418  // qDebug() << "axes history size:" << m_xAxisRangeHistory.size()
419  //<< "current index:" << m_lastAxisRangeHistoryIndex;
420 
422  {
423  // qDebug() << "current index is 0 returning doing nothing";
424 
425  return;
426  }
427 
428  // qDebug() << "setting index to:" << m_lastAxisRangeHistoryIndex - 1
429  //<< "and restoring axes history to that index";
430 
432 }
433 
434 
435 //! Get the axis histories at index \p index and update the plot ranges.
436 /*!
437 
438  \param index index at which to select the axis history item.
439 
440  \sa updateAxesRangeHistory().
441 
442 */
443 void
445 {
446  // qDebug() << "axes history size:" << m_xAxisRangeHistory.size()
447  //<< "current index:" << m_lastAxisRangeHistoryIndex
448  //<< "asking to restore index:" << index;
449 
450  if(index >= m_xAxisRangeHistory.size())
451  {
452  // qDebug() << "index >= history size. Returning.";
453  return;
454  }
455 
456  xAxis->setRange(*(m_xAxisRangeHistory.at(index)));
457  yAxis->setRange(*(m_yAxisRangeHistory.at(index)));
458 
460 
461  mp_vPosTracerItem->setVisible(false);
462  mp_hPosTracerItem->setVisible(false);
463 
464  mp_vStartTracerItem->setVisible(false);
465  mp_vEndTracerItem->setVisible(false);
466 
467 
468  // The start trace will keep beeing represented at the last position and last
469  // size even if we call this function repetitively. So actually do not show,
470  // it will reappare as soon as the mouse is moved.
471  // if(m_shouldTracersBeVisible)
472  //{
473  // mp_vStartTracerItem->setVisible(true);
474  //}
475 
476  replot();
477 
479 
480  // qDebug() << "restored axes history to index:" << index
481  //<< "with values:" << xAxis->range().lower << "--"
482  //<< xAxis->range().upper << "and" << yAxis->range().lower << "--"
483  //<< yAxis->range().upper;
484 
486 }
487 // AXES RANGE HISTORY-related functions
488 
489 
490 /// KEYBOARD-related EVENTS
491 void
493 {
494  // qDebug() << "ENTER";
495 
496  // We need this because some keys modify our behaviour.
497  m_context.pressedKeyCode = event->key();
498  m_context.keyboardModifiers = QGuiApplication::queryKeyboardModifiers();
499 
500  if(event->key() == Qt::Key_Left || event->key() == Qt::Key_Right ||
501  event->key() == Qt::Key_Up || event->key() == Qt::Key_Down)
502  {
503  return directionKeyPressEvent(event);
504  }
505  else if(event->key() == m_leftMousePseudoButtonKey ||
506  event->key() == m_rightMousePseudoButtonKey)
507  {
508  return mousePseudoButtonKeyPressEvent(event);
509  }
510 
511  // Do not do anything here, because this function is used by derived classes
512  // that will emit the signal below. Otherwise there are going to be multiple
513  // signals sent.
514  // qDebug() << "Going to emit keyPressEventSignal(m_context);";
515  // emit keyPressEventSignal(m_context);
516 }
517 
518 
519 //! Handle specific key codes and trigger respective actions.
520 void
522 {
523  m_context.releasedKeyCode = event->key();
524 
525  // The keyboard key is being released, set the key code to 0.
527  m_context.keyboardModifiers = QGuiApplication::queryKeyboardModifiers();
528 
529  // Now test if the key that was released is one of the housekeeping keys.
530  if(event->key() == Qt::Key_Backspace)
531  {
532  // The user wants to iterate back in the x/y axis range history.
534 
535  event->accept();
536  }
537  else if(event->key() == Qt::Key_Space)
538  {
539  return spaceKeyReleaseEvent(event);
540  }
541  else if(event->key() == Qt::Key_Delete)
542  {
543  // The user wants to delete a graph. What graph is to be determined
544  // programmatically:
545 
546  // If there is a single graph, then that is the graph to be removed.
547  // If there are more than one graph, then only the ones that are selected
548  // are to be removed.
549 
550  // Note that the user of this widget might want to provide the user with
551  // the ability to specify if all the children graph needs to be removed
552  // also. This can be coded in key modifiers. So provide the context.
553 
554  int graph_count = plottableCount();
555 
556  if(!graph_count)
557  {
558  // qDebug() << "Not a single graph in the plot widget. Doing
559  // nothing.";
560 
561  event->accept();
562  return;
563  }
564 
565  if(graph_count == 1)
566  {
567  // qDebug() << "A single graph is in the plot widget. Emitting a graph
568  // " "destruction requested signal for it:"
569  //<< graph();
570 
571  emit plottableDestructionRequestedSignal(this, graph(), m_context);
572  }
573  else
574  {
575  // At this point we know there are more than one graph in the plot
576  // widget. We need to get the selected one (if any).
577  QList<QCPGraph *> selected_graph_list;
578 
579  selected_graph_list = selectedGraphs();
580 
581  if(!selected_graph_list.size())
582  {
583  event->accept();
584  return;
585  }
586 
587  // qDebug() << "Number of selected graphs to be destrobyed:"
588  //<< selected_graph_list.size();
589 
590  for(int iter = 0; iter < selected_graph_list.size(); ++iter)
591  {
592  // qDebug()
593  //<< "Emitting a graph destruction requested signal for graph:"
594  //<< selected_graph_list.at(iter);
595 
597  this, selected_graph_list.at(iter), m_context);
598 
599  // We do not do this, because we want the slot called by the
600  // signal above to handle that removal. Remember that it is not
601  // possible to delete graphs manually.
602  //
603  // removeGraph(selected_graph_list.at(iter));
604  }
605  event->accept();
606  }
607  }
608  // End of
609  // else if(event->key() == Qt::Key_Delete)
610  else if(event->key() == Qt::Key_T)
611  {
612  // The user wants to toggle the visibiity of the tracers.
614 
616  hideTracers();
617  else
618  showTracers();
619 
620  event->accept();
621  }
622  else if(event->key() == Qt::Key_Left || event->key() == Qt::Key_Right ||
623  event->key() == Qt::Key_Up || event->key() == Qt::Key_Down)
624  {
625  return directionKeyReleaseEvent(event);
626  }
627  else if(event->key() == m_leftMousePseudoButtonKey ||
628  event->key() == m_rightMousePseudoButtonKey)
629  {
630  return mousePseudoButtonKeyReleaseEvent(event);
631  }
632 
633  // At this point emit the signal, since we did not treat it. Maybe the
634  // consumer widget wants to know that the keyboard key was released.
635 
637 }
638 
639 
640 void
641 BasePlotWidget::spaceKeyReleaseEvent([[maybe_unused]] QKeyEvent *event)
642 {
643  // qDebug();
644 }
645 
646 
647 void
649 {
650  // qDebug() << "event key:" << event->key();
651 
652  // The user is trying to move the positional cursor/markers. There are
653  // multiple way they can do that:
654  //
655  // 1.a. Hitting the arrow left/right keys alone will search for next pixel.
656  // 1.b. Hitting the arrow left/right keys with Alt modifier will search for a
657  // multiple of pixels that might be equivalent to one 20th of the pixel width
658  // of the plot widget.
659  // 1.c Hitting the left/right keys with Alt and Shift modifiers will search
660  // for a multiple of pixels that might be the equivalent to half of the pixel
661  // width.
662  //
663  // 2. Hitting the Control modifier will move the cursor to the next data point
664  // of the graph.
665 
666  int pixel_increment = 0;
667 
668  if(m_context.keyboardModifiers == Qt::NoModifier)
669  pixel_increment = 1;
670  else if(m_context.keyboardModifiers == Qt::AltModifier)
671  pixel_increment = 50;
672 
673  // The user is moving the positional markers. This is equivalent to a
674  // non-dragging cursor movement to the next pixel. Note that the origin is
675  // located at the top left, so key down increments and key up decrements.
676 
677  if(event->key() == Qt::Key_Left)
678  horizontalMoveMouseCursorCountPixels(-pixel_increment);
679  else if(event->key() == Qt::Key_Right)
680  horizontalMoveMouseCursorCountPixels(pixel_increment);
681  else if(event->key() == Qt::Key_Up)
682  verticalMoveMouseCursorCountPixels(-pixel_increment);
683  else if(event->key() == Qt::Key_Down)
684  verticalMoveMouseCursorCountPixels(pixel_increment);
685 
686  event->accept();
687 }
688 
689 
690 void
692 {
693  // qDebug() << "event key:" << event->key();
694  event->accept();
695 }
696 
697 
698 void
700  [maybe_unused]] QKeyEvent *event)
701 {
702  // qDebug();
703 }
704 
705 
706 void
708 {
709 
710  QPointF pixel_coordinates(
711  xAxis->coordToPixel(m_context.lastCursorHoveredPoint.x()),
712  yAxis->coordToPixel(m_context.lastCursorHoveredPoint.y()));
713 
714  Qt::MouseButton button = Qt::NoButton;
715  QEvent::Type q_event_type = QEvent::MouseButtonPress;
716 
717  if(event->key() == m_leftMousePseudoButtonKey)
718  {
719  // Toggles the left mouse button on/off
720 
721  button = Qt::LeftButton;
722 
725 
727  q_event_type = QEvent::MouseButtonPress;
728  else
729  q_event_type = QEvent::MouseButtonRelease;
730  }
731  else if(event->key() == m_rightMousePseudoButtonKey)
732  {
733  // Toggles the right mouse button.
734 
735  button = Qt::RightButton;
736 
739 
741  q_event_type = QEvent::MouseButtonPress;
742  else
743  q_event_type = QEvent::MouseButtonRelease;
744  }
745 
746  // qDebug() << "pressed/released pseudo button:" << button
747  //<< "q_event_type:" << q_event_type;
748 
749  // Synthesize a QMouseEvent and use it.
750 
751  QMouseEvent *mouse_event_p =
752  new QMouseEvent(q_event_type,
753  pixel_coordinates,
754  mapToGlobal(pixel_coordinates.toPoint()),
755  mapToGlobal(pixel_coordinates.toPoint()),
756  button,
757  button,
759  Qt::MouseEventSynthesizedByApplication);
760 
761  if(q_event_type == QEvent::MouseButtonPress)
762  mousePressHandler(mouse_event_p);
763  else
764  mouseReleaseHandler(mouse_event_p);
765 
766  // event->accept();
767 }
768 /// KEYBOARD-related EVENTS
769 
770 
771 /// MOUSE-related EVENTS
772 
773 void
775 {
776  // When the user clicks this widget it has to take focus.
777  setFocus();
778 
779  QPointF mousePoint = event->localPos();
780 
781  m_context.lastPressedMouseButton = event->button();
782  m_context.mouseButtonsAtMousePress = event->buttons();
783 
784  // The pressedMouseButtons must continually inform on the status of pressed
785  // buttons so add the pressed button.
786  m_context.pressedMouseButtons |= event->button();
787 
788  // qDebug().noquote() << m_context.toString();
789 
790  // In all the processing of the events, we need to know if the user is
791  // clicking somewhere with the intent to change the plot ranges (reframing
792  // or rescaling the plot).
793  //
794  // Reframing the plot means that the new x and y axes ranges are modified so
795  // that they match the region that the user has encompassed by left clicking
796  // the mouse and dragging it over the plot. That is we reframe the plot so
797  // that it contains only the "selected" region.
798  //
799  // Rescaling the plot means the the new x|y axis range is modified such that
800  // the lower axis range is constant and the upper axis range is moved either
801  // left or right by the same amont as the x|y delta encompassed by the user
802  // moving the mouse. The axis is thus either compressed (mouse movement is
803  // leftwards) or un-compressed (mouse movement is rightwards).
804 
805  // There are two ways to perform axis range modifications:
806  //
807  // 1. By clicking on any of the axes
808  // 2. By clicking on the plot region but using keyboard key modifiers, like
809  // Alt and Ctrl.
810  //
811  // We need to know both cases separately which is why we need to perform a
812  // number of tests below.
813 
814  // Let's check if the click is on the axes, either X or Y, because that
815  // will allow us to take proper actions.
816 
817  if(isClickOntoXAxis(mousePoint))
818  {
819  // The X axis was clicked upon, we need to document that:
820  // qDebug() << __FILE__ << __LINE__
821  //<< "Layout element is axisRect and actually on an X axis part.";
822 
823  m_context.wasClickOnXAxis = true;
824 
825  // int currentInteractions = interactions();
826  // currentInteractions |= QCP::iRangeDrag;
827  // setInteractions((QCP::Interaction)currentInteractions);
828  // axisRect()->setRangeDrag(xAxis->orientation());
829  }
830  else
831  m_context.wasClickOnXAxis = false;
832 
833  if(isClickOntoYAxis(mousePoint))
834  {
835  // The Y axis was clicked upon, we need to document that:
836  // qDebug() << __FILE__ << __LINE__
837  //<< "Layout element is axisRect and actually on an Y axis part.";
838 
839  m_context.wasClickOnYAxis = true;
840 
841  // int currentInteractions = interactions();
842  // currentInteractions |= QCP::iRangeDrag;
843  // setInteractions((QCP::Interaction)currentInteractions);
844  // axisRect()->setRangeDrag(yAxis->orientation());
845  }
846  else
847  m_context.wasClickOnYAxis = false;
848 
849  // At this point, let's see if we need to remove the QCP::iRangeDrag bit:
850 
852  {
853  // qDebug() << __FILE__ << __LINE__
854  // << "Click outside of axes.";
855 
856  // int currentInteractions = interactions();
857  // currentInteractions = currentInteractions & ~QCP::iRangeDrag;
858  // setInteractions((QCP::Interaction)currentInteractions);
859  }
860 
861  m_context.startDragPoint.setX(xAxis->pixelToCoord(mousePoint.x()));
862  m_context.startDragPoint.setY(yAxis->pixelToCoord(mousePoint.y()));
863 
864  // Now install the vertical start tracer at the last cursor hovered
865  // position.
867  mp_vStartTracerItem->setVisible(true);
868 
870  yAxis->range().upper);
872  yAxis->range().lower);
873 
874  replot();
875 }
876 
877 
878 void
880 {
881  m_context.lastReleasedMouseButton = event->button();
882 
883  // The event->buttons() is the description of the buttons that are pressed at
884  // the moment the handler is invoked, that is now. If left and right were
885  // pressed, and left was released, event->buttons() would be right.
886  m_context.mouseButtonsAtMouseRelease = event->buttons();
887 
888  // The pressedMouseButtons must continually inform on the status of pressed
889  // buttons so remove the released button.
890  m_context.pressedMouseButtons ^= event->button();
891 
892  // qDebug().noquote() << m_context.toString();
893 
894  // We'll need to know if modifiers were pressed a the moment the user
895  // released the mouse button.
896  m_context.keyboardModifiers = QGuiApplication::keyboardModifiers();
897 
899  {
900  // Let the user know that the mouse was *not* being dragged.
901  m_context.wasMouseDragging = false;
902 
903  event->accept();
904 
905  return;
906  }
907 
908  // Let the user know that the mouse was being dragged.
910 
911  // We cannot hide all items in one go because we rely on their visibility
912  // to know what kind of dragging operation we need to perform (line-only
913  // X-based zoom or rectangle-based X- and Y-based zoom, for example). The
914  // only thing we know is that we can make the text invisible.
915 
916  // We would not want to still see the selection line item.
917  mp_selectLineItem->setVisible(false);
918 
919  // Same for the x delta text item
920  mp_xDeltaTextItem->setVisible(false);
921 
922  // We do not show the end vertical region range marker.
923  mp_vEndTracerItem->setVisible(false);
924 
925  // Horizontal position tracer.
926  mp_hPosTracerItem->setVisible(true);
927  mp_hPosTracerItem->start->setCoords(xAxis->range().lower,
929  mp_hPosTracerItem->end->setCoords(xAxis->range().upper,
931 
932  // Vertical position tracer.
933  mp_vPosTracerItem->setVisible(true);
934 
935  mp_vPosTracerItem->setVisible(true);
936  mp_vPosTracerItem->start->setCoords(m_context.lastCursorHoveredPoint.x(),
937  yAxis->range().upper);
939  yAxis->range().lower);
940 
941  // Force replot now because later that call might not be performed.
942  replot();
943 
944  // If we were using the "quantum" display for the rescale of the axes
945  // using the Ctrl-modified left button click drag in the axes, then reset
946  // the count to 0.
948 
949  // Compute the delta values, X and Y, that correspond to the movement that
950  // was done by the user while pressing the mouse button, that is get the
951  // geometry of the drag movement.
952 
954 
955  // Now that we have computed the useful ranges, we need to check what to do
956  // depending on the button that was pressed.
957 
958  if(m_context.lastReleasedMouseButton == Qt::LeftButton)
959  {
961  }
962  else if(m_context.lastReleasedMouseButton == Qt::RightButton)
963  {
965  }
966 
967 
968  // By definition we are stopping the drag operation by releasing the mouse
969  // button. Whatever that mouse button was pressed before and if there was
970  // one pressed before. We cannot set that boolean value to false before
971  // this place, because we call a number of routines above that need to know
972  // that dragging was occurring. Like mouseReleaseHandledEvent(event) for
973  // example.
974 
975  m_context.isMouseDragging = false;
976 
977  event->accept();
978 
979  return;
980 }
981 
982 
983 void
985 {
986 
988  {
989 
990  // When the mouse move handler pans the plot, we cannot store each axes
991  // range history element that would mean store a huge amount of such
992  // elements, as many element as there are mouse move event handled by
993  // the Qt event queue. But we can store an axis range history element
994  // for the last situation of the mouse move: when the button is
995  // released:
996 
998 
1000 
1001  replot();
1002 
1003  // Nothing else to do.
1004  return;
1005  }
1006 
1007  // Start handling the zoom rectangle so that we can skip any keyboard
1008  // modified, like Ctrl that would not mean anything anyway.
1009  if(mp_zoomRectItem->visible())
1010  {
1011 
1012  // If we were dragging with the left button pressed and could draw a
1013  // rectangle, then we were preparing a zoom operation. Let's bring
1014  // that operation to an end.
1015 
1016  mp_zoomRectItem->setVisible(false);
1017 
1018  axisZoom();
1019 
1020  return;
1021  }
1022 
1023  // If the user started by clicking in the plot region, dragged the mouse
1024  // cursor with the left button and pressed the Ctrl modifier, then that
1025  // means that they wanted to do a rescale over the x-axis in the form of a
1026  // reframing.
1027 
1028  if(m_context.keyboardModifiers & Qt::ControlModifier)
1029  {
1030  return axisReframe();
1031  }
1032 }
1033 
1034 
1035 void
1037 {
1038 
1039  // The right button is used for the integrations. Not for axis range
1040  // operations. So all we have to do is remove the various graphics items and
1041  // send a signal with the context that contains all the data required by the
1042  // user to perform the integrations over the right plot regions.
1043 
1044  // Whatever we were doing we need to make the selection line invisible:
1045 
1046  if(mp_selectLineItem->visible())
1047  mp_selectLineItem->setVisible(false);
1048 
1049  if(mp_xDeltaTextItem->visible())
1050  mp_xDeltaTextItem->setVisible(false);
1051 
1052  // Also make the vertical end tracer invisible.
1053  mp_vEndTracerItem->setVisible(false);
1054 
1055  // Force replot now because later that call might not be performed.
1056  replot();
1057 
1058  // Note that we only request an integration if the x-axis delta is enough.
1059 
1060  double x_delta_pixel =
1061  fabs(xAxis->coordToPixel(m_context.currentDragPoint.x()) -
1062  xAxis->coordToPixel(m_context.startDragPoint.x()));
1063 
1064  if(x_delta_pixel > 3)
1066  // else
1067  // qDebug() << "Not asking for integration.";
1068 }
1069 
1070 
1071 void
1073 {
1074 
1075  // If we have no focus, then get it. See setFocus() to understand why asking
1076  // for focus is cosly and thus why we want to make this decision first.
1077  if(!hasFocus())
1078  setFocus();
1079 
1080  // The event->button() must be by Qt instructions considered to be 0.
1081 
1082  // Whatever happens, we want to store the plot coordinates of the current
1083  // mouse cursor position (will be useful later for countless needs).
1084 
1085  QPointF mousePoint = event->localPos();
1086 
1087  // qDebug() << "local mousePoint position in pixels:" << mousePoint;
1088 
1089  m_context.lastCursorHoveredPoint.setX(xAxis->pixelToCoord(mousePoint.x()));
1090  m_context.lastCursorHoveredPoint.setY(yAxis->pixelToCoord(mousePoint.y()));
1091 
1092  // qDebug() << "lastCursorHoveredPoint coord:"
1093  //<< m_context.lastCursorHoveredPoint;
1094 
1095  // Now, depending on the button(s) (if any) that are pressed or not, we have
1096  // a different processing.
1097 
1098  if(m_context.pressedMouseButtons & Qt::LeftButton ||
1099  m_context.pressedMouseButtons & Qt::RightButton)
1101  else
1103 
1104  event->accept();
1105 }
1106 
1107 
1108 void
1110 {
1111 
1112  m_context.isMouseDragging = false;
1113 
1114  // We are not dragging the mouse (no button pressed), simply let this
1115  // widget's consumer know the position of the cursor and update the markers.
1116 
1118 
1119  // We are not dragging, so we do not show the region end tracer we only show
1120  // the anchoring start trace that might be of use if the user starts using
1121  // the arrow keys to move the cursor.
1122  mp_vEndTracerItem->setVisible(false);
1123 
1124  // Only bother with the tracers if the user wants them to be visible. Their
1125  // crossing point must be exactly at the last cursor-hovered point.
1126 
1128  {
1129  // We are not dragging, so only show the position markers (v and h);
1130 
1131  // Horizontal position tracer.
1132  mp_hPosTracerItem->setVisible(true);
1133  mp_hPosTracerItem->start->setCoords(xAxis->range().lower,
1135  mp_hPosTracerItem->end->setCoords(xAxis->range().upper,
1137 
1138  // Vertical position tracer.
1139  mp_vPosTracerItem->setVisible(true);
1140 
1141  mp_vPosTracerItem->setVisible(true);
1142  mp_vPosTracerItem->start->setCoords(m_context.lastCursorHoveredPoint.x(),
1143  yAxis->range().upper);
1145  yAxis->range().lower);
1146 
1147  replot();
1148  }
1149 
1150  return;
1151 }
1152 
1153 
1154 void
1156 {
1157 
1158  m_context.isMouseDragging = true;
1159 
1160  // Now store the mouse position data into the the current drag point
1161  // member datum, that will be used in countless occasions later.
1163  m_context.keyboardModifiers = QGuiApplication::queryKeyboardModifiers();
1164 
1165  // When we drag (either keyboard or mouse), we hide the position markers
1166  // (black) and we show the start and end vertical markers for the region.
1167  // Then, we draw the horizontal region range marker that delimits
1168  // horizontally the dragged-over region.
1169 
1170  mp_hPosTracerItem->setVisible(false);
1171  mp_vPosTracerItem->setVisible(false);
1172 
1173  // Only bother with the tracers if the user wants them to be visible.
1175  {
1176 
1177  // The vertical end tracer position must be refreshed.
1178  mp_vEndTracerItem->start->setCoords(m_context.currentDragPoint.x(),
1179  yAxis->range().upper);
1180 
1181  mp_vEndTracerItem->end->setCoords(m_context.currentDragPoint.x(),
1182  yAxis->range().lower);
1183 
1184  mp_vEndTracerItem->setVisible(true);
1185  }
1186 
1187  // Whatever the button, when we are dealing with the axes, we do not
1188  // want to show any of the tracers.
1189 
1191  {
1192  mp_hPosTracerItem->setVisible(false);
1193  mp_vPosTracerItem->setVisible(false);
1194 
1195  mp_vStartTracerItem->setVisible(false);
1196  mp_vEndTracerItem->setVisible(false);
1197  }
1198 
1199  // We will need the axes ranges matching the region being selected by the
1200  // user while left-mouse-dragging the cursor.
1201 
1203 
1204  // Now deal with the BUTTON-SPECIFIC CODE.
1205 
1206  if(m_context.mouseButtonsAtMousePress & Qt::LeftButton)
1207  {
1209  }
1210  else if(m_context.mouseButtonsAtMousePress & Qt::RightButton)
1211  {
1213  }
1214 }
1215 
1216 
1217 void
1219 {
1220  // qDebug() << "the left button is dragging.";
1221 
1222  // Set the context.isMeasuringDistance to false, which later might be set to
1223  // true if effectively we are measuring a distance. This is required because
1224  // the derived widget classes might want to know if they have to perform
1225  // some action on the basis that context is measuring a distance, for
1226  // example the mass spectrum-specific widget might want to compute
1227  // deconvolutions.
1228 
1230 
1231  // Let's first check if the mouse drag operation originated on either
1232  // axis. In that case, the user is performing axis reframing or rescaling.
1233 
1235  {
1236  // qDebug() << __FILE__ << __LINE__ << "Click was on one of the axes.";
1237 
1238  if(m_context.keyboardModifiers & Qt::ControlModifier)
1239  {
1240  // The user is asking a rescale of the plot.
1241 
1242  // We know that we do not want the tracers when we perform axis
1243  // rescaling operations.
1244 
1245  mp_hPosTracerItem->setVisible(false);
1246  mp_vPosTracerItem->setVisible(false);
1247 
1248  mp_vStartTracerItem->setVisible(false);
1249  mp_vEndTracerItem->setVisible(false);
1250 
1251  // This operation is particularly intensive, thus we want to
1252  // reduce the number of calculations by skipping this calculation
1253  // a number of times. The user can ask for this feature by
1254  // clicking the 'Q' letter.
1255 
1256  if(m_context.pressedKeyCode == Qt::Key_Q)
1257  {
1259  {
1261  return;
1262  }
1263  else
1264  {
1266  }
1267  }
1268 
1269  // qDebug() << "Asking that the axes be rescaled.";
1270 
1271  axisRescale();
1272  }
1273  else
1274  {
1275  // The user was simply dragging the axis. Just pan, that is slide
1276  // the plot in the same direction as the mouse movement and with the
1277  // same amplitude.
1278 
1279  // qDebug() << "Asking that the axes be panned.";
1280 
1281  axisPan();
1282  }
1283 
1284  return;
1285  }
1286 
1287  // At this point we understand that the user was not performing any
1288  // panning/rescaling operation by clicking on any one of the axes.. Go on
1289  // with other possibilities.
1290 
1291  // Let's check if the user is actually drawing a rectangle (covering a
1292  // real area) or is drawing a line.
1293 
1294  // qDebug() << "The mouse dragging did not originate on an axis.";
1295 
1297  {
1298  // qDebug() << "Apparently the selection is a real rectangle.";
1299 
1300  // When we draw a rectangle the tracers are of no use.
1301 
1302  mp_hPosTracerItem->setVisible(false);
1303  mp_vPosTracerItem->setVisible(false);
1304 
1305  mp_vStartTracerItem->setVisible(false);
1306  mp_vEndTracerItem->setVisible(false);
1307 
1309  }
1310  else
1311  {
1312  // qDebug() << "Apparently we are measuring a delta.";
1313 
1314  // The pure position tracers should be hidden.
1315  mp_hPosTracerItem->setVisible(true);
1316  mp_vPosTracerItem->setVisible(true);
1317 
1318  // Then, make sure the region range vertical tracers are visible.
1319  mp_vStartTracerItem->setVisible(true);
1320  mp_vEndTracerItem->setVisible(true);
1321 
1323  }
1324 }
1325 
1326 
1327 void
1329 {
1330  // qDebug() << "the right button is dragging.";
1331 
1332  // Set the context.isMeasuringDistance to false, which later might be set to
1333  // true if effectively we are measuring a distance. This is required because
1334  // the derived widgets might want to know if they have to perform some
1335  // action on the basis that context is measuring a distance, for example the
1336  // mass spectrum-specific widget might want to compute deconvolutions.
1337 
1339 
1341 }
1342 
1343 
1344 void
1346  QCPAxis *axis,
1347  [[maybe_unused]] QCPAxis::SelectablePart part,
1348  QMouseEvent *event)
1349 {
1350 
1351  m_context.keyboardModifiers = QGuiApplication::queryKeyboardModifiers();
1352 
1353  if(m_context.keyboardModifiers & Qt::ControlModifier)
1354  {
1355 
1356  // If the Ctrl modifiers is active, then both axes are to be reset. Also
1357  // the histories are reset also.
1358 
1359  rescaleAxes();
1361  }
1362  else
1363  {
1364  // Only the axis passed as parameter is to be rescaled.
1365  // Reset the range of that axis to the max view possible.
1366 
1367  axis->rescale();
1368 
1370 
1371  event->accept();
1372  }
1373 
1374  // The double-click event does not cancel the mouse press event. That is, if
1375  // left-double-clicking, at the end of the operation the button still
1376  // "pressed". We need to remove manually the button from the pressed buttons
1377  // context member.
1378 
1379  m_context.pressedMouseButtons ^= event->button();
1380 
1382 
1384 
1385  replot();
1386 }
1387 
1388 
1389 bool
1390 BasePlotWidget::isClickOntoXAxis(const QPointF &mousePoint)
1391 {
1392  QCPLayoutElement *layoutElement = layoutElementAt(mousePoint);
1393 
1394  if(layoutElement &&
1395  layoutElement == dynamic_cast<QCPLayoutElement *>(axisRect()))
1396  {
1397  // The graph is *inside* the axisRect that is the outermost envelope of
1398  // the graph. Thus, if we want to know if the click was indeed on an
1399  // axis, we need to check what selectable part of the the axisRect we
1400  // were
1401  // clicking:
1402  QCPAxis::SelectablePart selectablePart;
1403 
1404  selectablePart = xAxis->getPartAt(mousePoint);
1405 
1406  if(selectablePart == QCPAxis::spAxisLabel ||
1407  selectablePart == QCPAxis::spAxis ||
1408  selectablePart == QCPAxis::spTickLabels)
1409  return true;
1410  }
1411 
1412  return false;
1413 }
1414 
1415 
1416 bool
1417 BasePlotWidget::isClickOntoYAxis(const QPointF &mousePoint)
1418 {
1419  QCPLayoutElement *layoutElement = layoutElementAt(mousePoint);
1420 
1421  if(layoutElement &&
1422  layoutElement == dynamic_cast<QCPLayoutElement *>(axisRect()))
1423  {
1424  // The graph is *inside* the axisRect that is the outermost envelope of
1425  // the graph. Thus, if we want to know if the click was indeed on an
1426  // axis, we need to check what selectable part of the the axisRect we
1427  // were
1428  // clicking:
1429  QCPAxis::SelectablePart selectablePart;
1430 
1431  selectablePart = yAxis->getPartAt(mousePoint);
1432 
1433  if(selectablePart == QCPAxis::spAxisLabel ||
1434  selectablePart == QCPAxis::spAxis ||
1435  selectablePart == QCPAxis::spTickLabels)
1436  return true;
1437  }
1438 
1439  return false;
1440 }
1441 
1442 /// MOUSE-related EVENTS
1443 
1444 
1445 /// MOUSE MOVEMENTS mouse/keyboard-triggered
1446 
1447 int
1449 {
1450  // The user is dragging the mouse, probably to rescale the axes, but we need
1451  // to sort out in which direction the drag is happening.
1452 
1453  // This function should be called after calculateDragDeltas, so that
1454  // m_context has the proper x/y delta values that we'll compare.
1455 
1456  // Note that we cannot compare simply x or y deltas because the y axis might
1457  // have a different scale that the x axis. So we first need to convert the
1458  // positions to pixels.
1459 
1460  double x_delta_pixel =
1461  fabs(xAxis->coordToPixel(m_context.currentDragPoint.x()) -
1462  xAxis->coordToPixel(m_context.startDragPoint.x()));
1463 
1464  double y_delta_pixel =
1465  fabs(yAxis->coordToPixel(m_context.currentDragPoint.y()) -
1466  yAxis->coordToPixel(m_context.startDragPoint.y()));
1467 
1468  if(x_delta_pixel > y_delta_pixel)
1469  return Qt::Horizontal;
1470 
1471  return Qt::Vertical;
1472 }
1473 
1474 
1475 void
1477 {
1478  // First convert the graph coordinates to pixel coordinates.
1479 
1480  QPointF pixels_coordinates(xAxis->coordToPixel(graph_coordinates.x()),
1481  yAxis->coordToPixel(graph_coordinates.y()));
1482 
1483  moveMouseCursorPixelCoordToGlobal(pixels_coordinates.toPoint());
1484 }
1485 
1486 
1487 void
1489 {
1490  // qDebug() << "Calling set pos with new cursor position.";
1491  QCursor::setPos(mapToGlobal(pixel_coordinates.toPoint()));
1492 }
1493 
1494 
1495 void
1497 {
1498  QPointF graph_coord = horizontalGetGraphCoordNewPointCountPixels(pixel_count);
1499 
1500  QPointF pixel_coord(xAxis->coordToPixel(graph_coord.x()),
1501  yAxis->coordToPixel(graph_coord.y()));
1502 
1503  // Now we need ton convert the new coordinates to the global position system
1504  // and to move the cursor to that new position. That will create an event to
1505  // move the mouse cursor.
1506 
1507  moveMouseCursorPixelCoordToGlobal(pixel_coord.toPoint());
1508 }
1509 
1510 
1511 QPointF
1513 {
1514  QPointF pixel_coordinates(
1515  xAxis->coordToPixel(m_context.lastCursorHoveredPoint.x()) + pixel_count,
1516  yAxis->coordToPixel(m_context.lastCursorHoveredPoint.y()));
1517 
1518  // Now convert back to local coordinates.
1519 
1520  QPointF graph_coordinates(xAxis->pixelToCoord(pixel_coordinates.x()),
1521  yAxis->pixelToCoord(pixel_coordinates.y()));
1522 
1523  return graph_coordinates;
1524 }
1525 
1526 
1527 void
1529 {
1530 
1531  QPointF graph_coord = verticalGetGraphCoordNewPointCountPixels(pixel_count);
1532 
1533  QPointF pixel_coord(xAxis->coordToPixel(graph_coord.x()),
1534  yAxis->coordToPixel(graph_coord.y()));
1535 
1536  // Now we need ton convert the new coordinates to the global position system
1537  // and to move the cursor to that new position. That will create an event to
1538  // move the mouse cursor.
1539 
1540  moveMouseCursorPixelCoordToGlobal(pixel_coord.toPoint());
1541 }
1542 
1543 
1544 QPointF
1546 {
1547  QPointF pixel_coordinates(
1548  xAxis->coordToPixel(m_context.lastCursorHoveredPoint.x()),
1549  yAxis->coordToPixel(m_context.lastCursorHoveredPoint.y()) + pixel_count);
1550 
1551  // Now convert back to local coordinates.
1552 
1553  QPointF graph_coordinates(xAxis->pixelToCoord(pixel_coordinates.x()),
1554  yAxis->pixelToCoord(pixel_coordinates.y()));
1555 
1556  return graph_coordinates;
1557 }
1558 
1559 /// MOUSE MOVEMENTS mouse/keyboard-triggered
1560 
1561 
1562 /// RANGE-related functions
1563 
1564 QCPRange
1565 BasePlotWidget::getRangeX(bool &found_range, int index) const
1566 {
1567  QCPGraph *graph_p = graph(index);
1568 
1569  if(graph_p == nullptr)
1570  qFatal("Programming error.");
1571 
1572  return graph_p->getKeyRange(found_range);
1573 }
1574 
1575 
1576 QCPRange
1577 BasePlotWidget::getRangeY(bool &found_range, int index) const
1578 {
1579  QCPGraph *graph_p = graph(index);
1580 
1581  if(graph_p == nullptr)
1582  qFatal("Programming error.");
1583 
1584  return graph_p->getValueRange(found_range);
1585 }
1586 
1587 
1588 QCPRange
1590  RangeType range_type,
1591  bool &found_range) const
1592 {
1593 
1594  // Iterate in all the graphs in this widget and return a QCPRange that has
1595  // its lower member as the greatest lower value of all
1596  // its upper member as the smallest upper value of all
1597 
1598  if(!graphCount())
1599  {
1600  found_range = false;
1601 
1602  return QCPRange(0, 1);
1603  }
1604 
1605  if(graphCount() == 1)
1606  return graph()->getKeyRange(found_range);
1607 
1608  bool found_at_least_one_range = false;
1609 
1610  // Create an invalid range.
1611  QCPRange result_range(QCPRange::minRange + 1, QCPRange::maxRange + 1);
1612 
1613  for(int iter = 0; iter < graphCount(); ++iter)
1614  {
1615  QCPRange temp_range;
1616 
1617  bool found_range_for_iter = false;
1618 
1619  QCPGraph *graph_p = graph(iter);
1620 
1621  // Depending on the axis param, select the key or value range.
1622 
1623  if(axis == PlotAxis::x_axis)
1624  temp_range = graph_p->getKeyRange(found_range_for_iter);
1625  else if(axis == PlotAxis::y_axis)
1626  temp_range = graph_p->getValueRange(found_range_for_iter);
1627  else
1628  qFatal("Cannot reach this point. Programming error.");
1629 
1630  // Was a range found for the iterated graph ? If not skip this
1631  // iteration.
1632 
1633  if(!found_range_for_iter)
1634  continue;
1635 
1636  // While the innermost_range is invalid, we need to seed it with a good
1637  // one. So check this.
1638 
1639  if(!QCPRange::validRange(result_range))
1640  qFatal("The obtained range is invalid !");
1641 
1642  // At this point we know the obtained range is OK.
1643  result_range = temp_range;
1644 
1645  // We found at least one valid range!
1646  found_at_least_one_range = true;
1647 
1648  // At this point we have two valid ranges to compare. Depending on
1649  // range_type, we need to perform distinct comparisons.
1650 
1651  if(range_type == RangeType::innermost)
1652  {
1653  if(temp_range.lower > result_range.lower)
1654  result_range.lower = temp_range.lower;
1655  if(temp_range.upper < result_range.upper)
1656  result_range.upper = temp_range.upper;
1657  }
1658  else if(range_type == RangeType::outermost)
1659  {
1660  if(temp_range.lower < result_range.lower)
1661  result_range.lower = temp_range.lower;
1662  if(temp_range.upper > result_range.upper)
1663  result_range.upper = temp_range.upper;
1664  }
1665  else
1666  qFatal("Cannot reach this point. Programming error.");
1667 
1668  // Continue to next graph, if any.
1669  }
1670  // End of
1671  // for(int iter = 0; iter < graphCount(); ++iter)
1672 
1673  // Let the caller know if we found at least one range.
1674  found_range = found_at_least_one_range;
1675 
1676  return result_range;
1677 }
1678 
1679 
1680 QCPRange
1681 BasePlotWidget::getInnermostRangeX(bool &found_range) const
1682 {
1683 
1684  return getRange(PlotAxis::x_axis, RangeType::innermost, found_range);
1685 }
1686 
1687 
1688 QCPRange
1689 BasePlotWidget::getOutermostRangeX(bool &found_range) const
1690 {
1691  return getRange(PlotAxis::x_axis, RangeType::outermost, found_range);
1692 }
1693 
1694 
1695 QCPRange
1696 BasePlotWidget::getInnermostRangeY(bool &found_range) const
1697 {
1698 
1699  return getRange(PlotAxis::y_axis, RangeType::innermost, found_range);
1700 }
1701 
1702 
1703 QCPRange
1704 BasePlotWidget::getOutermostRangeY(bool &found_range) const
1705 {
1706  return getRange(PlotAxis::y_axis, RangeType::outermost, found_range);
1707 }
1708 
1709 
1710 /// RANGE-related functions
1711 
1712 
1713 /// PLOTTING / REPLOTTING functions
1714 
1715 void
1717 {
1718  double xLower = xAxis->range().lower;
1719  double xUpper = xAxis->range().upper;
1720 
1721  // Get the current y lower/upper range.
1722  double yLower = yAxis->range().lower;
1723  double yUpper = yAxis->range().upper;
1724 
1725  // This function is called only when the user has clicked on the x/y axis or
1726  // when the user has dragged the left mouse button with the Ctrl key
1727  // modifier. The m_context.wasClickOnXAxis is then simulated in the mouse
1728  // move handler. So we need to test which axis was clicked-on.
1729 
1731  {
1732 
1733  // We are changing the range of the X axis.
1734 
1735  // What is the x delta ?
1736  double xDelta =
1738 
1739  // If xDelta is < 0, the we were dragging from right to left, we are
1740  // compressing the view on the x axis, by adding new data to the right
1741  // hand size of the graph. So we add xDelta to the upper bound of the
1742  // range. Otherwise we are uncompressing the view on the x axis and
1743  // remove the xDelta from the upper bound of the range. This is why we
1744  // have the
1745  // '-'
1746  // and not '+' below;
1747 
1748  // qDebug() << "Setting xaxis:" << xLower << "--" << xUpper - xDelta;
1749 
1750  xAxis->setRange(xLower, xUpper - xDelta);
1751  }
1752  // End of
1753  // if(m_context.wasClickOnXAxis)
1754  else // that is, if(m_context.wasClickOnYAxis)
1755  {
1756  // We are changing the range of the Y axis.
1757 
1758  // What is the y delta ?
1759  double yDelta =
1761 
1762  // See above for an explanation of the computation.
1763 
1764  yAxis->setRange(yLower, yUpper - yDelta);
1765 
1766  // Old version
1767  // if(yDelta < 0)
1768  //{
1769  //// The dragging operation was from top to bottom, we are enlarging
1770  //// the range (thus, we are unzooming the view, since the widget
1771  //// always has the same size).
1772 
1773  // yAxis->setRange(yLower, yUpper + fabs(yDelta));
1774  //}
1775  // else
1776  //{
1777  //// The dragging operation was from bottom to top, we are reducing
1778  //// the range (thus, we are zooming the view, since the widget
1779  //// always has the same size).
1780 
1781  // yAxis->setRange(yLower, yUpper - fabs(yDelta));
1782  //}
1783  }
1784  // End of
1785  // else // that is, if(m_context.wasClickOnYAxis)
1786 
1787  // Update the context with the current axes ranges
1788 
1790 
1792 
1793  replot();
1794 }
1795 
1796 
1797 void
1799 {
1800 
1801  // double sorted_start_drag_point_x =
1802  // std::min(m_context.startDragPoint.x(), m_context.currentDragPoint.x());
1803 
1804  // xAxis->setRange(sorted_start_drag_point_x,
1805  // sorted_start_drag_point_x + fabs(m_context.xDelta));
1806 
1807  xAxis->setRange(
1809 
1810  // Note that the y axis should be rescaled from current lower value to new
1811  // upper value matching the y-axis position of the cursor when the mouse
1812  // button was released.
1813 
1814  yAxis->setRange(
1815  xAxis->range().lower,
1817 
1818  // qDebug() << "xaxis:" << xAxis->range().lower << "-" <<
1819  // xAxis->range().upper
1820  //<< "yaxis:" << yAxis->range().lower << "-" << yAxis->range().upper;
1821 
1823 
1826 
1827  replot();
1828 }
1829 
1830 
1831 void
1833 {
1834 
1835  // Use the m_context.xRegionRangeStart/End values, but we need to sort the
1836  // values before using them, because now we want to really have the lower x
1837  // value. Simply craft a QCPRange that will swap the values if lower is not
1838  // < than upper QCustomPlot calls this normalization).
1839 
1840  xAxis->setRange(
1842 
1843  yAxis->setRange(
1845 
1847 
1850 
1851  replot();
1852 }
1853 
1854 void
1856 {
1858  {
1859  xAxis->setRange(m_context.xRange.lower - m_context.xDelta,
1860  m_context.xRange.upper - m_context.xDelta);
1861  }
1862 
1864  {
1865  yAxis->setRange(m_context.yRange.lower - m_context.yDelta,
1866  m_context.yRange.upper - m_context.yDelta);
1867  }
1868 
1870 
1871  // We cannot store the new ranges in the history, because the pan operation
1872  // involved a huge quantity of micro-movements elicited upon each mouse move
1873  // cursor event so we would have a huge history.
1874  // updateAxesRangeHistory();
1875 
1876  // Now that the contex has the right range values, we can emit the
1877  // signal that will be used by this plot widget users, typically to
1878  // abide by the x/y range lock required by the user.
1879 
1881 
1882  replot();
1883 }
1884 
1885 
1886 void
1888  QCPRange yAxisRange,
1889  PlotAxis axis)
1890 {
1891  if(static_cast<int>(axis) & static_cast<int>(PlotAxis::x_axis))
1892  {
1893  xAxis->setRange(xAxisRange.lower, xAxisRange.upper);
1894  }
1895 
1896  if(static_cast<int>(axis) & static_cast<int>(PlotAxis::y_axis))
1897  {
1898  yAxis->setRange(yAxisRange.lower, yAxisRange.upper);
1899  }
1900 
1901  // We do not want to update the history, because there would be way too
1902  // much history items, since this function is called upon mouse moving
1903  // handling and not only during mouse release events.
1904  // updateAxesRangeHistory();
1905 
1906  replot();
1907 }
1908 
1909 
1910 void
1911 BasePlotWidget::replotWithAxisRangeX(double lower, double upper)
1912 {
1913  // qDebug();
1914 
1915  xAxis->setRange(lower, upper);
1916 
1917  replot();
1918 }
1919 
1920 
1921 void
1922 BasePlotWidget::replotWithAxisRangeY(double lower, double upper)
1923 {
1924  // qDebug();
1925 
1926  yAxis->setRange(lower, upper);
1927 
1928  replot();
1929 }
1930 
1931 /// PLOTTING / REPLOTTING functions
1932 
1933 
1934 /// PLOT ITEMS : TRACER TEXT ITEMS...
1935 
1936 //! Hide the selection line, the xDelta text and the zoom rectangle items.
1937 void
1939 {
1940  mp_selectLineItem->setVisible(false);
1941 
1942  mp_xDeltaTextItem->setVisible(false);
1943 
1944  mp_zoomRectItem->setVisible(false);
1945 
1946  // Force a replot to make sure the action is immediately visible by the
1947  // user, even without moving the mouse.
1948  replot();
1949 }
1950 
1951 
1952 //! Show the traces (vertical and horizontal).
1953 void
1955 {
1956  m_shouldTracersBeVisible = true;
1957 
1958  mp_vPosTracerItem->setVisible(true);
1959  mp_hPosTracerItem->setVisible(true);
1960 
1961  mp_vStartTracerItem->setVisible(true);
1962  mp_vEndTracerItem->setVisible(true);
1963 
1964  // Force a replot to make sure the action is immediately visible by the
1965  // user, even without moving the mouse.
1966  replot();
1967 }
1968 
1969 
1970 //! Hide the traces (vertical and horizontal).
1971 void
1973 {
1974  m_shouldTracersBeVisible = false;
1975  mp_hPosTracerItem->setVisible(false);
1976  mp_vPosTracerItem->setVisible(false);
1977 
1978  mp_vStartTracerItem->setVisible(false);
1979  mp_vEndTracerItem->setVisible(false);
1980 
1981  // Force a replot to make sure the action is immediately visible by the
1982  // user, even without moving the mouse.
1983  replot();
1984 }
1985 
1986 
1987 void
1989 {
1990  // The user has drawn the mouse left button on the graph, which means he is
1991  // willing to draw a zoom rectangle.
1992 
1993  mp_selectLineItem->setVisible(false);
1994 
1995  mp_xDeltaTextItem->setVisible(false);
1996 
1997  mp_zoomRectItem->topLeft->setCoords(m_context.startDragPoint.x(),
1999  mp_zoomRectItem->bottomRight->setCoords(m_context.currentDragPoint.x(),
2001 
2002  mp_zoomRectItem->setVisible(true);
2003 
2004  // Now set the geometry of the rectangle to the context so that upon the
2005  // mouse button release, if the zoom rectanble is visible we know how to
2006  // reframe the plot axes. Note that we do not sort the values, if that is
2007  // required the user needs to use the sortAscendingRange() function.
2008 
2011 
2014 
2015  // Note that if we draw a zoom rectangle, then we are certainly not
2016  // measuring anything. So set the boolean value to false so that the user of
2017  // this widget or derived classes know that there is nothing to perform upon
2018  // (like deconvolution, for example).
2019 
2021 
2022  // Also remove the delta value from the pipeline by sending a simple
2023  // distance without measurement signal.
2024 
2025  emit xAxisMeasurementSignal(m_context, false);
2026 
2027  replot();
2028 }
2029 
2030 
2031 void
2033 {
2034  // The user has dragged the mouse left button on the graph in such a way
2035  // that the xDelta is big and the yDelta is almost nothing, that
2036  // means that he does not want to draw a rectangle but a line to
2037  // measure the delta between two points of the graph.
2038 
2039  mp_zoomRectItem->setVisible(false);
2040 
2041  // We also want to show the span as a text item.
2042 
2043  // m_context.xDelta is not an absolute value.
2044  double m_xDeltaHalf = fabs(m_context.xDelta / 2);
2045 
2046  // Use the m_context.xRegionRangeStart/End values, but we need to sort the
2047  // values before using them, because now we want to really have the lower x
2048  // value. Simply craft a QCPRange that will swap the values if lower is not
2049  // < than upper QCustomPlot calls this normalization).
2050 
2051  QCPRange sorted_range(m_context.xRegionRangeStart, m_context.xRegionRangeEnd);
2052 
2053  // qDebug() << "sorted range:" << sorted_range.lower << "-" <<
2054  // sorted_range.upper
2055  //<< "xrangedeltahalf:" << m_xDeltaHalf;
2056 
2057  mp_xDeltaTextItem->position->setCoords(sorted_range.lower + m_xDeltaHalf,
2059 
2060  mp_xDeltaTextItem->setText(QString("%1").arg(m_context.xDelta, 0, 'f', 3));
2061 
2062  mp_xDeltaTextItem->setFont(QFont(font().family(), 7));
2063 
2064  mp_xDeltaTextItem->setVisible(true);
2065 
2066  // We do not want to show the position markers because the only horiontal
2067  // line to be visible must be contained between the start and end vertiacal
2068  // tracer items.
2069  mp_hPosTracerItem->setVisible(false);
2070  mp_vPosTracerItem->setVisible(false);
2071 
2072  mp_selectLineItem->setVisible(true);
2073 
2074  mp_selectLineItem->start->setCoords(m_context.startDragPoint.x(),
2076  // But we want the line to be horizontal, thus we keep the original y
2077  // value.
2078  mp_selectLineItem->end->setCoords(m_context.currentDragPoint.x(),
2080 
2081  // Set the boolean to true so that derived widgets know that something is
2082  // being measured, and they can act accordingly, for example by computing
2083  // deconvolutions in a mass spectrum.
2085 
2086  return;
2087 }
2088 
2089 
2090 void
2092 {
2094 
2095  // Also, we do not want arrows, because we are not integrating anything
2096  // here.
2097  mp_selectLineItem->setHead(QCPLineEnding::esNone);
2098  mp_selectLineItem->setTail(QCPLineEnding::esNone);
2099 
2100  replot();
2101 
2102  // Let the caller know that we were measuring something.
2103  emit xAxisMeasurementSignal(m_context, true);
2104 }
2105 
2106 
2107 void
2109 {
2111 
2112  // Since we draw the selection line with the xDelta text for integration, we
2113  // let the user guess it by the arrows at the ends of the line.
2114  mp_selectLineItem->setHead(QCPLineEnding::esSpikeArrow);
2115  mp_selectLineItem->setTail(QCPLineEnding::esSpikeArrow);
2116 
2117  replot();
2118 
2119  // Let the caller know that we were measuring something.
2120  emit xAxisMeasurementSignal(m_context, true);
2121 }
2122 
2123 
2124 void
2126 {
2127 
2128  // We compute signed differentials. If the user does not want the sign,
2129  // fabs(double) is their friend.
2130 
2131  // Compute the xAxis differential:
2132 
2133  m_context.xDelta =
2135 
2136  // Same with the Y-axis range:
2137 
2138  m_context.yDelta =
2140 
2141  // qDebug() << "xDelta:" << m_context.xDelta
2142  //<< "and yDelta:" << m_context.yDelta;
2143 
2144  return;
2145 }
2146 
2147 
2148 void
2150 {
2151 
2153 
2154  // Note that we do not sort the x value nor the y values. If the user of the
2155  // ranges need them to be sorted, they can use the sortAscendingRange().
2156 
2159 
2162 
2163  return;
2164 }
2165 
2166 
2167 bool
2169 {
2170  // First get the height of the plot.
2171  double plotHeight = yAxis->range().upper - yAxis->range().lower;
2172 
2173  double heightDiff =
2175 
2176  double heightDiffRatio = (heightDiff / plotHeight) * 100;
2177 
2178  if(heightDiffRatio > 10)
2179  return true;
2180 
2181  return false;
2182 }
2183 
2184 
2185 void
2187 {
2188  // qDebug() << "Setting focus to the QCustomPlot:" << this;
2189 
2190  QCustomPlot::setFocus();
2191 
2192  // qDebug() << "Emitting setFocusSignal().";
2193 
2194  emit setFocusSignal();
2195 }
2196 
2197 
2198 //! Redraw the background of the \p focusedPlotWidget plot widget.
2199 void
2200 BasePlotWidget::redrawPlotBackground(QWidget *focusedPlotWidget)
2201 {
2202  if(focusedPlotWidget == nullptr)
2203  throw ExceptionNotPossible(
2204  "baseplotwidget.cpp @ redrawPlotBackground(QWidget *focusedPlotWidget -- "
2205  "ERROR focusedPlotWidget cannot be nullptr.");
2206 
2207  if(dynamic_cast<QWidget *>(this) != focusedPlotWidget)
2208  {
2209  // The focused widget is not *this widget. We should make sure that
2210  // we were not the one that had the focus, because in this case we
2211  // need to redraw an unfocused background.
2212 
2213  axisRect()->setBackground(m_unfocusedBrush);
2214  }
2215  else
2216  {
2217  axisRect()->setBackground(m_focusedBrush);
2218  }
2219 
2220  replot();
2221 }
2222 
2223 
2224 void
2226 {
2227  m_context.xRange = QCPRange(xAxis->range().lower, xAxis->range().upper);
2228  m_context.yRange = QCPRange(yAxis->range().lower, yAxis->range().upper);
2229 }
2230 
2231 
2232 const BasePlotContext &
2234 {
2235  return m_context;
2236 }
2237 
2238 
2239 } // namespace pappso
int basePlotContextPtrMetaTypeId
int basePlotContextMetaTypeId
int m_mouseMoveHandlerSkipAmount
How many mouse move events must be skipped *‍/.
std::size_t m_lastAxisRangeHistoryIndex
Index of the last axis range history item.
virtual void updateAxesRangeHistory()
Create new axis range history items and append them to the history.
virtual void drawXDeltaLineForIntegration()
virtual void drawRectangleAndPrepareZoom()
bool m_shouldTracersBeVisible
Tells if the tracers should be visible.
virtual void mouseMoveHandlerDraggingCursor()
virtual void directionKeyReleaseEvent(QKeyEvent *event)
virtual QCPRange getOutermostRangeX(bool &found_range) const
void lastCursorHoveredPointSignal(const QPointF &pointf)
virtual void drawXDeltaLineAndMeasure()
void plottableDestructionRequestedSignal(BasePlotWidget *base_plot_widget_p, QCPAbstractPlottable *plottable_p, const BasePlotContext &context)
virtual const BasePlotContext & getContext() const
virtual QCPRange getRangeY(bool &found_range, int index) const
QCPRange getRange(PlotAxis axis, RangeType range_type, bool &found_range) const
virtual bool isProperSelectionRectangle()
virtual void keyPressEvent(QKeyEvent *event)
KEYBOARD-related EVENTS.
virtual ~BasePlotWidget()
Destruct this BasePlotWidget instance.
QCPItemText * mp_xDeltaTextItem
Text describing the x-axis delta value during a drag operation.
virtual void replotWithAxesRanges(QCPRange xAxisRange, QCPRange yAxisRange, PlotAxis whichAxis)
virtual void setAxisLabelX(const QString &label)
virtual void mouseMoveHandlerLeftButtonDraggingCursor()
QCPItemLine * mp_selectLineItem
Line that is printed when the user selects a range.
int m_mouseMoveHandlerSkipCount
Counter to handle the "fat data" mouse move event handling.
virtual QCPRange getOutermostRangeY(bool &found_range) const
int dragDirection()
MOUSE-related EVENTS.
bool isClickOntoYAxis(const QPointF &mousePoint)
virtual void moveMouseCursorPixelCoordToGlobal(QPointF local_coordinates)
QCPItemLine * mp_hPosTracerItem
Horizontal position tracer.
QCPItemLine * mp_vPosTracerItem
Vertical position tracer.
virtual bool setupWidget()
virtual void setPen(const QPen &pen)
virtual void mouseReleaseHandlerRightButton()
virtual QCPRange getInnermostRangeX(bool &found_range) const
virtual void mouseMoveHandlerNotDraggingCursor()
virtual void redrawPlotBackground(QWidget *focusedPlotWidget)
Redraw the background of the focusedPlotWidget plot widget.
bool isClickOntoXAxis(const QPointF &mousePoint)
virtual void setAxisLabelY(const QString &label)
virtual void restoreAxesRangeHistory(std::size_t index)
Get the axis histories at index index and update the plot ranges.
virtual void spaceKeyReleaseEvent(QKeyEvent *event)
virtual void replotWithAxisRangeX(double lower, double upper)
virtual QColor getPlottingColor(QCPAbstractPlottable *plottable_p) const
virtual void mouseReleaseHandlerLeftButton()
QBrush m_focusedBrush
Color used for the background of focused plot.
QPen m_pen
Pen used to draw the graph and textual elements in the plot widget.
virtual void mousePressHandler(QMouseEvent *event)
KEYBOARD-related EVENTS.
virtual void verticalMoveMouseCursorCountPixels(int pixel_count)
virtual void resetAxesRangeHistory()
virtual void showTracers()
Show the traces (vertical and horizontal).
virtual QPointF horizontalGetGraphCoordNewPointCountPixels(int pixel_count)
virtual void horizontalMoveMouseCursorCountPixels(int pixel_count)
BasePlotWidget(QWidget *parent)
std::vector< QCPRange * > m_yAxisRangeHistory
List of y axis ranges occurring during the panning zooming actions.
virtual QCPRange getInnermostRangeY(bool &found_range) const
virtual void setFocus()
PLOT ITEMS : TRACER TEXT ITEMS...
void keyReleaseEventSignal(const BasePlotContext &context)
virtual const QPen & getPen() const
virtual void prepareXDeltaLineAndMeasure()
virtual void mousePseudoButtonKeyPressEvent(QKeyEvent *event)
virtual void setPlottingColor(QCPAbstractPlottable *plottable_p, const QColor &new_color)
virtual void calculateDragDeltas()
virtual QPointF verticalGetGraphCoordNewPointCountPixels(int pixel_count)
void plotRangesChangedSignal(const BasePlotContext &context)
QCPItemLine * mp_vStartTracerItem
Vertical selection start tracer (typically in green).
virtual void mouseReleaseHandler(QMouseEvent *event)
QBrush m_unfocusedBrush
Color used for the background of unfocused plot.
virtual void axisRescale()
RANGE-related functions.
virtual void updateContextRanges()
virtual void moveMouseCursorGraphCoordToGlobal(QPointF plot_coordinates)
virtual void axisDoubleClickHandler(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event)
virtual void mouseMoveHandlerRightButtonDraggingCursor()
QCPItemLine * mp_vEndTracerItem
Vertical selection end tracer (typically in red).
virtual void mouseMoveHandler(QMouseEvent *event)
virtual void directionKeyPressEvent(QKeyEvent *event)
QCPItemRect * mp_zoomRectItem
Rectangle defining the borders of zoomed-in/out data.
virtual void keyReleaseEvent(QKeyEvent *event)
Handle specific key codes and trigger respective actions.
virtual void restorePreviousAxesRangeHistory()
Go up one history element in the axis history.
virtual void calculateDragDeltasAndUnSortedRegionCorners()
void integrationRequestedSignal(const BasePlotContext &context)
void xAxisMeasurementSignal(const BasePlotContext &context, bool with_delta)
virtual void replotWithAxisRangeY(double lower, double upper)
virtual void hideTracers()
Hide the traces (vertical and horizontal).
virtual void mousePseudoButtonKeyReleaseEvent(QKeyEvent *event)
virtual void hideAllPlotItems()
PLOTTING / REPLOTTING functions.
virtual QCPRange getRangeX(bool &found_range, int index) const
MOUSE MOVEMENTS mouse/keyboard-triggered.
std::vector< QCPRange * > m_xAxisRangeHistory
List of x axis ranges occurring during the panning zooming actions.
BasePlotContext m_context
tries to keep as much as possible monoisotopes, removing any possible C13 peaks and changes multichar...
Definition: aa.cpp:39
Qt::KeyboardModifiers keyboardModifiers
Qt::MouseButtons mouseButtonsAtMousePress
Qt::MouseButtons lastReleasedMouseButton
Qt::MouseButtons lastPressedMouseButton
Qt::MouseButtons pressedMouseButtons
QString toString() const
Qt::MouseButtons mouseButtonsAtMouseRelease