PulseView  unreleased development snapshot
A Qt-based sigrok GUI
logicsignal.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 <extdef.h>
21 
22 #include <cassert>
23 #include <cmath>
24 
25 #include <algorithm>
26 
27 #include <QApplication>
28 #include <QFormLayout>
29 #include <QToolBar>
30 
31 #include "logicsignal.hpp"
32 #include "view.hpp"
33 
34 #include <pv/data/logic.hpp>
35 #include <pv/data/logicsegment.hpp>
36 #include <pv/data/signalbase.hpp>
37 #include <pv/devicemanager.hpp>
38 #include <pv/devices/device.hpp>
39 #include <pv/globalsettings.hpp>
40 #include <pv/session.hpp>
41 
42 #include <libsigrokcxx/libsigrokcxx.hpp>
43 
44 using std::deque;
45 using std::max;
46 using std::make_pair;
47 using std::min;
48 using std::none_of;
49 using std::out_of_range;
50 using std::pair;
51 using std::shared_ptr;
52 using std::vector;
53 
54 using sigrok::ConfigKey;
55 using sigrok::Capability;
56 using sigrok::Trigger;
57 using sigrok::TriggerMatch;
58 using sigrok::TriggerMatchType;
59 
61 
62 namespace pv {
63 namespace views {
64 namespace trace {
65 
66 const float LogicSignal::Oversampling = 2.0f;
67 
68 const QColor LogicSignal::EdgeColor(0x80, 0x80, 0x80);
69 const QColor LogicSignal::HighColor(0x00, 0xC0, 0x00);
70 const QColor LogicSignal::LowColor(0xC0, 0x00, 0x00);
71 const QColor LogicSignal::SamplingPointColor(0x77, 0x77, 0x77);
72 
73 QColor LogicSignal::TriggerMarkerBackgroundColor = QColor(0xED, 0xD4, 0x00);
75 const char* LogicSignal::TriggerMarkerIcons[8] = {
76  nullptr,
77  ":/icons/trigger-marker-low.svg",
78  ":/icons/trigger-marker-high.svg",
79  ":/icons/trigger-marker-rising.svg",
80  ":/icons/trigger-marker-falling.svg",
81  ":/icons/trigger-marker-change.svg",
82  nullptr,
83  nullptr
84 };
85 
86 QCache<QString, const QIcon> LogicSignal::icon_cache_;
87 QCache<QString, const QPixmap> LogicSignal::pixmap_cache_;
88 
89 LogicSignal::LogicSignal(pv::Session &session, shared_ptr<data::SignalBase> base) :
90  Signal(session, base),
91  trigger_types_(get_trigger_types()),
92  trigger_none_(nullptr),
93  trigger_rising_(nullptr),
94  trigger_high_(nullptr),
95  trigger_falling_(nullptr),
96  trigger_low_(nullptr),
97  trigger_change_(nullptr)
98 {
99  GlobalSettings settings;
102  settings.value(GlobalSettings::Key_View_ShowSamplingPoints).toBool();
104  settings.value(GlobalSettings::Key_View_FillSignalHighAreas).toBool();
105  high_fill_color_ = QColor::fromRgba(settings.value(
107 
109 
110  /* Populate this channel's trigger setting with whatever we
111  * find in the current session trigger, if anything. */
112  trigger_match_ = nullptr;
113  if (shared_ptr<Trigger> trigger = session_.session()->trigger())
114  for (auto stage : trigger->stages())
115  for (auto match : stage->matches())
116  if (match->channel() == base_->channel())
117  trigger_match_ = match->type();
118 }
119 
120 std::map<QString, QVariant> LogicSignal::save_settings() const
121 {
122  std::map<QString, QVariant> result;
123 
124  result["trace_height"] = signal_height_;
125 
126  return result;
127 }
128 
129 void LogicSignal::restore_settings(std::map<QString, QVariant> settings)
130 {
131  auto entry = settings.find("trace_height");
132  if (entry != settings.end()) {
133  const int old_height = signal_height_;
134  signal_height_ = settings["trace_height"].toInt();
135 
136  if ((signal_height_ != old_height) && owner_) {
138 
139  // Call order is important, otherwise the lazy event handler won't work
140  owner_->extents_changed(false, true);
141  owner_->row_item_appearance_changed(false, true);
142  }
143  }
144 }
145 
146 pair<int, int> LogicSignal::v_extents() const
147 {
148  const int signal_margin =
149  QFontMetrics(QApplication::font()).height() / 2;
150  return make_pair(-signal_height_ - signal_margin, signal_margin);
151 }
152 
154 {
155  QLineF *line;
156 
157  vector< pair<int64_t, bool> > edges;
158 
159  assert(base_);
160  assert(owner_);
161 
162  const int y = get_visual_y();
163 
164  if (!base_->enabled())
165  return;
166 
167  const float low_offset = y + low_level_offset_;
168  const float high_offset = y + high_level_offset_;
169  const float fill_height = low_offset - high_offset;
170 
171  shared_ptr<LogicSegment> segment = get_logic_segment_to_paint();
172  if (!segment || (segment->get_sample_count() == 0))
173  return;
174 
175  double samplerate = segment->samplerate();
176 
177  // Show sample rate as 1Hz when it is unknown
178  if (samplerate == 0.0)
179  samplerate = 1.0;
180 
181  const double pixels_offset = pp.pixels_offset();
182  const pv::util::Timestamp& start_time = segment->start_time();
183  const int64_t last_sample = (int64_t)segment->get_sample_count() - 1;
184  const double samples_per_pixel = samplerate * pp.scale();
185  const double pixels_per_sample = 1 / samples_per_pixel;
186  const pv::util::Timestamp start = samplerate * (pp.offset() - start_time);
187  const pv::util::Timestamp end = start + samples_per_pixel * pp.width();
188 
189  const int64_t start_sample = min(max(floor(start).convert_to<int64_t>(),
190  (int64_t)0), last_sample);
191  const uint64_t end_sample = min(max(ceil(end).convert_to<int64_t>(),
192  (int64_t)0), last_sample);
193 
194  segment->get_subsampled_edges(edges, start_sample, end_sample,
195  samples_per_pixel / Oversampling, base_->logic_bit_index());
196  assert(edges.size() >= 2);
197 
198  const float first_sample_x =
199  pp.left() + (edges.front().first / samples_per_pixel - pixels_offset);
200  const float last_sample_x =
201  pp.left() + (edges.back().first / samples_per_pixel - pixels_offset);
202 
203  // Check whether we need to paint the sampling points
204  const bool show_sampling_points = show_sampling_points_ && (samples_per_pixel < 0.25);
205  vector<QRectF> sampling_points;
206  float sampling_point_x = first_sample_x;
207  int64_t sampling_point_sample = start_sample;
208  const int w = 2;
209 
210  if (show_sampling_points)
211  sampling_points.reserve(end_sample - start_sample + 1);
212 
213  vector<QRectF> high_rects;
214  float rising_edge_x;
215  bool rising_edge_seen = false;
216 
217  // Paint the edges
218  const unsigned int edge_count = edges.size() - 2;
219  QLineF *const edge_lines = new QLineF[edge_count];
220  line = edge_lines;
221 
222  if (edges.front().second) {
223  // Beginning of trace is high
224  rising_edge_x = first_sample_x;
225  rising_edge_seen = true;
226  }
227 
228  for (auto i = edges.cbegin() + 1; i != edges.cend() - 1; i++) {
229  // Note: multiple edges occupying a single pixel are represented by an edge
230  // with undefined logic level. This means that only the first falling edge
231  // after a rising edge corresponds to said rising edge - and vice versa. If
232  // more edges with the same logic level follow, they denote multiple edges.
233 
234  const float x = pp.left() + ((*i).first / samples_per_pixel - pixels_offset);
235  *line++ = QLineF(x, high_offset, x, low_offset);
236 
237  if (fill_high_areas_) {
238  // Any edge terminates a high area
239  if (rising_edge_seen) {
240  const int width = x - rising_edge_x;
241  if (width > 0)
242  high_rects.emplace_back(rising_edge_x, high_offset, width, fill_height);
243  rising_edge_seen = false;
244  }
245 
246  // Only rising edges start high areas
247  if ((*i).second) {
248  rising_edge_x = x;
249  rising_edge_seen = true;
250  }
251  }
252 
253  if (show_sampling_points)
254  while (sampling_point_sample < (*i).first) {
255  const float y = (*i).second ? low_offset : high_offset;
256  sampling_points.emplace_back(
257  QRectF(sampling_point_x - (w / 2), y - (w / 2), w, w));
258  sampling_point_sample++;
259  sampling_point_x += pixels_per_sample;
260  };
261  }
262 
263  // Calculate the sample points from the last edge to the end of the trace
264  if (show_sampling_points)
265  while ((uint64_t)sampling_point_sample <= end_sample) {
266  // Signal changed after the last edge, so the level is inverted
267  const float y = (edges.cend() - 1)->second ? high_offset : low_offset;
268  sampling_points.emplace_back(
269  QRectF(sampling_point_x - (w / 2), y - (w / 2), w, w));
270  sampling_point_sample++;
271  sampling_point_x += pixels_per_sample;
272  };
273 
274  if (fill_high_areas_) {
275  // Add last high rectangle if the signal is still high at the end of the trace
276  if (rising_edge_seen && (edges.cend() - 1)->second)
277  high_rects.emplace_back(rising_edge_x, high_offset,
278  last_sample_x - rising_edge_x, signal_height_);
279 
280  p.setPen(high_fill_color_);
281  p.setBrush(high_fill_color_);
282  p.drawRects((const QRectF*)(high_rects.data()), high_rects.size());
283  }
284 
285  p.setPen(EdgeColor);
286  p.drawLines(edge_lines, edge_count);
287  delete[] edge_lines;
288 
289  // Paint the caps
290  const unsigned int max_cap_line_count = edges.size();
291  QLineF *const cap_lines = new QLineF[max_cap_line_count];
292 
293  p.setPen(HighColor);
294  paint_caps(p, cap_lines, edges, true, samples_per_pixel,
295  pixels_offset, pp.left(), high_offset);
296  p.setPen(LowColor);
297  paint_caps(p, cap_lines, edges, false, samples_per_pixel,
298  pixels_offset, pp.left(), low_offset);
299 
300  delete[] cap_lines;
301 
302  // Paint the sampling points
303  if (show_sampling_points) {
304  p.setPen(SamplingPointColor);
305  p.drawRects(sampling_points.data(), sampling_points.size());
306  }
307 }
308 
310 {
311  if (base_->enabled()) {
312  if (trigger_match_) {
313  // Draw the trigger marker
314  const int y = get_visual_y();
315 
316  for (int32_t type_id : trigger_types_) {
317  const TriggerMatchType *const type =
318  TriggerMatchType::get(type_id);
319  if (trigger_match_ != type || type_id < 0 ||
320  (size_t)type_id >= countof(TriggerMarkerIcons) ||
321  !TriggerMarkerIcons[type_id])
322  continue;
323 
324  const QPixmap *const pixmap = get_pixmap(
325  TriggerMarkerIcons[type_id]);
326  if (!pixmap)
327  continue;
328 
329  const float pad = TriggerMarkerPadding - 0.5f;
330  const QSize size = pixmap->size();
331  const QPoint point(
332  pp.right() - size.width() - pad * 2,
333  y - (signal_height_ + size.height()) / 2);
334 
335  p.setPen(QPen(TriggerMarkerBackgroundColor.darker()));
336  p.setBrush(TriggerMarkerBackgroundColor);
337  p.drawRoundedRect(QRectF(point, size).adjusted(
338  -pad, -pad, pad, pad), pad, pad);
339  p.drawPixmap(point, *pixmap);
340 
341  break;
342  }
343  }
344 
345  if (show_hover_marker_)
347  }
348 }
349 
350 vector<LogicSegment::EdgePair> LogicSignal::get_nearest_level_changes(uint64_t sample_pos)
351 {
352  assert(base_);
353  assert(owner_);
354 
355  if (sample_pos == 0)
356  return vector<LogicSegment::EdgePair>();
357 
358  shared_ptr<LogicSegment> segment = get_logic_segment_to_paint();
359  if (!segment || (segment->get_sample_count() == 0))
360  return vector<LogicSegment::EdgePair>();
361 
362  const View *view = owner_->view();
363  assert(view);
364  const double samples_per_pixel = base_->get_samplerate() * view->scale();
365 
366  vector<LogicSegment::EdgePair> edges;
367 
368  segment->get_surrounding_edges(edges, sample_pos,
369  samples_per_pixel / Oversampling, base_->index());
370 
371  if (edges.empty())
372  return vector<LogicSegment::EdgePair>();
373 
374  return edges;
375 }
376 
377 void LogicSignal::paint_caps(QPainter &p, QLineF *const lines,
378  vector< pair<int64_t, bool> > &edges, bool level,
379  double samples_per_pixel, double pixels_offset, float x_offset,
380  float y_offset)
381 {
382  QLineF *line = lines;
383 
384  for (auto i = edges.begin(); i != (edges.end() - 1); i++)
385  if ((*i).second == level) {
386  *line++ = QLineF(
387  ((*i).first / samples_per_pixel -
388  pixels_offset) + x_offset, y_offset,
389  ((*(i+1)).first / samples_per_pixel -
390  pixels_offset) + x_offset, y_offset);
391  }
392 
393  p.drawLines(lines, line - lines);
394 }
395 
396 shared_ptr<pv::data::LogicSegment> LogicSignal::get_logic_segment_to_paint() const
397 {
398  shared_ptr<pv::data::LogicSegment> segment;
399 
400  const deque< shared_ptr<pv::data::LogicSegment> > &segments =
401  base_->logic_data()->logic_segments();
402 
403  if (!segments.empty()) {
405  segment = segments.back();
406 
409  try {
410  segment = segments.at(current_segment_);
411  } catch (out_of_range&) {
412  qDebug() << "Current logic segment out of range for signal" << base_->name() << ":" << current_segment_;
413  }
414  }
415  }
416 
417  return segment;
418 }
419 
421 {
422  trigger_none_ = new QAction(*get_icon(":/icons/trigger-none.svg"),
423  tr("No trigger"), parent);
424  trigger_none_->setCheckable(true);
425  connect(trigger_none_, SIGNAL(triggered()), this, SLOT(on_trigger()));
426 
427  trigger_rising_ = new QAction(*get_icon(":/icons/trigger-rising.svg"),
428  tr("Trigger on rising edge"), parent);
429  trigger_rising_->setCheckable(true);
430  connect(trigger_rising_, SIGNAL(triggered()), this, SLOT(on_trigger()));
431 
432  trigger_high_ = new QAction(*get_icon(":/icons/trigger-high.svg"),
433  tr("Trigger on high level"), parent);
434  trigger_high_->setCheckable(true);
435  connect(trigger_high_, SIGNAL(triggered()), this, SLOT(on_trigger()));
436 
437  trigger_falling_ = new QAction(*get_icon(":/icons/trigger-falling.svg"),
438  tr("Trigger on falling edge"), parent);
439  trigger_falling_->setCheckable(true);
440  connect(trigger_falling_, SIGNAL(triggered()), this, SLOT(on_trigger()));
441 
442  trigger_low_ = new QAction(*get_icon(":/icons/trigger-low.svg"),
443  tr("Trigger on low level"), parent);
444  trigger_low_->setCheckable(true);
445  connect(trigger_low_, SIGNAL(triggered()), this, SLOT(on_trigger()));
446 
447  trigger_change_ = new QAction(*get_icon(":/icons/trigger-change.svg"),
448  tr("Trigger on rising or falling edge"), parent);
449  trigger_change_->setCheckable(true);
450  connect(trigger_change_, SIGNAL(triggered()), this, SLOT(on_trigger()));
451 }
452 
453 const vector<int32_t> LogicSignal::get_trigger_types() const
454 {
455  // We may not be associated with a device
456  if (!session_.device())
457  return vector<int32_t>();
458 
459  const auto sr_dev = session_.device()->device();
460  if (sr_dev->config_check(ConfigKey::TRIGGER_MATCH, Capability::LIST)) {
461  const Glib::VariantContainerBase gvar =
462  sr_dev->config_list(ConfigKey::TRIGGER_MATCH);
463 
464  vector<int32_t> ttypes;
465 
466  for (unsigned int i = 0; i < gvar.get_n_children(); i++) {
467  Glib::VariantBase tmp_vb;
468  gvar.get_child(tmp_vb, i);
469 
470  Glib::Variant<int32_t> tmp_v =
471  Glib::VariantBase::cast_dynamic< Glib::Variant<int32_t> >(tmp_vb);
472 
473  ttypes.push_back(tmp_v.get());
474  }
475 
476  return ttypes;
477  } else {
478  return vector<int32_t>();
479  }
480 }
481 
482 QAction* LogicSignal::action_from_trigger_type(const TriggerMatchType *type)
483 {
484  QAction *action;
485 
486  action = trigger_none_;
487  if (type) {
488  switch (type->id()) {
489  case SR_TRIGGER_ZERO:
490  action = trigger_low_;
491  break;
492  case SR_TRIGGER_ONE:
493  action = trigger_high_;
494  break;
495  case SR_TRIGGER_RISING:
496  action = trigger_rising_;
497  break;
498  case SR_TRIGGER_FALLING:
499  action = trigger_falling_;
500  break;
501  case SR_TRIGGER_EDGE:
502  action = trigger_change_;
503  break;
504  default:
505  assert(false);
506  }
507  }
508 
509  return action;
510 }
511 
512 const TriggerMatchType *LogicSignal::trigger_type_from_action(QAction *action)
513 {
514  if (action == trigger_low_)
515  return TriggerMatchType::ZERO;
516  else if (action == trigger_high_)
517  return TriggerMatchType::ONE;
518  else if (action == trigger_rising_)
519  return TriggerMatchType::RISING;
520  else if (action == trigger_falling_)
521  return TriggerMatchType::FALLING;
522  else if (action == trigger_change_)
523  return TriggerMatchType::EDGE;
524  else
525  return nullptr;
526 }
527 
528 void LogicSignal::populate_popup_form(QWidget *parent, QFormLayout *form)
529 {
530  Signal::populate_popup_form(parent, form);
531 
532  signal_height_sb_ = new QSpinBox(parent);
533  signal_height_sb_->setRange(5, 1000);
534  signal_height_sb_->setSingleStep(5);
535  signal_height_sb_->setSuffix(tr(" pixels"));
537  connect(signal_height_sb_, SIGNAL(valueChanged(int)),
538  this, SLOT(on_signal_height_changed(int)));
539  form->addRow(tr("Trace height"), signal_height_sb_);
540 
541  // Trigger settings
542  const vector<int32_t> trig_types = get_trigger_types();
543 
544  if (!trig_types.empty()) {
545  trigger_bar_ = new QToolBar(parent);
547  trigger_bar_->addAction(trigger_none_);
548  trigger_none_->setChecked(!trigger_match_);
549 
550  for (auto type_id : trig_types) {
551  const TriggerMatchType *const type =
552  TriggerMatchType::get(type_id);
553  QAction *const action = action_from_trigger_type(type);
554  trigger_bar_->addAction(action);
555  action->setChecked(trigger_match_ == type);
556  }
557 
558  // Only allow triggers to be changed when we're stopped
560  for (QAction* action : trigger_bar_->findChildren<QAction*>()) // clazy:exclude=range-loop
561  action->setEnabled(false);
562 
563  form->addRow(tr("Trigger"), trigger_bar_);
564  }
565 }
566 
568 {
569  auto trigger = session_.session()->trigger();
570  auto new_trigger = session_.device_manager().context()->create_trigger("pulseview");
571 
572  if (trigger) {
573  for (auto stage : trigger->stages()) {
574  const auto &matches = stage->matches();
575  if (none_of(matches.begin(), matches.end(),
576  [&](shared_ptr<TriggerMatch> match) {
577  return match->channel() != base_->channel(); }))
578  continue;
579 
580  auto new_stage = new_trigger->add_stage();
581  for (auto match : stage->matches()) {
582  if (match->channel() == base_->channel())
583  continue;
584  new_stage->add_match(match->channel(), match->type());
585  }
586  }
587  }
588 
589  if (trigger_match_) {
590  // Until we can let the user decide how to group trigger matches
591  // into stages, put all of the matches into a single stage --
592  // most devices only support a single trigger stage.
593  if (new_trigger->stages().empty())
594  new_trigger->add_stage();
595 
596  new_trigger->stages().back()->add_match(base_->channel(),
598  }
599 
600  session_.session()->set_trigger(
601  new_trigger->stages().empty() ? nullptr : new_trigger);
602 
603  if (owner_)
604  owner_->row_item_appearance_changed(false, true);
605 }
606 
607 const QIcon* LogicSignal::get_icon(const char *path)
608 {
609  if (!icon_cache_.contains(path)) {
610  const QIcon *icon = new QIcon(path);
611  icon_cache_.insert(path, icon);
612  }
613 
614  return icon_cache_.take(path);
615 }
616 
617 const QPixmap* LogicSignal::get_pixmap(const char *path)
618 {
619  if (!pixmap_cache_.contains(path)) {
620  const QPixmap *pixmap = new QPixmap(path);
621  pixmap_cache_.insert(path, pixmap);
622  }
623 
624  return pixmap_cache_.take(path);
625 }
626 
628 {
629  low_level_offset_ = 0.5f;
631 }
632 
633 void LogicSignal::on_setting_changed(const QString &key, const QVariant &value)
634 {
635  Signal::on_setting_changed(key, value);
636 
638  show_sampling_points_ = value.toBool();
639 
641  fill_high_areas_ = value.toBool();
642 
644  high_fill_color_ = QColor::fromRgba(value.value<uint32_t>());
645 }
646 
648 {
649  QAction *action;
650 
651  action_from_trigger_type(trigger_match_)->setChecked(false);
652 
653  action = (QAction *)sender();
654  action->setChecked(true);
656 
657  modify_trigger();
658 }
659 
661 {
662  signal_height_ = height;
663 
664  if (owner_) {
666 
667  // Call order is important, otherwise the lazy event handler won't work
668  owner_->extents_changed(false, true);
669  owner_->row_item_appearance_changed(false, true);
670  }
671 }
672 
673 } // namespace trace
674 } // namespace views
675 } // namespace pv
static QColor TriggerMarkerBackgroundColor
Definition: logicsignal.hpp:68
static const QIcon * get_icon(const char *path)
virtual std::map< QString, QVariant > save_settings() const
void on_setting_changed(const QString &key, const QVariant &value)
QAction * action_from_trigger_type(const sigrok::TriggerMatchType *type)
const sigrok::TriggerMatchType * trigger_match_
const vector< int32_t > get_trigger_types() const
virtual void extents_changed(bool horz, bool vert)=0
void init_trigger_actions(QWidget *parent)
const shared_ptr< sigrok::Context > & context() const
static const QString Key_View_FillSignalHighAreaColor
shared_ptr< sigrok::Session > session() const
Definition: session.cpp:156
virtual void paint_fore(QPainter &p, ViewItemPaintParams &pp)
T value(details::expression_node< T > *n)
Definition: exprtk.hpp:12358
virtual void restore_settings(std::map< QString, QVariant > settings)
int current_segment_
The ID of the currently displayed segment.
Definition: trace.hpp:217
static const QString Key_View_DefaultLogicHeight
static const QColor HighColor
Definition: logicsignal.hpp:64
static QCache< QString, const QPixmap > pixmap_cache_
shared_ptr< data::SignalBase > base_
Definition: trace.hpp:208
static const float Oversampling
Definition: logicsignal.hpp:61
T max(const T v0, const T v1)
Definition: exprtk.hpp:1411
virtual void paint_mid(QPainter &p, ViewItemPaintParams &pp)
shared_ptr< devices::Device > device() const
Definition: session.cpp:163
void populate_popup_form(QWidget *parent, QFormLayout *form)
DeviceManager & device_manager()
Definition: session.cpp:146
void paint_caps(QPainter &p, QLineF *const lines, vector< pair< int64_t, bool > > &edges, bool level, double samples_per_pixel, double pixels_offset, float x_offset, float y_offset)
const pv::util::Timestamp & offset() const
const vector< int32_t > trigger_types_
const sigrok::TriggerMatchType * trigger_type_from_action(QAction *action)
shared_ptr< pv::data::LogicSegment > get_logic_segment_to_paint() const
T min(const T v0, const T v1)
Definition: exprtk.hpp:1404
capture_state get_capture_state() const
Definition: session.cpp:788
pv::Session & session_
Definition: signal.hpp:111
virtual void populate_popup_form(QWidget *parent, QFormLayout *form)
Definition: signal.cpp:132
#define countof(x)
Definition: extdef.h:23
static const int TriggerMarkerPadding
Definition: logicsignal.hpp:69
SegmentDisplayMode segment_display_mode_
Definition: trace.hpp:211
LogicSignal(pv::Session &session, shared_ptr< data::SignalBase > base)
Definition: logicsignal.cpp:89
static const QString Key_View_ShowSamplingPoints
static const QString Key_View_FillSignalHighAreas
x y t t *t x y t t t x y t t t x *y t *t t x *y t *t t x y t t t x y t t t x(y+z)
static const QColor SamplingPointColor
Definition: logicsignal.hpp:66
static QCache< QString, const QIcon > icon_cache_
static const QColor EdgeColor
Definition: logicsignal.hpp:63
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 char * TriggerMarkerIcons[8]
Definition: logicsignal.hpp:70
virtual void update_logic_level_offsets()
static const QPixmap * get_pixmap(const char *path)
static const QColor LowColor
Definition: logicsignal.hpp:65
virtual vector< data::LogicSegment::EdgePair > get_nearest_level_changes(uint64_t sample_pos)
virtual pair< int, int > v_extents() const
virtual void row_item_appearance_changed(bool label, bool content)=0
virtual void on_setting_changed(const QString &key, const QVariant &value)
Definition: trace.cpp:114
void on_signal_height_changed(int height)
void paint_hover_marker(QPainter &p)
Definition: trace.cpp:341