PulseView  unreleased development snapshot
A Qt-based sigrok GUI
enum.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of the PulseView project.
3  *
4  * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include <cassert>
21 #include <cfloat>
22 #include <cmath>
23 #include <limits>
24 #include <vector>
25 
26 #include <QComboBox>
27 #include <QDebug>
28 #include <QHBoxLayout>
29 #include <QLabel>
30 #include <QSlider>
31 
32 #include <libsigrokcxx/libsigrokcxx.hpp>
33 
34 #include "enum.hpp"
35 
36 using std::abs;
37 // Note that "using std::isnan;" is _not_ put here since that would break
38 // compilation on some platforms. Use "std::isnan()" instead in checks below.
39 using std::numeric_limits;
40 using std::pair;
41 using std::vector;
42 
43 namespace pv {
44 namespace prop {
45 
46 Enum::Enum(QString name, QString desc,
47  vector<pair<Glib::VariantBase, QString> > values,
48  Getter getter, Setter setter) :
49  Property(name, desc, getter, setter),
50  values_(values),
51  is_range_(false),
52  selector_(nullptr),
53  slider_layout_widget_(nullptr),
54  slider_(nullptr),
55  slider_label_(nullptr)
56 {
57  // Try to determine whether the values make up a range, created by e.g.
58  // std_gvar_min_max_step_thresholds()
59 
60  vector<double> deltas;
61  double prev_value = 0;
62 
63  for (const pair<Glib::VariantBase, QString> &v : values_) {
64  gdouble value;
65  if (v.first.is_of_type(Glib::VariantType("d"))) {
66  g_variant_get((GVariant*)(v.first.gobj()), "d", &value);
67  } else if (v.first.is_of_type(Glib::VariantType("(dd)"))) {
68  gdouble dummy;
69  g_variant_get((GVariant*)(v.first.gobj()), "(dd)", &value, &dummy);
70  } else
71  break; // Type not d or (dd), so not a range that we can handle
72  deltas.push_back(value - prev_value);
73  prev_value = value;
74  }
75 
76  if (deltas.size() > 0) {
77  bool constant_delta = true;
78  double prev_delta = numeric_limits<double>::quiet_NaN();
79 
80  bool skip_first = true;
81  for (double delta : deltas) {
82  // First value is incorrect, it's the delta to 0 since no
83  // previous value existed yet
84  if (skip_first) {
85  skip_first = false;
86  continue;
87  }
88  if (std::isnan(prev_delta))
89  prev_delta = delta;
90 
91  // 2*DBL_EPSILON doesn't work here, so use a workaround
92  if (abs(delta - prev_delta) > (delta/10))
93  constant_delta = false;
94 
95  prev_delta = delta;
96  }
97 
98  if (constant_delta)
99  is_range_ = true;
100  }
101 }
102 
103 QWidget* Enum::get_widget(QWidget *parent, bool auto_commit)
104 {
105  if (!getter_)
106  return nullptr;
107 
108  Glib::VariantBase variant;
109 
110  try {
111  variant = getter_();
112  } catch (const sigrok::Error &e) {
113  qWarning() << tr("Querying config key %1 resulted in %2").arg(name_, e.what());
114  return nullptr;
115  }
116 
117  if (!variant.gobj())
118  return nullptr;
119 
120  if (is_range_) {
121  // Use slider
123  return slider_layout_widget_;
124 
125  slider_ = new QSlider();
126  // Sliders can't handle float values, so we just use it to specify
127  // the number of steps that we're away from the range's beginning
128  slider_->setOrientation(Qt::Horizontal);
129  slider_->setMinimum(0);
130  slider_->setMaximum(values_.size() - 1);
131  slider_->setSingleStep(1);
132 
133  slider_label_ = new QLabel();
134 
135  slider_layout_widget_ = new QWidget(parent);
136  QHBoxLayout *layout = new QHBoxLayout(slider_layout_widget_);
137  layout->addWidget(slider_);
138  layout->addWidget(slider_label_);
139 
140  update_widget();
141 
142  if (auto_commit)
143  connect(slider_, SIGNAL(valueChanged(int)),
144  this, SLOT(on_value_changed(int)));
145 
146  return slider_layout_widget_;
147 
148  } else {
149  // Use combo box
150  if (selector_)
151  return selector_;
152 
153  selector_ = new QComboBox(parent);
154  for (unsigned int i = 0; i < values_.size(); i++) {
155  const pair<Glib::VariantBase, QString> &v = values_[i];
156  selector_->addItem(v.second, QVariant::fromValue(v.first));
157  }
158 
159  update_widget();
160 
161  if (auto_commit)
162  connect(selector_, SIGNAL(currentIndexChanged(int)),
163  this, SLOT(on_current_index_changed(int)));
164 
165  return selector_;
166  }
167 }
168 
170 {
171  Glib::VariantBase variant;
172 
173  try {
174  variant = getter_();
175  } catch (const sigrok::Error &e) {
176  qWarning() << tr("Querying config key %1 resulted in %2").arg(name_, e.what());
177  return;
178  }
179 
180  assert(variant.gobj());
181 
182  if (is_range_) {
183 
184  // Use slider
186  return;
187 
188  for (unsigned int i = 0; i < values_.size(); i++) {
189  const pair<Glib::VariantBase, QString> &v = values_[i];
190 
191  // g_variant_equal() doesn't handle floating point properly
192  if (v.first.is_of_type(Glib::VariantType("d"))) {
193  gdouble a, b;
194  g_variant_get(variant.gobj(), "d", &a);
195  g_variant_get((GVariant*)(v.first.gobj()), "d", &b);
196 
197  if (abs(a - b) <= 2 * DBL_EPSILON) {
198  slider_->setValue(i);
199  slider_label_->setText(v.second);
200  }
201  } else {
202  // Check for "(dd)" type and handle it if it's found
203  if (v.first.is_of_type(Glib::VariantType("(dd)"))) {
204  gdouble a1, a2, b1, b2;
205  g_variant_get(variant.gobj(), "(dd)", &a1, &a2);
206  g_variant_get((GVariant*)(v.first.gobj()), "(dd)", &b1, &b2);
207 
208  if ((abs(a1 - b1) <= 2 * DBL_EPSILON) && \
209  (abs(a2 - b2) <= 2 * DBL_EPSILON)) {
210  slider_->setValue(i);
211  slider_label_->setText(v.second);
212  }
213 
214  } else {
215  qWarning() << "Enum property" << name() << "encountered unsupported type";
216  return;
217  }
218  }
219  }
220 
221  } else {
222  // Use combo box
223  if (!selector_)
224  return;
225 
226  for (unsigned int i = 0; i < values_.size(); i++) {
227  const pair<Glib::VariantBase, QString> &v = values_[i];
228 
229  // g_variant_equal() doesn't handle floating point properly
230  if (v.first.is_of_type(Glib::VariantType("d"))) {
231  gdouble a, b;
232  g_variant_get(variant.gobj(), "d", &a);
233  g_variant_get((GVariant*)(v.first.gobj()), "d", &b);
234  if (abs(a - b) <= 2 * DBL_EPSILON)
235  selector_->setCurrentIndex(i);
236  } else {
237  // Check for "(dd)" type and handle it if it's found
238  if (v.first.is_of_type(Glib::VariantType("(dd)"))) {
239  gdouble a1, a2, b1, b2;
240  g_variant_get(variant.gobj(), "(dd)", &a1, &a2);
241  g_variant_get((GVariant*)(v.first.gobj()), "(dd)", &b1, &b2);
242  if ((abs(a1 - b1) <= 2 * DBL_EPSILON) && \
243  (abs(a2 - b2) <= 2 * DBL_EPSILON))
244  selector_->setCurrentIndex(i);
245 
246  } else
247  // Handle all other types
248  if (v.first.equal(variant))
249  selector_->setCurrentIndex(i);
250  }
251  }
252  }
253 }
254 
256 {
257  assert(setter_);
258 
259  if (is_range_) {
260  // Use slider
262  return;
263 
264  setter_(values_.at(slider_->value()).first);
265 
266  update_widget();
267  } else {
268  // Use combo box
269  if (!selector_)
270  return;
271 
272  const int index = selector_->currentIndex();
273  if (index < 0)
274  return;
275 
276  setter_(selector_->itemData(index).value<Glib::VariantBase>());
277 
278  // The combo box needs no update, it already shows the current value
279  // by definition: the user picked it
280  }
281 }
282 
284 {
285  commit();
286 }
287 
289 {
290  commit();
291 }
292 
293 } // namespace prop
294 } // namespace pv
QComboBox * selector_
Definition: enum.hpp:66
void on_current_index_changed(int)
Definition: enum.cpp:283
T value(details::expression_node< T > *n)
Definition: exprtk.hpp:12358
Enum(QString name, QString desc, vector< pair< Glib::VariantBase, QString > > values, Getter getter, Setter setter)
Definition: enum.cpp:46
const QString & name() const
Definition: property.cpp:33
const Getter getter_
Definition: property.hpp:63
void on_value_changed(int)
Definition: enum.cpp:288
QWidget * slider_layout_widget_
Definition: enum.hpp:68
QLabel * slider_label_
Definition: enum.hpp:70
QWidget * get_widget(QWidget *parent, bool auto_commit)
Definition: enum.cpp:103
void commit()
Definition: enum.cpp:255
const Setter setter_
Definition: property.hpp:64
const vector< pair< Glib::VariantBase, QString > > values_
Definition: enum.hpp:63
void update_widget()
Definition: enum.cpp:169
function< Glib::VariantBase()> Getter
Definition: property.hpp:46
QSlider * slider_
Definition: enum.hpp:69
function< void(Glib::VariantBase)> Setter
Definition: property.hpp:47
bool is_range_
Definition: enum.hpp:64