PulseView  unreleased development snapshot
A Qt-based sigrok GUI
mainbar.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of the PulseView project.
3  *
4  * Copyright (C) 2012-2015 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 <extdef.h>
21 
22 #include <algorithm>
23 #include <cassert>
24 
25 #include <QAction>
26 #include <QDebug>
27 #include <QFileDialog>
28 #include <QHelpEvent>
29 #include <QMenu>
30 #include <QMessageBox>
31 #include <QSettings>
32 #include <QToolTip>
33 
34 #include "mainbar.hpp"
35 
36 #include <boost/algorithm/string/join.hpp>
37 
38 #include <pv/data/mathsignal.hpp>
39 #include <pv/devicemanager.hpp>
41 #include <pv/devices/inputfile.hpp>
43 #include <pv/dialogs/connect.hpp>
46 #include <pv/mainwindow.hpp>
47 #include <pv/popups/channels.hpp>
49 #include <pv/util.hpp>
50 #include <pv/views/trace/view.hpp>
53 #ifdef ENABLE_DECODE
54 #include <pv/data/decodesignal.hpp>
55 #endif
56 
57 #include <libsigrokcxx/libsigrokcxx.hpp>
58 
59 using std::back_inserter;
60 using std::copy;
61 using std::list;
62 using std::make_pair;
63 using std::make_shared;
64 using std::map;
65 using std::max;
66 using std::min;
67 using std::pair;
68 using std::set;
69 using std::shared_ptr;
70 using std::string;
71 using std::vector;
72 
73 using sigrok::Capability;
74 using sigrok::ConfigKey;
75 using sigrok::Error;
76 using sigrok::InputFormat;
77 using sigrok::OutputFormat;
78 
79 using boost::algorithm::join;
80 
81 namespace pv {
82 namespace toolbars {
83 
84 const uint64_t MainBar::MinSampleCount = 100ULL;
85 const uint64_t MainBar::MaxSampleCount = 1000000000000ULL;
86 const uint64_t MainBar::DefaultSampleCount = 1000000;
87 
88 const char *MainBar::SettingOpenDirectory = "MainWindow/OpenDirectory";
89 const char *MainBar::SettingSaveDirectory = "MainWindow/SaveDirectory";
90 
91 MainBar::MainBar(Session &session, QWidget *parent, pv::views::trace::View *view) :
92  StandardBar(session, parent, view, false),
93  action_new_view_(new QAction(this)),
94  action_open_(new QAction(this)),
95  action_save_(new QAction(this)),
96  action_save_as_(new QAction(this)),
97  action_save_selection_as_(new QAction(this)),
98  action_restore_setup_(new QAction(this)),
99  action_save_setup_(new QAction(this)),
100  action_connect_(new QAction(this)),
101  new_view_button_(new QToolButton()),
102  open_button_(new QToolButton()),
103  save_button_(new QToolButton()),
104  device_selector_(parent, session.device_manager(), action_connect_),
105  configure_button_(this),
106  configure_button_action_(nullptr),
107  channels_button_(this),
108  channels_button_action_(nullptr),
109  sample_count_(" samples", this),
110  sample_rate_("Hz", this),
111  updating_sample_rate_(false),
112  updating_sample_count_(false),
113  sample_count_supported_(false),
114 #ifdef ENABLE_DECODE
115  add_decoder_button_(new QToolButton()),
116 #endif
117  add_math_signal_button_(new QToolButton())
118 {
119  setObjectName(QString::fromUtf8("MainBar"));
120 
121  setContextMenuPolicy(Qt::PreventContextMenu);
122 
123  // Actions
124  action_new_view_->setText(tr("New &View"));
125  action_new_view_->setIcon(QIcon::fromTheme("window-new",
126  QIcon(":/icons/window-new.png")));
127  connect(action_new_view_, SIGNAL(triggered(bool)),
128  this, SLOT(on_actionNewView_triggered()));
129 
130  action_open_->setText(tr("&Open..."));
131  action_open_->setIcon(QIcon::fromTheme("document-open",
132  QIcon(":/icons/document-open.png")));
133 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
134  action_open_->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_O));
135 #else
136  action_open_->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_O));
137 #endif
138  connect(action_open_, SIGNAL(triggered(bool)),
139  this, SLOT(on_actionOpen_triggered()));
140 
141  action_restore_setup_->setText(tr("Restore Session Setu&p..."));
142  connect(action_restore_setup_, SIGNAL(triggered(bool)),
143  this, SLOT(on_actionRestoreSetup_triggered()));
144 
145  action_save_->setText(tr("&Save..."));
146  action_save_->setIcon(QIcon::fromTheme("document-save-as",
147  QIcon(":/icons/document-save-as.png")));
148 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
149  action_save_->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_S));
150 #else
151  action_save_->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_S));
152 #endif
153  connect(action_save_, SIGNAL(triggered(bool)),
154  this, SLOT(on_actionSave_triggered()));
155 
156  action_save_as_->setText(tr("Save &As..."));
157  action_save_as_->setIcon(QIcon::fromTheme("document-save-as",
158  QIcon(":/icons/document-save-as.png")));
159  connect(action_save_as_, SIGNAL(triggered(bool)),
160  this, SLOT(on_actionSaveAs_triggered()));
161 
162  action_save_selection_as_->setText(tr("Save Selected &Range As..."));
163  action_save_selection_as_->setIcon(QIcon::fromTheme("document-save-as",
164  QIcon(":/icons/document-save-as.png")));
165 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
166  action_save_selection_as_->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_R));
167 #else
168  action_save_selection_as_->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_R));
169 #endif
170  connect(action_save_selection_as_, SIGNAL(triggered(bool)),
171  this, SLOT(on_actionSaveSelectionAs_triggered()));
172 
173  action_save_setup_->setText(tr("Save Session Setu&p..."));
174  connect(action_save_setup_, SIGNAL(triggered(bool)),
175  this, SLOT(on_actionSaveSetup_triggered()));
176 
177  widgets::ExportMenu *menu_file_export = new widgets::ExportMenu(this,
178  session.device_manager().context());
179  menu_file_export->setTitle(tr("&Export"));
180  connect(menu_file_export, SIGNAL(format_selected(shared_ptr<sigrok::OutputFormat>)),
181  this, SLOT(export_file(shared_ptr<sigrok::OutputFormat>)));
182 
183  widgets::ImportMenu *menu_file_import = new widgets::ImportMenu(this,
184  session.device_manager().context());
185  menu_file_import->setTitle(tr("&Import"));
186  connect(menu_file_import, SIGNAL(format_selected(shared_ptr<sigrok::InputFormat>)),
187  this, SLOT(import_file(shared_ptr<sigrok::InputFormat>)));
188 
189  action_connect_->setText(tr("&Connect to Device..."));
190  connect(action_connect_, SIGNAL(triggered(bool)),
191  this, SLOT(on_actionConnect_triggered()));
192 
193  // New view button
194  QMenu *menu_new_view = new QMenu();
195  connect(menu_new_view, SIGNAL(triggered(QAction*)),
196  this, SLOT(on_actionNewView_triggered(QAction*)));
197 
198  for (int i = 0; i < views::ViewTypeCount; i++) {
199  QAction *const action = menu_new_view->addAction(tr(views::ViewTypeNames[i]));
200  action->setData(QVariant::fromValue(i));
201  }
202 
203  new_view_button_->setMenu(menu_new_view);
204  new_view_button_->setDefaultAction(action_new_view_);
205  new_view_button_->setPopupMode(QToolButton::MenuButtonPopup);
206 
207  // Open button
208  vector<QAction*> open_actions;
209  open_actions.push_back(action_open_);
210  QAction* separator_o = new QAction(this);
211  separator_o->setSeparator(true);
212  open_actions.push_back(separator_o);
213  open_actions.push_back(action_restore_setup_);
214 
215  widgets::ImportMenu *import_menu = new widgets::ImportMenu(this,
216  session.device_manager().context(), open_actions);
217  connect(import_menu, SIGNAL(format_selected(shared_ptr<sigrok::InputFormat>)),
218  this, SLOT(import_file(shared_ptr<sigrok::InputFormat>)));
219 
220  open_button_->setMenu(import_menu);
221  open_button_->setDefaultAction(action_open_);
222  open_button_->setPopupMode(QToolButton::MenuButtonPopup);
223 
224  // Save button
225  vector<QAction*> save_actions;
226  save_actions.push_back(action_save_);
227  save_actions.push_back(action_save_as_);
228  save_actions.push_back(action_save_selection_as_);
229  QAction* separator_s = new QAction(this);
230  separator_s->setSeparator(true);
231  save_actions.push_back(separator_s);
232  save_actions.push_back(action_save_setup_);
233 
234  widgets::ExportMenu *export_menu = new widgets::ExportMenu(this,
235  session.device_manager().context(), save_actions);
236  connect(export_menu, SIGNAL(format_selected(shared_ptr<sigrok::OutputFormat>)),
237  this, SLOT(export_file(shared_ptr<sigrok::OutputFormat>)));
238 
239  save_button_->setMenu(export_menu);
240  save_button_->setDefaultAction(action_save_);
241  save_button_->setPopupMode(QToolButton::MenuButtonPopup);
242 
243  // Device selector menu
244  connect(&device_selector_, SIGNAL(device_selected()),
245  this, SLOT(on_device_selected()));
246 
247  // Setup the decoder button
248 #ifdef ENABLE_DECODE
249  add_decoder_button_->setIcon(QIcon(":/icons/add-decoder.svg"));
250  add_decoder_button_->setPopupMode(QToolButton::InstantPopup);
251  add_decoder_button_->setToolTip(tr("Add protocol decoder"));
252  add_decoder_button_->setShortcut(QKeySequence(Qt::Key_D));
253 
254  connect(add_decoder_button_, SIGNAL(clicked()),
255  this, SLOT(on_add_decoder_clicked()));
256 #endif
257 
258  // Setup the math signal button
259  add_math_signal_button_->setIcon(QIcon(":/icons/add-math-signal.svg"));
260  add_math_signal_button_->setPopupMode(QToolButton::InstantPopup);
261  add_math_signal_button_->setToolTip(tr("Add math signal"));
262  add_math_signal_button_->setShortcut(QKeySequence(Qt::Key_M));
263 
264  connect(add_math_signal_button_, SIGNAL(clicked()),
265  this, SLOT(on_add_math_signal_clicked()));
266 
267 
268  connect(&sample_count_, SIGNAL(value_changed()),
269  this, SLOT(on_sample_count_changed()));
270  connect(&sample_rate_, SIGNAL(value_changed()),
271  this, SLOT(on_sample_rate_changed()));
272 
273  sample_count_.show_min_max_step(0, UINT64_MAX, 1);
274 
276 
277  configure_button_.setToolTip(tr("Configure Device"));
278  configure_button_.setIcon(QIcon::fromTheme("preferences-system",
279  QIcon(":/icons/preferences-system.png")));
280 
281  channels_button_.setToolTip(tr("Configure Channels"));
282  channels_button_.setIcon(QIcon(":/icons/channels.svg"));
283 
285 
286  sample_count_.installEventFilter(this);
287  sample_rate_.installEventFilter(this);
288 
289  // Setup session_ events
290  connect(&session_, SIGNAL(capture_state_changed(int)),
291  this, SLOT(on_capture_state_changed(int)));
292  connect(&session, SIGNAL(device_changed()),
293  this, SLOT(on_device_changed()));
294 
296 }
297 
299 {
301  shared_ptr<devices::Device> selected_device = session_.device();
302  list< shared_ptr<devices::Device> > devs;
303 
304  copy(mgr.devices().begin(), mgr.devices().end(), back_inserter(devs));
305 
306  if (std::find(devs.begin(), devs.end(), selected_device) == devs.end())
307  devs.push_back(selected_device);
308 
309  device_selector_.set_device_list(devs, selected_device);
311 }
312 
314 {
315  bool ui_enabled = (state == pv::Session::Stopped) ? true : false;
316 
317  device_selector_.setEnabled(ui_enabled);
318  configure_button_.setEnabled(ui_enabled);
319  channels_button_.setEnabled(ui_enabled);
320  sample_count_.setEnabled(ui_enabled);
321  sample_rate_.setEnabled(ui_enabled);
322 }
323 
325 {
327 }
328 
329 QAction* MainBar::action_open() const
330 {
331  return action_open_;
332 }
333 
334 QAction* MainBar::action_save() const
335 {
336  return action_save_;
337 }
338 
339 QAction* MainBar::action_save_as() const
340 {
341  return action_save_as_;
342 }
343 
345 {
347 }
348 
349 QAction* MainBar::action_connect() const
350 {
351  return action_connect_;
352 }
353 
355 {
356  Glib::VariantContainerBase gvar_dict;
357  GVariant *gvar_list;
358  const uint64_t *elements = nullptr;
359  gsize num_elements;
360  map< const ConfigKey*, set<Capability> > keys;
361 
362  if (updating_sample_rate_) {
364  return;
365  }
366 
367  const shared_ptr<devices::Device> device = device_selector_.selected_device();
368  if (!device)
369  return;
370 
371  assert(!updating_sample_rate_);
372  updating_sample_rate_ = true;
373 
374  const shared_ptr<sigrok::Device> sr_dev = device->device();
375 
377  if (sr_dev->config_check(ConfigKey::EXTERNAL_CLOCK, Capability::GET)) {
378  try {
379  auto gvar = sr_dev->config_get(ConfigKey::EXTERNAL_CLOCK);
380  if (gvar.gobj()) {
381  bool value = Glib::VariantBase::cast_dynamic<Glib::Variant<bool>>(
382  gvar).get();
384  }
385  } catch (Error& error) {
386  // Do nothing
387  }
388  }
389 
390 
391  if (sr_dev->config_check(ConfigKey::SAMPLERATE, Capability::LIST)) {
392  try {
393  gvar_dict = sr_dev->config_list(ConfigKey::SAMPLERATE);
394  } catch (Error& error) {
395  qDebug() << tr("Failed to get sample rate list:") << error.what();
396  }
397  } else {
399  updating_sample_rate_ = false;
400  return;
401  }
402 
403  if ((gvar_list = g_variant_lookup_value(gvar_dict.gobj(),
404  "samplerate-steps", G_VARIANT_TYPE("at")))) {
405  elements = (const uint64_t *)g_variant_get_fixed_array(
406  gvar_list, &num_elements, sizeof(uint64_t));
407 
408  const uint64_t min = elements[0];
409  const uint64_t max = elements[1];
410  const uint64_t step = elements[2];
411 
412  g_variant_unref(gvar_list);
413 
414  assert(min > 0);
415  assert(max > 0);
416  assert(max > min);
417  assert(step > 0);
418 
419  if (step == 1)
420  sample_rate_.show_125_list(min, max);
421  else {
422  // When the step is not 1, we cam't make a 1-2-5-10
423  // list of sample rates, because we may not be able to
424  // make round numbers. Therefore in this case, show a
425  // spin box.
426  sample_rate_.show_min_max_step(min, max, step);
427  }
428  } else if ((gvar_list = g_variant_lookup_value(gvar_dict.gobj(),
429  "samplerates", G_VARIANT_TYPE("at")))) {
430  elements = (const uint64_t *)g_variant_get_fixed_array(
431  gvar_list, &num_elements, sizeof(uint64_t));
432  sample_rate_.show_list(elements, num_elements);
433  g_variant_unref(gvar_list);
434  }
435  updating_sample_rate_ = false;
436 
438 }
439 
441 {
443  return;
444 
445  const shared_ptr<devices::Device> device = device_selector_.selected_device();
446  if (!device)
447  return;
448 
449  try {
450  auto gvar = device->device()->config_get(ConfigKey::SAMPLERATE);
451  uint64_t samplerate =
452  Glib::VariantBase::cast_dynamic<Glib::Variant<guint64>>(gvar).get();
453  assert(!updating_sample_rate_);
454  updating_sample_rate_ = true;
455  sample_rate_.set_value(samplerate);
456  updating_sample_rate_ = false;
457  } catch (Error& error) {
458  qDebug() << tr("Failed to get sample rate:") << error.what();
459  }
460 }
461 
463 {
465  return;
466 
467  const shared_ptr<devices::Device> device = device_selector_.selected_device();
468  if (!device)
469  return;
470 
471  const shared_ptr<sigrok::Device> sr_dev = device->device();
472 
473  assert(!updating_sample_count_);
474  updating_sample_count_ = true;
475 
478  updating_sample_count_ = false;
479  return;
480  }
481 
482  uint64_t sample_count = sample_count_.value();
483  uint64_t min_sample_count = 0;
484  uint64_t max_sample_count = MaxSampleCount;
485  bool default_count_set = false;
486 
487  if (sample_count == 0) {
488  sample_count = DefaultSampleCount;
489  default_count_set = true;
490  }
491 
492  if (sr_dev->config_check(ConfigKey::LIMIT_SAMPLES, Capability::LIST)) {
493  try {
494  auto gvar = sr_dev->config_list(ConfigKey::LIMIT_SAMPLES);
495  if (gvar.gobj())
496  g_variant_get(gvar.gobj(), "(tt)",
497  &min_sample_count, &max_sample_count);
498  } catch (Error& error) {
499  qDebug() << tr("Failed to get sample limit list:") << error.what();
500  }
501  }
502 
503  min_sample_count = min(max(min_sample_count, MinSampleCount),
504  max_sample_count);
505 
506  sample_count_.show_125_list(min_sample_count, max_sample_count);
507 
508  if (sr_dev->config_check(ConfigKey::LIMIT_SAMPLES, Capability::GET)) {
509  auto gvar = sr_dev->config_get(ConfigKey::LIMIT_SAMPLES);
510  sample_count = g_variant_get_uint64(gvar.gobj());
511  if (sample_count == 0) {
512  sample_count = DefaultSampleCount;
513  default_count_set = true;
514  }
515  sample_count = min(max(sample_count, MinSampleCount),
516  max_sample_count);
517  }
518 
519  sample_count_.set_value(sample_count);
520 
521  updating_sample_count_ = false;
522 
523  // If we show the default rate then make sure the device uses the same
524  if (default_count_set)
526 }
527 
529 {
530  using namespace pv::popups;
531 
532  const shared_ptr<devices::Device> device = device_selector_.selected_device();
533 
534  // Hide the widgets if no device is selected
535  channels_button_action_->setVisible(!!device);
536  if (!device) {
537  configure_button_action_->setVisible(false);
540  return;
541  }
542 
543  const shared_ptr<sigrok::Device> sr_dev = device->device();
544  if (!sr_dev)
545  return;
546 
547  // Update the configure popup
548  DeviceOptions *const opts = new DeviceOptions(sr_dev, this);
549  configure_button_action_->setVisible(!opts->binding().properties().empty());
551 
552  // Update the channels popup
553  Channels *const channels = new Channels(session_, this);
554  channels_button_.set_popup(channels);
555 
556  // Update supported options.
557  sample_count_supported_ = false;
558 
559  if (sr_dev->config_check(ConfigKey::LIMIT_SAMPLES, Capability::SET))
561 
562  // Add notification of reconfigure events
563  // Note: No need to disconnect the previous signal as that QObject instance is destroyed
564  connect(&opts->binding(), SIGNAL(config_changed()),
565  this, SLOT(on_config_changed()));
566 
567  // Update sweep timing widgets.
570 }
571 
573 {
574  uint64_t sample_rate = 0;
575 
576  const shared_ptr<devices::Device> device = device_selector_.selected_device();
577  if (!device)
578  return;
579 
580  const shared_ptr<sigrok::Device> sr_dev = device->device();
581 
582  sample_rate = sample_rate_.value();
583 
584  try {
585  sr_dev->config_set(ConfigKey::SAMPLERATE,
586  Glib::Variant<guint64>::create(sample_rate));
588  } catch (Error& error) {
589  qDebug() << tr("Failed to configure samplerate:") << error.what();
590  return;
591  }
592 
593  // Devices with built-in memory might impose limits on certain
594  // configurations, so let's check what sample count the driver
595  // lets us use now.
597 }
598 
600 {
601  uint64_t sample_count = 0;
602 
603  const shared_ptr<devices::Device> device = device_selector_.selected_device();
604  if (!device)
605  return;
606 
607  const shared_ptr<sigrok::Device> sr_dev = device->device();
608 
609  sample_count = sample_count_.value();
611  try {
612  sr_dev->config_set(ConfigKey::LIMIT_SAMPLES,
613  Glib::Variant<guint64>::create(sample_count));
615  } catch (Error& error) {
616  qDebug() << tr("Failed to configure sample count:") << error.what();
617  return;
618  }
619  }
620 
621  // Devices with built-in memory might impose limits on certain
622  // configurations, so let's check what sample rate the driver
623  // lets us use now.
625 }
626 
627 void MainBar::show_session_error(const QString text, const QString info_text)
628 {
629  QMessageBox msg(this);
630  msg.setText(text + "\n\n" + info_text);
631  msg.setStandardButtons(QMessageBox::Ok);
632  msg.setIcon(QMessageBox::Warning);
633  msg.exec();
634 }
635 
636 void MainBar::export_file(shared_ptr<OutputFormat> format, bool selection_only, QString file_name)
637 {
639 
640  // Stop any currently running capture session
642 
643  QSettings settings;
644  const QString dir = settings.value(SettingSaveDirectory).toString();
645 
646  pair<uint64_t, uint64_t> sample_range;
647 
648  // Selection only? Verify that the cursors are active and fetch their values
649  if (selection_only) {
650  views::trace::View *trace_view =
651  qobject_cast<views::trace::View*>(session_.main_view().get());
652 
653  if (!trace_view->cursors()->enabled()) {
654  show_session_error(tr("Missing Cursors"), tr("You need to set the " \
655  "cursors before you can save the data enclosed by them " \
656  "to a session file (e.g. using the Show Cursors button)."));
657  return;
658  }
659 
660  const double samplerate = session_.get_samplerate();
661 
662  const pv::util::Timestamp& start_time = trace_view->cursors()->first()->time();
663  const pv::util::Timestamp& end_time = trace_view->cursors()->second()->time();
664 
665  const uint64_t start_sample = (uint64_t)max(
666  0.0, start_time.convert_to<double>() * samplerate);
667  const uint64_t end_sample = (uint64_t)max(
668  0.0, end_time.convert_to<double>() * samplerate);
669 
670  if ((start_sample == 0) && (end_sample == 0)) {
671  // Both cursors are negative and were clamped to 0
672  show_session_error(tr("Invalid Range"), tr("The cursors don't " \
673  "define a valid range of samples."));
674  return;
675  }
676 
677  sample_range = make_pair(start_sample, end_sample);
678  } else {
679  sample_range = make_pair(0, 0);
680  }
681 
682  // Construct the filter
683  const vector<string> exts = format->extensions();
684  QString filter = tr("%1 files ").arg(
685  QString::fromStdString(format->description()));
686 
687  if (exts.empty())
688  filter += "(*)";
689  else
690  filter += QString("(*.%1);;%2 (*)").arg(
691  QString::fromStdString(join(exts, ", *.")),
692  tr("All Files"));
693 
694  // Show the file dialog
695  if (file_name.isEmpty())
696  file_name = QFileDialog::getSaveFileName(this, tr("Save File"), dir, filter);
697 
698  if (file_name.isEmpty())
699  return;
700 
701  const QString abs_path = QFileInfo(file_name).absolutePath();
702  settings.setValue(SettingSaveDirectory, abs_path);
703 
704  // Show the options dialog
705  map<string, Glib::VariantBase> options;
706  if (!format->options().empty()) {
708  tr("Export %1").arg(QString::fromStdString(
709  format->description())),
710  format->options(), this);
711  if (!dlg.exec())
712  return;
713  options = dlg.options();
714  }
715 
716  if (!selection_only) {
717  if (format == session_.device_manager().context()->output_formats()["srzip"]) {
718  session_.set_save_path(QFileInfo(file_name).absolutePath());
719  session_.set_name(QFileInfo(file_name).fileName());
720  } else
722  }
723 
724  StoreProgress *dlg = new StoreProgress(file_name, format, options,
725  sample_range, session_, this);
726  dlg->run();
727 }
728 
729 void MainBar::import_file(shared_ptr<InputFormat> format)
730 {
731  assert(format);
732 
733  QSettings settings;
734  const QString dir = settings.value(SettingOpenDirectory).toString();
735 
736  // Construct the filter
737  const vector<string> exts = format->extensions();
738  const QString filter_exts = exts.empty() ? "" : QString::fromStdString("%1 (%2)").arg(
739  tr("%1 files").arg(QString::fromStdString(format->description())),
740  QString::fromStdString("*.%1").arg(QString::fromStdString(join(exts, " *."))));
741  const QString filter_all = QString::fromStdString("%1 (%2)").arg(
742  tr("All Files"), QString::fromStdString("*"));
743  const QString filter = QString::fromStdString("%1%2%3").arg(
744  exts.empty() ? "" : filter_exts,
745  exts.empty() ? "" : ";;",
746  filter_all);
747 
748  // Show the file dialog
749  const QString file_name = QFileDialog::getOpenFileName(
750  this, tr("Import File"), dir, filter);
751 
752  if (file_name.isEmpty())
753  return;
754 
755  // Show the options dialog
756  map<string, Glib::VariantBase> options;
757  if (!format->options().empty()) {
759  tr("Import %1").arg(QString::fromStdString(
760  format->description())),
761  format->options(), this);
762  if (!dlg.exec())
763  return;
764  options = dlg.options();
765  }
766 
767  session_.load_file(file_name, "", format, options);
768 
769  const QString abs_path = QFileInfo(file_name).absolutePath();
770  settings.setValue(SettingOpenDirectory, abs_path);
771 }
772 
774 {
775  shared_ptr<devices::Device> device = device_selector_.selected_device();
776 
777  if (device)
778  session_.select_device(device);
779  else
781 }
782 
784 {
787 }
788 
790 {
792 }
793 
795 {
798 }
799 
801 {
804 }
805 
807 {
808  // We want to also call update_sample_rate_selector() here in case
809  // the user changed the SR_CONF_EXTERNAL_CLOCK option. However,
810  // commit_sample_rate() does this already, so we don't call it here
811 
814 }
815 
817 {
818  if (action)
819  new_view(&session_, action->data().toInt());
820  else
821  // When the icon of the button is clicked, we create a trace view
823 }
824 
826 {
827  QSettings settings;
828  const QString dir = settings.value(SettingOpenDirectory).toString();
829 
830  // Show the dialog
831  const QString file_name = QFileDialog::getOpenFileName(
832  this, tr("Open File"), dir, tr(
833  "sigrok Sessions (*.sr);;"
834  "All Files (*)"));
835 
836  if (!file_name.isEmpty()) {
837  session_.load_file(file_name);
838 
839  const QString abs_path = QFileInfo(file_name).absolutePath();
840  settings.setValue(SettingOpenDirectory, abs_path);
841  }
842 }
843 
845 {
846  // A path is only set if we loaded/saved an srzip file before
847  if (session_.save_path().isEmpty()) {
849  return;
850  }
851 
852  QFileInfo fi = QFileInfo(QDir(session_.save_path()), session_.name());
853  export_file(session_.device_manager().context()->output_formats()["srzip"], false,
854  fi.absoluteFilePath());
855 }
856 
858 {
859  export_file(session_.device_manager().context()->output_formats()["srzip"]);
860 }
861 
863 {
864  export_file(session_.device_manager().context()->output_formats()["srzip"], true);
865 }
866 
868 {
869  QSettings settings;
870  const QString dir = settings.value(SettingSaveDirectory).toString();
871 
872  const QString file_name = QFileDialog::getSaveFileName(
873  this, tr("Save File"), dir, tr(
874  "PulseView Session Setups (*.pvs);;"
875  "All Files (*)"));
876 
877  if (file_name.isEmpty())
878  return;
879 
880  QSettings settings_storage(file_name, QSettings::IniFormat);
881  session_.save_setup(settings_storage);
882 }
883 
885 {
886  QSettings settings;
887  const QString dir = settings.value(SettingSaveDirectory).toString();
888 
889  const QString file_name = QFileDialog::getOpenFileName(
890  this, tr("Open File"), dir, tr(
891  "PulseView Session Setups (*.pvs);;"
892  "All Files (*)"));
893 
894  if (file_name.isEmpty())
895  return;
896 
897  QSettings settings_storage(file_name, QSettings::IniFormat);
898  session_.restore_setup(settings_storage);
899 }
900 
902 {
903  // Stop any currently running capture session
905 
907 
908  // If the user selected a device, select it in the device list. Select the
909  // current device otherwise.
910  if (dlg.exec())
911  session_.select_device(dlg.get_selected_device());
912 
914 }
915 
917 {
919 }
920 
922 {
923  shared_ptr<data::SignalBase> signal = make_shared<data::MathSignal>(session_);
925 }
926 
928 {
929  addWidget(new_view_button_);
930  addSeparator();
931  addWidget(open_button_);
932  addWidget(save_button_);
933  addSeparator();
934 
935  StandardBar::add_toolbar_widgets();
936 
937  addWidget(&device_selector_);
940  addWidget(&sample_count_);
941  addWidget(&sample_rate_);
942 #ifdef ENABLE_DECODE
943  addSeparator();
944  addWidget(add_decoder_button_);
945 #endif
946  addWidget(add_math_signal_button_);
947 }
948 
949 bool MainBar::eventFilter(QObject *watched, QEvent *event)
950 {
951  if (sample_count_supported_ && (watched == &sample_count_ ||
952  watched == &sample_rate_) &&
953  (event->type() == QEvent::ToolTip)) {
954 
956  QHelpEvent *help_event = static_cast<QHelpEvent*>(event);
957 
958  QString str = tr("Total sampling time: %1").arg(
960  QToolTip::showText(help_event->globalPos(), str);
961 
962  return true;
963  }
964 
965  return false;
966 }
967 
968 } // namespace toolbars
969 } // namespace pv
libsigrok allows users to import and export data from files in various formats some of them as generic as others very specific For a list and make sure to check not so common and outright exotic ways to represent data and sigrok tries to suit as many needs as it can To see which formats your version of PulseView just click on the small arrow next to the _Open_ PulseView will ask for the file name to open Once you picked the you may be asked to specify the details of the format
void show_session_error(const QString text, const QString info_text)
Definition: mainbar.cpp:627
static const uint64_t MinSampleCount
Definition: mainbar.hpp:71
void select_device(shared_ptr< devices::Device > device)
Definition: session.cpp:551
void commit_sample_count()
Definition: mainbar.cpp:599
void new_view(Session *session, int type)
pv::widgets::SweepTimingWidget sample_rate_
Definition: mainbar.hpp:180
QAction *const action_open_
Definition: mainbar.hpp:161
shared_ptr< CursorPair > cursors() const
Definition: view.cpp:1007
QAction *const action_restore_setup_
Definition: mainbar.hpp:165
QAction * action_save() const
Definition: mainbar.cpp:334
QToolButton * new_view_button_
Definition: mainbar.hpp:169
const char * ViewTypeNames[ViewTypeCount]
Definition: viewbase.cpp:36
void add_toolbar_widgets()
Definition: mainbar.cpp:927
void restore_setup(QSettings &settings)
Definition: session.cpp:376
QString format_time_si(const Timestamp &v, SIPrefix prefix, unsigned int precision, QString unit, bool sign)
Definition: util.cpp:132
QAction * action_connect() const
Definition: mainbar.cpp:349
QAction * configure_button_action_
Definition: mainbar.hpp:174
void stop_capture()
Definition: session.cpp:838
QAction * action_save_selection_as() const
Definition: mainbar.cpp:344
void on_actionNewView_triggered(QAction *action=nullptr)
Definition: mainbar.cpp:816
const shared_ptr< sigrok::Context > & context() const
QToolButton * save_button_
Definition: mainbar.hpp:169
const vector< shared_ptr< prop::Property > > & properties()
Definition: binding.cpp:39
const list< shared_ptr< devices::HardwareDevice > > & devices() const
void reset_device_selector()
Definition: mainbar.cpp:324
QAction *const action_new_view_
Definition: mainbar.hpp:160
QAction *const action_connect_
Definition: mainbar.hpp:167
T value(details::expression_node< T > *n)
Definition: exprtk.hpp:12358
void on_add_math_signal_clicked()
Definition: mainbar.cpp:921
void show_list(const uint64_t *vals, size_t count)
QAction * action_open() const
Definition: mainbar.cpp:329
void on_actionSaveSelectionAs_triggered()
Definition: mainbar.cpp:862
void import_file(shared_ptr< sigrok::InputFormat > format)
Definition: mainbar.cpp:729
QString save_path() const
Definition: session.cpp:186
pv::widgets::PopupToolButton channels_button_
Definition: mainbar.hpp:176
Mac OS X or Android For some we provide binary for others we provide installers and for others we provide AppImage containers that you can run without the need to install anything Check the the usual way to install PulseView is to install the packages provided by your distro s package manager sometimes only outdated packages are made available to you In that you have two options
Definition: installation.txt:2
QAction * action_save_as() const
Definition: mainbar.cpp:339
void add_generated_signal(shared_ptr< data::SignalBase > signal)
Definition: session.cpp:974
QAction *const action_save_selection_as_
Definition: mainbar.hpp:164
pv::widgets::SweepTimingWidget sample_count_
Definition: mainbar.hpp:179
static const char * SettingSaveDirectory
Definition: mainbar.hpp:85
T max(const T v0, const T v1)
Definition: exprtk.hpp:1411
shared_ptr< devices::Device > device() const
Definition: session.cpp:163
bool eventFilter(QObject *watched, QEvent *event)
Definition: mainbar.cpp:949
double get_samplerate() const
Definition: session.cpp:910
void show_125_list(uint64_t min, uint64_t max)
DeviceManager & device_manager()
Definition: session.cpp:146
void set_capture_state(pv::Session::capture_state state)
Definition: mainbar.cpp:313
void set_save_path(QString path)
Definition: session.cpp:191
void update_sample_rate_selector_value()
Definition: mainbar.cpp:440
void on_actionSaveAs_triggered()
Definition: mainbar.cpp:857
void on_actionSave_triggered()
Definition: mainbar.cpp:844
void update_sample_count_selector()
Definition: mainbar.cpp:462
void on_actionConnect_triggered()
Definition: mainbar.cpp:901
void show_decoder_selector(Session *session)
shared_ptr< devices::Device > selected_device()
CMake MinSizeRel FORCE endif() add_subdirectory(manual) include(CheckCSourceCompiles) include(CheckCXXCompilerFlag) include(CheckCXXSourceCompiles) include(CMakePushCheckState) include(memaccess) find_package(PkgConfig) if(CMAKE_VERSION VERSION_EQUAL"3.8.0"OR CMAKE_VERSION VERSION_GREATER"3.8.0") check_cxx_compiler_flag("-std
QAction * channels_button_action_
Definition: mainbar.hpp:177
T min(const T v0, const T v1)
Definition: exprtk.hpp:1404
void on_capture_state_changed(int state)
Definition: mainbar.cpp:789
shared_ptr< views::ViewBase > main_view() const
Definition: session.cpp:201
void on_actionOpen_triggered()
Definition: mainbar.cpp:825
void set_name(QString name)
Definition: session.cpp:173
void update_device_config_widgets()
Definition: mainbar.cpp:528
void set_device_list(const list< shared_ptr< devices::Device > > &devices, shared_ptr< devices::Device > selected)
void export_file(shared_ptr< sigrok::OutputFormat > format, bool selection_only=false, QString file_name="")
Definition: mainbar.cpp:636
pv::widgets::DeviceToolButton device_selector_
Definition: mainbar.hpp:171
void on_sample_count_changed()
Definition: mainbar.cpp:794
void show_min_max_step(uint64_t min, uint64_t max, uint64_t step)
QToolButton * add_math_signal_button_
Definition: mainbar.hpp:190
void on_sample_rate_changed()
Definition: mainbar.cpp:800
manual txt set(MANUAL_OUT_HTML"${CMAKE_CURRENT_BINARY_DIR}/manual.html") set(MANUAL_OUT_PDF"$
Definition: CMakeLists.txt:36
it will come up with a session that has the demo device selected That you can get to know the program even when you don t have any hardware to use it you see a list of devices PulseView has recognized If the device you want to use is you can just select it here image::device_selector_dropdown png[] If it s not you ll need to scan for it first Since most serial port and Ethernet devices can t be auto this is usually required for those To do either choose the Connect to Device option from the list or click on the button itself You will see the following you ll need to pick a driver that you want to use In order to do this
Definition: acquisition.txt:14
boost::multiprecision::number< boost::multiprecision::cpp_dec_float< 24 >, boost::multiprecision::et_off > Timestamp
Timestamp type providing yoctosecond resolution.
Definition: util.hpp:67
static const uint64_t MaxSampleCount
Definition: mainbar.hpp:72
QAction *const action_save_as_
Definition: mainbar.hpp:163
MainBar(Session &session, QWidget *parent, pv::views::trace::View *view)
Definition: mainbar.cpp:91
pv::widgets::PopupToolButton configure_button_
Definition: mainbar.hpp:173
static const char * SettingOpenDirectory
Definition: mainbar.hpp:79
QToolButton * open_button_
Definition: mainbar.hpp:169
void update_sample_rate_selector()
Definition: mainbar.cpp:354
QAction *const action_save_setup_
Definition: mainbar.hpp:166
QAction *const action_save_
Definition: mainbar.hpp:162
static const uint64_t DefaultSampleCount
Definition: mainbar.hpp:73
pv::binding::Device & binding()
void on_actionSaveSetup_triggered()
Definition: mainbar.cpp:867
void on_actionRestoreSetup_triggered()
Definition: mainbar.cpp:884
QString name() const
Definition: session.cpp:168
void on_add_decoder_clicked()
Definition: mainbar.cpp:916
void load_file(QString file_name, QString setup_file_name=QString(), shared_ptr< sigrok::InputFormat > format=nullptr, const map< string, Glib::VariantBase > &options=map< string, Glib::VariantBase >())
Definition: session.cpp:735
void allow_user_entered_values(bool value)
void save_setup(QSettings &settings) const
Definition: session.cpp:221