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