libpappsomspp
Library for mass spectrometry
basetraceplotwidget.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 "basetraceplotwidget.h"
36#include "../../exception/exceptionnotpossible.h"
37#include "../../pappsoexception.h"
38
39
40namespace pappso
41{
42
43
45 : BasePlotWidget(parent)
46{
47 // We can afford to call createAllAncillaryItems() in this derived class
48 // because all the items will have been created *before* the addition of plots
49 // and then the rendering order will hide them to the viewer, since the
50 // rendering order is according to the order in which the items have been
51 // created.
52 //
53 // The fact that the ancillary items are created before trace plots is not a
54 // problem because the trace plots are sparse and do not effectively hide the
55 // data.
56 //
57 // But, in the color map plot widgets, we cannot afford to create the
58 // ancillary items *before* the plot itself because then, the rendering of the
59 // plot (created after) would screen off the ancillary items (created before).
60 //
61 // So, the createAllAncillaryItems() function needs to be called in the
62 // derived classes at the most appropriate moment in the setting up of the
63 // widget.
65}
66
67
69 const QString &x_axis_label,
70 const QString &y_axis_label)
71 : BasePlotWidget(parent, x_axis_label, y_axis_label)
72{
73 // We can afford to call createAllAncillaryItems() in this derived class
74 // because all the items will have been created *before* the addition of plots
75 // and then the rendering order will hide them to the viewer, since the
76 // rendering order is according to the order in which the items have been
77 // created.
78 //
79 // The fact that the ancillary items are created before trace plots is not a
80 // problem because the trace plots are sparse and do not effectively hide the
81 // data.
82 //
83 // But, in the color map plot widgets, we cannot afford to create the
84 // ancillary items *before* the plot itself because then, the rendering of the
85 // plot (created after) would screen off the ancillary items (created before).
86 //
87 // So, the createAllAncillaryItems() function needs to be called in the
88 // derived classes at the most appropriate moment in the setting up of the
89 // widget.
91}
92
93
94//! Destruct \c this BaseTracePlotWidget instance.
95/*!
96
97 The destruction involves clearing the history, deleting all the axis range
98 history items for x and y axes.
99
100*/
102{
103}
104
105
106void
108 const std::vector<double> &keys,
109 const std::vector<double> &values)
110{
111 QCPGraph *graph_p = graph(graph_index);
112
113 if(graph_p == nullptr)
114 qFatal("Programming error.");
115
116 return setGraphData(graph_p, keys, values);
117}
118
119
120void
122 const std::vector<double> &keys,
123 const std::vector<double> &values)
124{
125 if(graph_p == nullptr)
126 qFatal("Pointer cannot be nullptr.");
127
128 // Version that is now deprecated (20200924)
129 // graph_p->setData(QVector<double>::fromStdVector(keys),
130 // QVector<double>::fromStdVector(values));
131
132 QVector<double> key_qvector;
133 QVector<double> value_qvector;
134
135
136#if 0
137 // Now replace the graph's data. Note that the data are
138 // inherently sorted (true below).
139
140 // The begin() -- end() ranges constructor did not work as of
141 // Qt 5.14.2 this day: 20200721
142
143 key_qvector =
144 QVector(keys.begin(),
145 keys.end());
146 value_qvector =
147 QVector(values.begin(),
148 values.end());
149#endif
150
151 for(auto &value : keys)
152 key_qvector.push_back(value);
153
154 for(auto &value : values)
155 value_qvector.push_back(value);
156
157 graph_p->setData(key_qvector, value_qvector, true);
158
159 graph_p->setPen(m_pen);
160
161 rescaleAxes();
163 replot();
164}
165
166
167void
169{
170 QCPGraph *graph_p = graph(graph_index);
171
172 if(graph_p == nullptr)
173 qFatal("Programming error.");
174
175 graph_p->data().clear();
176
177 rescaleAxes();
179 replot();
180}
181
182
183QCPGraph *
184BaseTracePlotWidget::addTrace(const pappso::Trace &trace, const QColor &color)
185{
186 // qDebug();
187
188 if(!color.isValid())
189 throw PappsoException(
190 QString("The color to be used for the plot graph is invalid."));
191
192 // This seems to be unpleasant.
193 // setFocus();
194
195 QCPGraph *graph_p = addGraph();
196
197 graph_p->setLayer("plotsLayer");
198
199 // Now depracated as of 20200924
200 // graph_p->setData(QVector<double>::fromStdVector(trace.xValues()),
201 // QVector<double>::fromStdVector(trace.yValues()));
202
203 QVector<double> key_qvector;
204 QVector<double> value_qvector;
205
206#if 0
207 // Now replace the graph's data. Note that the data are
208 // inherently sorted (true below).
209
210 // The begin() -- end() ranges constructor did not work as of
211 // Qt 5.14.2 this day: 20200721
212
213 key_qvector =
214 QVector(trace.xValues().begin(),
215 .trace.xValues()end());
216 value_qvector =
217 QVector(trace.yValues().begin(),
218 trace.yValues().end());
219#endif
220
221 for(auto &value : trace.xValues())
222 key_qvector.push_back(value);
223
224 for(auto &value : trace.yValues())
225 value_qvector.push_back(value);
226
227 graph_p->setData(key_qvector, value_qvector, true);
228
229 QPen pen = graph()->pen();
230 pen.setColor(color);
231 graph()->setPen(pen);
232
233 // Connect the signal of selection change so that we can re-emit it for the
234 // widget that is using *this widget.
235
236 connect(graph_p,
237 static_cast<void (QCPAbstractPlottable::*)(bool)>(
238 &QCPAbstractPlottable::selectionChanged),
239 [this, graph_p]() {
240 emit plottableSelectionChangedSignal(graph_p, graph_p->selected());
241 });
242
243 // Rescaling the axes is actually unpleasant if there are more than one
244 // graph in the plot widget and that we are adding one. So only, rescale if
245 // the number of graphs is == 1, that is we are adding the first one.
246
247 if(graphCount() == 1)
248 {
249 rescaleAxes();
251 }
252
253 replot();
254
255 return graph_p;
256}
257
258
259//! Find a minimal integration range starting at an existing data point
260/*!
261
262 If the user clicks onto a plot at a location that is not a true data point,
263 get a data range that begins at the preceding data point and that ends at
264 the clicked location point.
265
266*/
267bool
269 double key,
270 QCPRange &range)
271{
272
273 // Given a key double value, we want to know what is the range that will
274 // frame correctly the key double value if that key value is not exactly
275 // the one of a point of the trace.
276
277 // First of all get the keys of the graph.
278
279 QCPGraph *theGraph = graph(index);
280
281 if(theGraph == nullptr)
283 "basetraceplotwidget.cpp @ indIntegrationLowerRangeForKey() -- ERROR "
284 "theGraph cannot be nullptr.");
285
286 // QCPGraphDataContainer is a typedef QCPDataContainer<QCPGraphData> and
287 // QCPDataContainer< DataType > is a Class Template. So in this context,
288 // DataType is QCPGraphData.
289 // QCPGraphData is the data point, that is the (key,value) pair.
290 QSharedPointer<QCPGraphDataContainer> graph_data_container_p =
291 theGraph->data();
292
293 QCPDataRange dataRange = graph_data_container_p->dataRange();
294
295 if(!dataRange.isValid())
296 return false;
297
298 if(!dataRange.size())
299 return false;
300
301 if(dataRange.size() > 1)
302 {
303 double firstKey = graph_data_container_p->at(dataRange.begin())->key;
304 double lastKey = graph_data_container_p->at(dataRange.end())->key;
305
306 // There is one check to be done: the user might erroneously set the mouse
307 // cursor beyond the last point of the graph. If that is the case, then
308 // upper key needs to be that very point. All we need to do is return the
309 // lower key, that is the pre-last key of the keys list. No need to
310 // iterate in the keys list.
311
312 if(key > lastKey)
313 {
314 // No need to search for the key in the keys, just get the lower key
315 // immediately, that is, the key that is one slot left the last key.
316 range.lower = graph_data_container_p->at(dataRange.end() - 2)->key;
317 range.upper = graph_data_container_p->at(dataRange.end() - 1)->key;
318
319 return true;
320 }
321
322 // Likewise, if the cursor is set left of the first plot point, then that
323 // will be the lower range point. All we need is to provide the upper
324 // range point as the second point of the plot.
325
326 if(key < firstKey)
327 {
328 range.lower = firstKey;
329 range.upper = graph_data_container_p->at(dataRange.begin() + 1)->key;
330
331 return true;
332 }
333
334 // Finally the generic case where the user point to any point *in* the
335 // graph.
336
337 range.lower =
338 graph_data_container_p->findBegin(key, /*expandedRange*/ true)->key;
339 range.upper =
340 std::prev(graph_data_container_p->findEnd(key, /*expandedRange*/ true))
341 ->key;
342
343 return true;
344 }
345
346 return false;
347}
348
349
350std::vector<double>
352{
353 std::vector<double> keys;
354
355 QCPGraph *graph_p = graph(graph_index);
356
357 if(graph_p == nullptr)
358 qFatal("Programming error.");
359
360 QSharedPointer<QCPGraphDataContainer> graph_data_container_p =
361 graph_p->data();
362
363 // Iterate in the keys
364 auto beginIt = graph_data_container_p->begin();
365 auto endIt = graph_data_container_p->end();
366
367 for(auto iter = beginIt; iter != endIt; ++iter)
368 keys.push_back(iter->key);
369
370 return keys;
371}
372
373
374std::vector<double>
376{
377 std::vector<double> values;
378
379 QCPGraph *graph_p = graph(graph_index);
380
381 if(graph_p == nullptr)
382 qFatal("Programming error.");
383
384 QSharedPointer<QCPGraphDataContainer> graph_data_container_p =
385 graph_p->data();
386
387 // Iterate in the values
388 auto beginIt = graph_data_container_p->begin();
389 auto endIt = graph_data_container_p->end();
390
391 for(auto iter = beginIt; iter != endIt; ++iter)
392 values.push_back(iter->key);
393
394 return values;
395}
396
397
398QCPRange
399BaseTracePlotWidget::getValueRangeOnKeyRange(QCPAbstractPlottable *plottable_p,
400 bool &ok)
401{
402
403 // The X axis range is set. But we want to find for that X axis range the
404 // min and max Y values. This function is useful when the user asks that
405 // while changing the X axis range, the trace be always in full scale on the
406 // Y axis.
407
408 QCPRange key_range(xAxis->range().lower, xAxis->range().upper);
409
410 if(plottable_p != nullptr)
411 {
412
413 return plottable_p->getValueRange(ok, QCP::SignDomain::sdBoth, key_range);
414 }
415 else
416 {
417
418 // How many graphs are currently plotted in this plot widget ?
419 int graph_count = graphCount();
420
421 // Iterate in each graph and get the y max value. Then compare with the
422 // largest one and update if necessary. Store the pointer to the graph
423 // that has a larger y value. At the end of the iteration, it will be
424 // the winner.
425
426 double temp_min_value = std::numeric_limits<double>::max();
427 double temp_max_value = std::numeric_limits<double>::min();
428
429 bool found_range = false;
430
431 for(int iter = 0; iter < graph_count; ++iter)
432 {
433 QCPGraph *plottable_p = graph(iter);
434
435 QCPRange value_range =
436 plottable_p->getValueRange(ok, QCP::SignDomain::sdBoth, key_range);
437
438 if(ok)
439 found_range = true;
440
441 if(value_range.lower < temp_min_value)
442 temp_min_value = value_range.lower;
443 if(value_range.upper > temp_max_value)
444 temp_max_value = value_range.upper;
445 }
446
447 // At this point return the range.
448
449 ok = found_range;
450 return QCPRange(temp_min_value, temp_max_value);
451 }
452}
453
454
455QCPRange
457{
458
459 // The X axis range is set. But we want to find for that X axis range the
460 // min and max Y values. This function is useful when the user asks that
461 // while changing the X axis range, the trace be always in full scale on the
462 // Y axis.
463
464 QCPAbstractPlottable *plottable_p = plottable(index);
465
466 if(plottable_p == nullptr)
467 qFatal("Programming error.");
468
469 return getValueRangeOnKeyRange(plottable_p, ok);
470}
471
472
473double
474BaseTracePlotWidget::getYatX(double x, QCPGraph *graph_p)
475{
476 if(graph_p == nullptr)
477 qFatal("Programming error.");
478
479 QCPItemTracer tracer(this);
480 tracer.setGraph(graph_p);
481 tracer.setInterpolating(true);
482 tracer.setGraphKey(x);
483 tracer.updatePosition();
484
485 return tracer.position->value();
486}
487
488
489double
491{
492 QCPGraph *graph_p = graph(index);
493
494 if(graph_p == nullptr)
495 qFatal("Programming error.");
496
497 return getYatX(x, graph_p);
498}
499
500
501void
503 QCPAxis *axis,
504 [[maybe_unused]] QCPAxis::SelectablePart part,
505 QMouseEvent *event)
506{
507 //qDebug();
508
509 m_context.m_keyboardModifiers = QGuiApplication::queryKeyboardModifiers();
510
511 if(m_context.m_keyboardModifiers & Qt::ControlModifier)
512 {
513 //qDebug();
514
515 // If the Ctrl modifiers is active, then both axes are to be reset. Also
516 // the histories are reset also.
517
518 rescaleAxes();
520 }
521 else
522 {
523 //qDebug();
524
525 // Only the axis passed as parameter is to be rescaled.
526 // Reset the range of that axis to the max view possible, but for the y
527 // axis check if the Shift keyboard key is pressed. If so the full scale
528 // should be calculated only on the data in the current x range.
529
530 if(axis->orientation() == Qt::Vertical)
531 {
532 if(m_context.m_keyboardModifiers & Qt::ShiftModifier)
533 {
534
535 // In this case, we want to make a rescale of the Y axis such
536 // that it displays full scale the data in the current X axis
537 // range only.
538
539 bool ok = false;
540
541 QCPRange value_range = getValueRangeOnKeyRange(nullptr, ok);
542
543 yAxis->setRange(value_range);
544 }
545 else
546 axis->rescale();
547 }
548 else
549 axis->rescale();
550
552
553 event->accept();
554 }
555
556 // The double-click event does not cancel the mouse press event. That is, if
557 // left-double-clicking, at the end of the operation the button still
558 // "pressed". We need to remove manually the button from the pressed buttons
559 // context member.
560
561 m_context.m_pressedMouseButtons ^= event->button();
562
564
566
567 replot();
568}
569
570
571void
573{
574 double xLower = xAxis->range().lower;
575 double xUpper = xAxis->range().upper;
576
577 // Get the current y lower/upper range.
578 double yLower = yAxis->range().lower;
579 double yUpper = yAxis->range().upper;
580
581 // This function is called only when the user has clicked on the x/y axis or
582 // when the user has dragged the left mouse button with the Ctrl key
583 // modifier. The m_context.m_wasClickOnXAxis is then simulated in the mouse
584 // move handler. So we need to test which axis was clicked-on.
585
587 {
588
589 // We are changing the range of the X axis.
590
591 // What is the x delta ?
592 double xDelta =
594
595 // If xDelta is < 0, the we were dragging from right to left, we are
596 // compressing the view on the x axis, by adding new data to the right
597 // hand size of the graph. So we add xDelta to the upper bound of the
598 // range. Otherwise we are uncompressing the view on the x axis and
599 // remove the xDelta from the upper bound of the range. This is why we
600 // have the
601 // '-'
602 // and not '+' below;
603
604 // qDebug() << "Setting xaxis:" << xLower << "--" << xUpper - xDelta;
605
606 xAxis->setRange(xLower, xUpper - xDelta);
607
608
609 // Old version
610 // if(xDelta < 0)
611 //{
612 //// The dragging operation was from right to left, we are enlarging
613 //// the range (thus, we are unzooming the view, since the widget
614 //// always has the same size).
615
616 // xAxis->setRange(xLower, xUpper + fabs(xDelta));
617 //}
618 // else
619 //{
620 //// The dragging operation was from left to right, we are reducing
621 //// the range (thus, we are zooming the view, since the widget
622 //// always has the same size).
623
624 // xAxis->setRange(xLower, xUpper - fabs(xDelta));
625 //}
626
627 // We may either leave the scale of the Y axis as is (default) or
628 // the user may want an automatic scale of the Y axis such that the
629 // data displayed in the new X axis range are full scale on the Y
630 // axis. For this, the Shift modifier key should be pressed.
631
632 if(m_context.m_keyboardModifiers & Qt::ShiftModifier)
633 {
634
635 // In this case, we want to make a rescale of the Y axis such that
636 // it displays full scale the data in the current X axis range only.
637
638 bool ok = false;
639
640 QCPRange value_range = getValueRangeOnKeyRange(nullptr, ok);
641
642 yAxis->setRange(value_range);
643 }
644 // else, do leave the Y axis range unchanged.
645 }
646 // End of
647 // if(m_context.m_wasClickOnXAxis)
648 else // that is, if(m_context.m_wasClickOnYAxis)
649 {
650 // We are changing the range of the Y axis.
651
652 // What is the y delta ?
653 double yDelta =
655
656 // See above for an explanation of the computation.
657
658 yAxis->setRange(yLower, yUpper - yDelta);
659
660 // Old version
661 // if(yDelta < 0)
662 //{
663 //// The dragging operation was from top to bottom, we are enlarging
664 //// the range (thus, we are unzooming the view, since the widget
665 //// always has the same size).
666
667 // yAxis->setRange(yLower, yUpper + fabs(yDelta));
668 //}
669 // else
670 //{
671 //// The dragging operation was from bottom to top, we are reducing
672 //// the range (thus, we are zooming the view, since the widget
673 //// always has the same size).
674
675 // yAxis->setRange(yLower, yUpper - fabs(yDelta));
676 //}
677 }
678 // End of
679 // else // that is, if(m_context.m_wasClickOnYAxis)
680
681 // Update the context with the current axes ranges
682
684
686
687 replot();
688}
689
690
691void
693{
694 //qDebug();
695
696 // double sorted_start_drag_point_x =
697 // std::min(m_context.m_startDragPoint.x(), m_context.m_currentDragPoint.x());
698
699 // xAxis->setRange(sorted_start_drag_point_x,
700 // sorted_start_drag_point_x + fabs(m_context.m_xDelta));
701
702 xAxis->setRange(
704
705 // Note that the y axis should be rescaled from current lower value to new
706 // upper value matching the y-axis position of the cursor when the mouse
707 // button was released.
708
709 yAxis->setRange(xAxis->range().lower,
710 std::max<double>(m_context.m_yRegionRangeStart,
712
713 // qDebug() << "xaxis:" << xAxis->range().lower << "-" <<
714 // xAxis->range().upper
715 //<< "yaxis:" << yAxis->range().lower << "-" << yAxis->range().upper;
716
717 // If the shift modifier key is pressed, then the user want the y axis
718 // to be full scale.
719 if(m_context.m_keyboardModifiers & Qt::ShiftModifier)
720 {
721
722 bool ok = false;
723
724 QCPRange value_range = getValueRangeOnKeyRange(nullptr, ok);
725
726 yAxis->setRange(value_range);
727 }
728 // else do nothing, let the y axis range as is.
729
731
734
735 replot();
736}
737
738
739void
741{
742
743 // Use the m_context.m_xRegionRangeStart/End values, but we need to sort the
744 // values before using them, because now we want to really have the lower x
745 // value. Simply craft a QCPRange that will swap the values if lower is not
746 // < than upper QCustomPlot calls this normalization).
747
748 xAxis->setRange(
750
751 // If the shift modifier key is pressed, then the user want the y axis
752 // to be full scale.
753 if(m_context.m_keyboardModifiers & Qt::ShiftModifier)
754 {
755
756 bool ok = false;
757
758 QCPRange value_range = getValueRangeOnKeyRange(nullptr, ok);
759
760 yAxis->setRange(value_range);
761 }
762 else
763 yAxis->setRange(
765
767
770
771 replot();
772}
773
774void
776{
777 //qDebug();
778
779 // Sanity check
781 qFatal(
782 "This function can only be called if the mouse click was on one of the "
783 "axes");
784
786 {
787 xAxis->setRange(m_context.m_xRange.lower - m_context.m_xDelta,
789
790 // If the shift modifier key is pressed, then the user want the y axis
791 // to be full scale.
792 if(m_context.m_keyboardModifiers & Qt::ShiftModifier)
793 {
794
795 bool ok = false;
796
797 QCPRange value_range = getValueRangeOnKeyRange(nullptr, ok);
798
799 yAxis->setRange(value_range);
800 }
801 // else nothing to do we do not change the y axis scale.
802 }
803
805 {
806 yAxis->setRange(m_context.m_yRange.lower - m_context.m_yDelta,
808 }
809
811
812 //qDebug() << "The updated context:" << m_context.toString();
813
814 // We cannot store the new ranges in the history, because the pan operation
815 // involved a huge quantity of micro-movements elicited upon each mouse move
816 // cursor event so we would have a huge history.
817 // updateAxesRangeHistory();
818
819 // Now that the contex has the right range values, we can emit the
820 // signal that will be used by this plot widget users, typically to
821 // abide by the x/y range lock required by the user.
822
824
825 replot();
826}
827
828
831{
832 QCPGraph *graph_p = graph(index);
833
834 return toTrace(graph_p);
835}
836
837
839BaseTracePlotWidget::toTrace(const QCPGraph *graph_p) const
840{
841 if(graph_p == nullptr)
842 qFatal("Programming error. Pointer cannot be nullptr.");
843
844 pappso::Trace trace;
845
846 QSharedPointer<QCPGraphDataContainer> graph_data_container_p =
847 graph_p->data();
848
849 // Iterate in the keys
850 auto beginIt = graph_data_container_p->begin();
851 auto endIt = graph_data_container_p->end();
852
853 for(auto iter = beginIt; iter != endIt; ++iter)
854 trace.push_back(pappso::DataPoint(iter->key, iter->value));
855
856 return trace;
857}
858
859
861BaseTracePlotWidget::toTrace(const QCPRange &x_axis_range, int index) const
862{
863 QCPGraph *graph_p = graph(index);
864
865 if(graph_p == nullptr)
866 qFatal("Programming error.");
867
868 return toTrace(x_axis_range, graph_p);
869}
870
871
873BaseTracePlotWidget::toTrace(const QCPRange &x_axis_range,
874 const QCPGraph *graph_p) const
875{
876
877 // Make a Trace with the data in the range.
878 Trace data_trace;
879
880 QSharedPointer<QCPGraphDataContainer> graph_data_container_sp;
881
882 graph_data_container_sp = graph_p->data();
883
884 // Grab the iterator to the start to the x axis range
885 auto beginIt = graph_data_container_sp->findBegin(x_axis_range.lower,
886 /*expandedRange*/ true);
887 // Grab the iterator to the end of the axis range
888 auto endIt = graph_data_container_sp->findEnd(x_axis_range.upper,
889 /*expandedRange*/ true);
890
891 for(auto iter = beginIt; iter != endIt; ++iter)
892 data_trace.push_back(DataPoint(iter->key, iter->value));
893
894 return data_trace;
895}
896
897
898} // namespace pappso
Qt::KeyboardModifiers m_keyboardModifiers
Qt::MouseButtons m_pressedMouseButtons
virtual void updateAxesRangeHistory()
Create new axis range history items and append them to the history.
virtual void createAllAncillaryItems()
QPen m_pen
Pen used to draw the graph and textual elements in the plot widget.
virtual void resetAxesRangeHistory()
virtual void updateContextXandYAxisRanges()
void plottableSelectionChangedSignal(QCPAbstractPlottable *plottable_p, bool selected)
void plotRangesChangedSignal(const BasePlotContext &context)
BasePlotContext m_context
QCPRange getValueRangeOnKeyRange(QCPAbstractPlottable *plottable_p, bool &ok)
virtual bool findIntegrationLowerRangeForKey(int index, double key, QCPRange &range)
Find a minimal integration range starting at an existing data point.
virtual void axisDoubleClickHandler(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event) override
virtual void axisRescale() override
RANGE-related functions.
virtual ~BaseTracePlotWidget()
Destruct this BaseTracePlotWidget instance.
virtual void setGraphData(int graph_index, const std::vector< double > &keys, const std::vector< double > &values)
std::vector< double > getValuesY(int index) const
BaseTracePlotWidget(QWidget *parent=0)
virtual QCPGraph * addTrace(const pappso::Trace &trace, const QColor &color)
std::vector< double > getValuesX(int index) const
virtual void axisPan() override
virtual void axisZoom() override
virtual void clearGraphData(int graph_index)
pappso::Trace toTrace(int index) const
double getYatX(double x, QCPGraph *graph_p)
virtual void axisReframe() override
A simple container of DataPoint instances.
Definition: trace.h:148
std::vector< pappso_double > xValues() const
Definition: trace.cpp:623
std::vector< pappso_double > yValues() const
Definition: trace.cpp:637
tries to keep as much as possible monoisotopes, removing any possible C13 peaks and changes multichar...
Definition: aa.cpp:39