##// END OF EJS Templates
Fixes compilation warning
Alexandre Leroux -
r1014:a57177af4c0c
parent child
Show More
@@ -1,228 +1,228
1 1 #ifndef SCIQLOP_DATASERIESUTILS_H
2 2 #define SCIQLOP_DATASERIESUTILS_H
3 3
4 4 #include "CoreGlobal.h"
5 5
6 6 #include <Data/DataSeriesIterator.h>
7 7
8 #include <cmath>
9 8 #include <QLoggingCategory>
9 #include <cmath>
10 10
11 11 Q_DECLARE_LOGGING_CATEGORY(LOG_DataSeriesUtils)
12 12
13 13 /**
14 14 * Utility class with methods for data series
15 15 */
16 16 struct SCIQLOP_CORE_EXPORT DataSeriesUtils {
17 17 /**
18 18 * Define a meshs.
19 19 *
20 20 * A mesh is a regular grid representing cells of the same width (in x) and of the same height
21 21 * (in y). At each mesh point is associated a value.
22 22 *
23 23 * Each axis of the mesh is defined by a minimum value, a number of values is a mesh step.
24 24 * For example: if min = 1, nbValues = 5 and step = 2 => the axis of the mesh will be [1, 3, 5,
25 25 * 7, 9].
26 26 *
27 27 * The values are defined in an array of size {nbX * nbY}. The data is stored along the X axis.
28 28 *
29 29 * For example, the mesh:
30 30 * Y = 2 [ 7 ; 8 ; 9
31 31 * Y = 1 4 ; 5 ; 6
32 32 * Y = 0 1 ; 2 ; 3 ]
33 33 * X = 0 X = 1 X = 2
34 34 *
35 35 * will be represented by data [1, 2, 3, 4, 5, 6, 7, 8, 9]
36 36 */
37 37 struct Mesh {
38 38 explicit Mesh() = default;
39 39 explicit Mesh(int nbX, double xMin, double xStep, int nbY, double yMin, double yStep)
40 40 : m_NbX{nbX},
41 41 m_XMin{xMin},
42 42 m_XStep{xStep},
43 43 m_NbY{nbY},
44 44 m_YMin{yMin},
45 45 m_YStep{yStep},
46 46 m_Data(nbX * nbY)
47 47 {
48 48 }
49 49
50 50 inline bool isEmpty() const { return m_Data.size() == 0; }
51 51 inline double xMax() const { return m_XMin + (m_NbX - 1) * m_XStep; }
52 52 inline double yMax() const { return m_YMin + (m_NbY - 1) * m_YStep; }
53 53
54 54 int m_NbX{0};
55 55 double m_XMin{};
56 56 double m_XStep{};
57 57 int m_NbY{0};
58 58 double m_YMin{};
59 59 double m_YStep{};
60 60 std::vector<double> m_Data{};
61 61 };
62 62
63 63 /**
64 64 * Represents a resolution used to generate the data of a mesh on the x-axis or in Y.
65 65 *
66 66 * A resolution is represented by a value and flag indicating if it's in the logarithmic scale
67 67 * @sa Mesh
68 68 */
69 69 struct Resolution {
70 70 double m_Val{std::numeric_limits<double>::quiet_NaN()};
71 71 bool m_Logarithmic{false};
72 72 };
73 73
74 74 /**
75 75 * Processes data from a data series to complete the data holes with a fill value.
76 76 *
77 77 * A data hole is determined by the resolution passed in parameter: if, between two continuous
78 78 * data on the x-axis, the difference between these data is greater than the resolution, then
79 79 * there is one or more holes between them. The holes are filled by adding:
80 80 * - for the x-axis, new data corresponding to the 'step resolution' starting from the first
81 81 * data;
82 82 * - for values, a default value (fill value) for each new data added on the x-axis.
83 83 *
84 84 * For example, with :
85 85 * - xAxisData = [0, 1, 5, 7, 14 ]
86 86 * - valuesData = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] (two components per x-axis data)
87 87 * - fillValue = NaN
88 88 * - and resolution = 2;
89 89 *
90 90 * For the x axis, we calculate as data holes: [3, 9, 11, 13]. These holes are added to the
91 91 * x-axis data, and NaNs (two per x-axis data) are added to the values:
92 92 * => xAxisData = [0, 1, 3, 5, 7, 9, 11, 13, 14 ]
93 93 * => valuesData = [0, 1, 2, 3, NaN, NaN, 4, 5, 6, 7, NaN, NaN, NaN, NaN, NaN, NaN, 8, 9]
94 94 *
95 95 * It is also possible to set bounds for the data series. If these bounds are defined and exceed
96 96 * the limits of the data series, data holes are added to the series at the beginning and/or the
97 97 * end.
98 98 *
99 99 * The generation of data holes at the beginning/end of the data series is performed starting
100 100 * from the x-axis series limit and adding data holes at each 'resolution step' as long as the
101 101 * new bound is not reached.
102 102 *
103 103 * For example, with :
104 104 * - xAxisData = [3, 4, 5, 6, 7 ]
105 105 * - valuesData = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
106 106 * - fillValue = NaN
107 107 * - minBound = 0
108 108 * - maxBound = 12
109 109 * - and resolution = 2;
110 110 *
111 111 * => Starting from 3 and decreasing 2 by 2 until reaching 0 : a data hole at value 1 will be
112 112 * added to the beginning of the series
113 113 * => Starting from 7 and increasing 2 by 2 until reaching 12 : data holes at values 9 and 11
114 114 * will be added to the end of the series
115 115 *
116 116 * So :
117 117 * => xAxisData = [1, 3, 4, 5, 6, 7, 9, 11 ]
118 118 * => valuesData = [NaN, NaN, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, NaN, NaN, NaN, NaN]
119 119 *
120 120 * @param xAxisData the x-axis data of the data series
121 121 * @param valuesData the values data of the data series
122 122 * @param resolution the resoultion (on x-axis) used to determinate data holes
123 123 * @param fillValue the fill value used for data holes in the values data
124 124 * @param minBound the limit at which to start filling data holes for the series. If set to NaN,
125 125 * the limit is not used
126 126 * @param maxBound the limit at which to end filling data holes for the series. If set to NaN,
127 127 * the limit is not used
128 128 *
129 129 * @remarks There is no control over the consistency between x-axis data and values data. The
130 130 * method considers that the data is well formed (the total number of values data is a multiple
131 131 * of the number of x-axis data)
132 132 */
133 133 static void fillDataHoles(std::vector<double> &xAxisData, std::vector<double> &valuesData,
134 134 double resolution,
135 135 double fillValue = std::numeric_limits<double>::quiet_NaN(),
136 136 double minBound = std::numeric_limits<double>::quiet_NaN(),
137 137 double maxBound = std::numeric_limits<double>::quiet_NaN());
138 138 /**
139 139 * Computes the resolution of a dataset passed as a parameter.
140 140 *
141 141 * The resolution of a dataset is the minimum difference between two values that follow in the
142 142 * set.
143 143 * For example:
144 144 * - for the set [0, 2, 4, 8, 10, 11, 13] => the resolution is 1 (difference between 10 and 11).
145 145 *
146 146 * A resolution can be calculated on the logarithmic scale (base of 10). In this case, the
147 147 * dataset is first converted to logarithmic values.
148 148 * For example:
149 149 * - for the set [10, 100, 10000, 1000000], the values are converted to [1, 2, 4, 6] => the
150 150 * logarithmic resolution is 1 (difference between 1 and 2).
151 151 *
152 152 * @param begin the iterator pointing to the beginning of the dataset
153 153 * @param end the iterator pointing to the end of the dataset
154 154 * @param logarithmic computes a logarithmic resolution or not
155 155 * @return the resolution computed
156 156 * @warning the method considers the dataset as sorted and doesn't control it.
157 157 */
158 158 template <typename Iterator>
159 159 static Resolution resolution(Iterator begin, Iterator end, bool logarithmic = false);
160 160
161 161 /**
162 162 * Computes a regular mesh for a data series, according to resolutions for x-axis and y-axis
163 163 * passed as parameters.
164 164 *
165 165 * The mesh is created from the resolutions in x and y and the boundaries delimiting the data
166 166 * series. If the resolutions do not allow to obtain a regular mesh, they are recalculated.
167 167 *
168 168 * For example :
169 169 * Let x-axis data = [0, 1, 3, 5, 9], its associated values ​​= [0, 10, 30, 50, 90] and
170 170 * xResolution = 2.
171 171 * Based on the resolution, the mesh would be [0, 2, 4, 6, 8, 10] and would be invalid because
172 172 * it exceeds the maximum bound of the data. The resolution is thus recalculated so that the
173 173 * mesh holds between the data terminals.
174 174 * So => resolution is 1.8 and the mesh is [0, 1.8, 3.6, 5.4, 7.2, 9].
175 175 *
176 176 * Once the mesh is generated in x and y, the values ​​are associated with each mesh point,
177 177 * based on the data in the series, finding the existing data at which the mesh point would be
178 178 * or would be closest to, without exceeding it.
179 179 *
180 180 * In the example, we determine the value of each mesh point:
181 181 * - x = 0 => value = 0 (existing x in the data series)
182 182 * - x = 1.8 => value = 10 (the closest existing x: 1)
183 183 * - x = 3.6 => value = 30 (the closest existing x: 3)
184 184 * - x = 5.4 => value = 50 (the closest existing x: 5)
185 185 * - x = 7.2 => value = 50 (the closest existing x: 5)
186 186 * - x = 9 => value = 90 (existing x in the data series)
187 187 *
188 188 * Same algorithm is applied for y-axis.
189 189 *
190 190 * @param begin the iterator pointing to the beginning of the data series
191 191 * @param end the iterator pointing to the end of the data series
192 192 * @param xResolution the resolution expected for the mesh's x-axis
193 193 * @param yResolution the resolution expected for the mesh's y-axis
194 194 * @return the mesh created, an empty mesh if the input data do not allow to generate a regular
195 195 * mesh (empty data, null resolutions, logarithmic x-axis)
196 196 * @warning the method considers the dataset as sorted and doesn't control it.
197 197 */
198 198 static Mesh regularMesh(DataSeriesIterator begin, DataSeriesIterator end,
199 199 Resolution xResolution, Resolution yResolution);
200 200 };
201 201
202 202 template <typename Iterator>
203 203 DataSeriesUtils::Resolution DataSeriesUtils::resolution(Iterator begin, Iterator end,
204 204 bool logarithmic)
205 205 {
206 206 // Retrieves data into a work dataset
207 207 using ValueType = typename Iterator::value_type;
208 208 std::vector<ValueType> values{};
209 209 std::copy(begin, end, std::back_inserter(values));
210 210
211 211 // Converts data if logarithmic flag is activated
212 212 if (logarithmic) {
213 213 std::for_each(values.begin(), values.end(),
214 214 [logarithmic](auto &val) { val = std::log10(val); });
215 215 }
216 216
217 217 // Computes the differences between the values in the dataset
218 218 std::adjacent_difference(values.begin(), values.end(), values.begin());
219 219
220 220 // Retrieves the smallest difference
221 221 auto resolutionIt = std::min_element(values.begin(), values.end());
222 222 auto resolution
223 223 = resolutionIt != values.end() ? *resolutionIt : std::numeric_limits<double>::quiet_NaN();
224 224
225 225 return Resolution{resolution, logarithmic};
226 226 }
227 227
228 228 #endif // SCIQLOP_DATASERIESUTILS_H
@@ -1,100 +1,100
1 1
2 2 gui_moc_headers = [
3 3 'include/DataSource/DataSourceWidget.h',
4 4 'include/Settings/SqpSettingsDialog.h',
5 5 'include/Settings/SqpSettingsGeneralWidget.h',
6 6 'include/SidePane/SqpSidePane.h',
7 7 'include/SqpApplication.h',
8 8 'include/DragAndDrop/DragDropScroller.h',
9 9 'include/DragAndDrop/DragDropTabSwitcher.h',
10 10 'include/TimeWidget/TimeWidget.h',
11 11 'include/Variable/VariableInspectorWidget.h',
12 12 'include/Variable/RenameVariableDialog.h',
13 13 'include/Visualization/qcustomplot.h',
14 14 'include/Visualization/VisualizationGraphWidget.h',
15 15 'include/Visualization/VisualizationTabWidget.h',
16 16 'include/Visualization/VisualizationWidget.h',
17 17 'include/Visualization/VisualizationZoneWidget.h',
18 18 'include/Visualization/VisualizationDragDropContainer.h',
19 19 'include/Visualization/VisualizationDragWidget.h',
20 'include/Visualization/ColorScaleWidget.h'
20 'include/Visualization/ColorScaleEditor.h'
21 21 ]
22 22
23 23 gui_ui_files = [
24 24 'ui/DataSource/DataSourceWidget.ui',
25 25 'ui/Settings/SqpSettingsDialog.ui',
26 26 'ui/Settings/SqpSettingsGeneralWidget.ui',
27 27 'ui/SidePane/SqpSidePane.ui',
28 28 'ui/TimeWidget/TimeWidget.ui',
29 29 'ui/Variable/VariableInspectorWidget.ui',
30 30 'ui/Variable/RenameVariableDialog.ui',
31 31 'ui/Variable/VariableMenuHeaderWidget.ui',
32 32 'ui/Visualization/VisualizationGraphWidget.ui',
33 33 'ui/Visualization/VisualizationTabWidget.ui',
34 34 'ui/Visualization/VisualizationWidget.ui',
35 35 'ui/Visualization/VisualizationZoneWidget.ui',
36 'ui/Visualization/ColorScaleWidget.ui'
36 'ui/Visualization/ColorScaleEditor.ui'
37 37 ]
38 38
39 39 gui_qresources = ['resources/sqpguiresources.qrc']
40 40
41 41 gui_moc_files = qt5.preprocess(moc_headers : gui_moc_headers,
42 42 ui_files : gui_ui_files,
43 43 qresources : gui_qresources)
44 44
45 45 gui_sources = [
46 46 'src/SqpApplication.cpp',
47 47 'src/DragAndDrop/DragDropHelper.cpp',
48 48 'src/DragAndDrop/DragDropScroller.cpp',
49 49 'src/DragAndDrop/DragDropTabSwitcher.cpp',
50 50 'src/Common/ColorUtils.cpp',
51 51 'src/Common/VisualizationDef.cpp',
52 52 'src/DataSource/DataSourceTreeWidgetItem.cpp',
53 53 'src/DataSource/DataSourceTreeWidgetHelper.cpp',
54 54 'src/DataSource/DataSourceWidget.cpp',
55 55 'src/DataSource/DataSourceTreeWidget.cpp',
56 56 'src/Settings/SqpSettingsDialog.cpp',
57 57 'src/Settings/SqpSettingsGeneralWidget.cpp',
58 58 'src/SidePane/SqpSidePane.cpp',
59 59 'src/TimeWidget/TimeWidget.cpp',
60 60 'src/Variable/VariableInspectorWidget.cpp',
61 61 'src/Variable/VariableInspectorTableView.cpp',
62 62 'src/Variable/VariableMenuHeaderWidget.cpp',
63 63 'src/Variable/RenameVariableDialog.cpp',
64 64 'src/Visualization/VisualizationGraphHelper.cpp',
65 65 'src/Visualization/VisualizationGraphRenderingDelegate.cpp',
66 66 'src/Visualization/VisualizationGraphWidget.cpp',
67 67 'src/Visualization/VisualizationTabWidget.cpp',
68 68 'src/Visualization/VisualizationWidget.cpp',
69 69 'src/Visualization/VisualizationZoneWidget.cpp',
70 70 'src/Visualization/qcustomplot.cpp',
71 71 'src/Visualization/QCustomPlotSynchronizer.cpp',
72 72 'src/Visualization/operations/FindVariableOperation.cpp',
73 73 'src/Visualization/operations/GenerateVariableMenuOperation.cpp',
74 74 'src/Visualization/operations/MenuBuilder.cpp',
75 75 'src/Visualization/operations/RemoveVariableOperation.cpp',
76 76 'src/Visualization/operations/RescaleAxeOperation.cpp',
77 77 'src/Visualization/VisualizationDragDropContainer.cpp',
78 78 'src/Visualization/VisualizationDragWidget.cpp',
79 79 'src/Visualization/AxisRenderingUtils.cpp',
80 80 'src/Visualization/PlottablesRenderingUtils.cpp',
81 81 'src/Visualization/MacScrollBarStyle.cpp',
82 82 'src/Visualization/VisualizationCursorItem.cpp',
83 'src/Visualization/ColorScaleWidget.cpp',
83 'src/Visualization/ColorScaleEditor.cpp',
84 84 'src/Visualization/SqpColorScale.cpp'
85 85 ]
86 86
87 87 gui_inc = include_directories(['include'])
88 88
89 89 sciqlop_gui_lib = library('sciqlopgui',
90 90 gui_sources,
91 91 gui_moc_files,
92 92 include_directories : [gui_inc],
93 93 dependencies : [ qt5printsupport, qt5gui, qt5widgets, qt5svg, sciqlop_core],
94 94 install : true
95 95 )
96 96
97 97 sciqlop_gui = declare_dependency(link_with : sciqlop_gui_lib,
98 98 include_directories : gui_inc,
99 99 dependencies : [qt5printsupport, qt5gui, qt5widgets, qt5svg, sciqlop_core])
100 100
@@ -1,164 +1,164
1 1 #include "Visualization/ColorScaleEditor.h"
2 2 #include "Visualization/SqpColorScale.h"
3 3
4 4 #include "ui_ColorScaleEditor.h"
5 5
6 6 namespace {
7 7
8 8 const auto GRADIENTS = QVariantMap{{"Candy", QCPColorGradient::gpCandy},
9 9 {"Cold", QCPColorGradient::gpCold},
10 10 {"Geography", QCPColorGradient::gpGeography},
11 11 {"Grayscale", QCPColorGradient::gpGrayscale},
12 12 {"Hot", QCPColorGradient::gpHot},
13 13 {"Hues", QCPColorGradient::gpHues},
14 14 {"Ion", QCPColorGradient::gpIon},
15 15 {"Jet", QCPColorGradient::gpJet},
16 16 {"Night", QCPColorGradient::gpNight},
17 17 {"Polar", QCPColorGradient::gpPolar},
18 18 {"Spectrum", QCPColorGradient::gpSpectrum},
19 19 {"Thermal", QCPColorGradient::gpThermal}};
20 20
21 21 } // namespace
22 22
23 23 ColorScaleEditor::ColorScaleEditor(SqpColorScale &scale, QWidget *parent)
24 24 : QDialog{parent},
25 25 ui{new Ui::ColorScaleEditor},
26 m_Scale{scale},
27 m_ThresholdGroup{new QButtonGroup{this}}
26 m_ThresholdGroup{new QButtonGroup{this}},
27 m_Scale{scale}
28 28 {
29 29 ui->setupUi(this);
30 30
31 31 // Inits gradient combobox content
32 32 for (auto it = GRADIENTS.begin(), end = GRADIENTS.end(); it != end; ++it) {
33 33 ui->gradientComboBox->addItem(it.key(), it.value());
34 34 }
35 35
36 36 // Creates threshold group
37 37 m_ThresholdGroup->addButton(ui->thresholdAutoButton);
38 38 m_ThresholdGroup->addButton(ui->thresholdManualButton);
39 39
40 40 // Inits min/max spinboxes' properties
41 41 auto setSpinBoxProperties = [](auto &spinBox) {
42 42 spinBox.setDecimals(3);
43 43 spinBox.setMinimum(-std::numeric_limits<double>::max());
44 44 spinBox.setMaximum(std::numeric_limits<double>::max());
45 45 };
46 46 setSpinBoxProperties(*ui->minSpinBox);
47 47 setSpinBoxProperties(*ui->maxSpinBox);
48 48
49 49 // Creates color scale preview
50 50 m_PreviewScale = new QCPColorScale{ui->plot};
51 51 m_PreviewScale->setType(QCPAxis::atTop);
52 52 m_PreviewScale->setMinimumMargins(QMargins{5, 5, 5, 5});
53 53 m_PreviewScale->axis()->setScaleType(QCPAxis::stLogarithmic);
54 54 m_PreviewScale->axis()->setNumberPrecision(0);
55 55 m_PreviewScale->axis()->setNumberFormat("eb");
56 56 m_PreviewScale->axis()->setTicker(QSharedPointer<QCPAxisTickerLog>::create());
57 57 m_PreviewScale->setGradient(QCPColorGradient{QCPColorGradient::gpJet});
58 58
59 59 ui->plot->plotLayout()->clear();
60 60 ui->plot->plotLayout()->insertRow(0);
61 61 ui->plot->plotLayout()->addElement(0, 0, m_PreviewScale);
62 62
63 63 // Inits connections
64 64 connect(ui->gradientComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(updatePreview()));
65 65 connect(ui->thresholdAutoButton, SIGNAL(toggled(bool)), this, SLOT(onThresholdChanged(bool)));
66 66 connect(ui->thresholdManualButton, SIGNAL(toggled(bool)), this, SLOT(onThresholdChanged(bool)));
67 67 connect(ui->minSpinBox, SIGNAL(editingFinished()), this, SLOT(onMinChanged()));
68 68 connect(ui->maxSpinBox, SIGNAL(editingFinished()), this, SLOT(onMaxChanged()));
69 69
70 70 // OK/cancel buttons
71 71 connect(ui->okButton, SIGNAL(clicked(bool)), this, SLOT(accept()));
72 72 connect(ui->cancelButton, SIGNAL(clicked(bool)), this, SLOT(reject()));
73 73
74 74 // Loads color scale
75 75 loadScale();
76 76 }
77 77
78 78 ColorScaleEditor::~ColorScaleEditor() noexcept
79 79 {
80 80 delete ui;
81 81 }
82 82
83 83 void ColorScaleEditor::loadScale()
84 84 {
85 85 // Gradient
86 86 auto gradientPresetIndex = ui->gradientComboBox->findData(m_Scale.m_GradientPreset);
87 87 ui->gradientComboBox->setCurrentIndex(gradientPresetIndex);
88 88
89 89 // Threshold mode
90 90 (m_Scale.m_AutomaticThreshold ? ui->thresholdAutoButton : ui->thresholdManualButton)
91 91 ->setChecked(true);
92 92
93 93 // Min/max
94 94 auto qcpColorScale = m_Scale.m_Scale;
95 95 auto range = qcpColorScale->dataRange();
96 96 ui->minSpinBox->setValue(range.lower);
97 97 ui->maxSpinBox->setValue(range.upper);
98 98
99 99 updatePreview();
100 100 }
101 101
102 102 void ColorScaleEditor::saveScale()
103 103 {
104 104 auto qcpColorScale = m_Scale.m_Scale;
105 105
106 106 // Gradient
107 107 auto gradientPreset
108 108 = ui->gradientComboBox->currentData().value<QCPColorGradient::GradientPreset>();
109 109 qcpColorScale->setGradient(gradientPreset);
110 110 m_Scale.m_GradientPreset = gradientPreset;
111 111
112 112 // Threshold mode
113 113 m_Scale.m_AutomaticThreshold = ui->thresholdAutoButton->isChecked();
114 114
115 115 // Min/max
116 116 qcpColorScale->setDataRange(QCPRange{ui->minSpinBox->value(), ui->maxSpinBox->value()});
117 117 }
118 118
119 119 void ColorScaleEditor::accept()
120 120 {
121 121 saveScale();
122 122 QDialog::accept();
123 123 }
124 124
125 125 void ColorScaleEditor::onMaxChanged()
126 126 {
127 127 // Ensures that max >= min
128 128 auto maxValue = ui->maxSpinBox->value();
129 129 if (maxValue < ui->minSpinBox->value()) {
130 130 ui->minSpinBox->setValue(maxValue);
131 131 }
132 132
133 133 updatePreview();
134 134 }
135 135
136 136 void ColorScaleEditor::onMinChanged()
137 137 {
138 138 // Ensures that min <= max
139 139 auto minValue = ui->minSpinBox->value();
140 140 if (minValue > ui->maxSpinBox->value()) {
141 141 ui->maxSpinBox->setValue(minValue);
142 142 }
143 143
144 144 updatePreview();
145 145 }
146 146
147 147 void ColorScaleEditor::onThresholdChanged(bool checked)
148 148 {
149 149 if (checked) {
150 150 auto isAutomatic = ui->thresholdAutoButton == m_ThresholdGroup->checkedButton();
151 151
152 152 ui->minSpinBox->setEnabled(!isAutomatic);
153 153 ui->maxSpinBox->setEnabled(!isAutomatic);
154 154 }
155 155 }
156 156
157 157 void ColorScaleEditor::updatePreview()
158 158 {
159 159 m_PreviewScale->setDataRange(QCPRange{ui->minSpinBox->value(), ui->maxSpinBox->value()});
160 160 m_PreviewScale->setGradient(
161 161 ui->gradientComboBox->currentData().value<QCPColorGradient::GradientPreset>());
162 162
163 163 ui->plot->replot();
164 164 }
General Comments 0
You need to be logged in to leave comments. Login now