PulseView  unreleased development snapshot
A Qt-based sigrok GUI
model.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of the PulseView project.
3  *
4  * Copyright (C) 2020 Soeren Apel <soeren@apelpie.net>
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 <QApplication>
21 #include <QDebug>
22 #include <QString>
23 
25 
26 #include "view.hpp"
27 
28 #include "pv/util.hpp"
29 #include "pv/globalsettings.hpp"
30 
31 using std::make_shared;
32 
36 using pv::util::SIPrefix;
37 
38 namespace pv {
39 namespace views {
40 namespace tabular_decoder {
41 
43  QAbstractTableModel(parent),
44  all_annotations_(nullptr),
45  dataset_(nullptr),
46  signal_(nullptr),
47  first_hidden_column_(0),
48  prev_segment_(0),
49  prev_last_row_(0),
50  had_highlight_before_(false),
51  hide_hidden_(false)
52 {
53  // Note: when adding entries, consider ViewVisibleFilterProxyModel::filterAcceptsRow()
54 
55  uint8_t i = 0;
56  header_data_.emplace_back(tr("Sample")); i++; // Column #0
57  header_data_.emplace_back(tr("Time")); i++; // Column #1
58  header_data_.emplace_back(tr("Decoder")); i++; // Column #2
59  header_data_.emplace_back(tr("Ann Row")); i++; // Column #3
60  header_data_.emplace_back(tr("Ann Class")); i++; // Column #4
61  header_data_.emplace_back(tr("Value")); i++; // Column #5
62 
64  header_data_.emplace_back("End Sample"); // Column #6, hidden
65 }
66 
68 {
69  int level = 0;
70 
71  const unsigned int ann_stack_level = ann->row_data()->row()->decoder()->get_stack_level();
72  level = (signal_->decoder_stack().size() - 1 - ann_stack_level);
73 
74  return level;
75 }
76 
78 {
79  switch (index) {
80  case 0: return QVariant((qulonglong)ann->start_sample()); // Column #0, Start Sample
81  case 1: { // Column #1, Start Time
83  QString unit = signal_->get_samplerate() ? tr("s") : tr("sa");
84  QString s;
85  if ((t < 60) || (signal_->get_samplerate() == 0)) // i.e. if unit is sa
86  s = format_time_si(t, SIPrefix::unspecified, 3, unit, false);
87  else
88  s = format_time_minutes(t, 3, false);
89  return QVariant(s);
90  }
91  case 2: return QVariant(ann->row()->decoder()->name()); // Column #2, Decoder
92  case 3: return QVariant(ann->row()->description()); // Column #3, Ann Row
93  case 4: return QVariant(ann->ann_class_description()); // Column #4, Ann Class
94  case 5: return QVariant(ann->longest_annotation()); // Column #5, Value
95  case 6: return QVariant((qulonglong)ann->end_sample()); // Column #6, End Sample
96  default: return QVariant();
97  }
98 }
99 
100 QVariant AnnotationCollectionModel::data(const QModelIndex& index, int role) const
101 {
102  if (!signal_ || !index.isValid() || !index.internalPointer())
103  return QVariant();
104 
105  const Annotation* ann =
106  static_cast<const Annotation*>(index.internalPointer());
107 
108  if ((role == Qt::DisplayRole) || (role == Qt::ToolTipRole))
109  return data_from_ann(ann, index.column());
110 
111  if (role == Qt::ForegroundRole) {
112  if (index.column() >= get_hierarchy_level(ann)) {
113  // Invert the text color if this cell is highlighted
114  const bool must_highlight = (highlight_sample_num_ > 0) &&
115  ((int64_t)ann->start_sample() <= highlight_sample_num_) &&
116  ((int64_t)ann->end_sample() >= highlight_sample_num_);
117 
118  if (must_highlight) {
120  return QApplication::palette().brush(QPalette::Window);
121  else
122  return QApplication::palette().brush(QPalette::WindowText);
123  }
124  }
125 
126  return QApplication::palette().brush(QPalette::WindowText);
127  }
128 
129  if (role == Qt::BackgroundRole) {
130  // Only use custom cell background color if column index reached the hierarchy level
131  if (index.column() >= get_hierarchy_level(ann)) {
132 
133  QColor color;
134  const bool must_highlight = (highlight_sample_num_ > 0) &&
135  ((int64_t)ann->start_sample() <= highlight_sample_num_) &&
136  ((int64_t)ann->end_sample() >= highlight_sample_num_);
137 
138  if (must_highlight)
139  color = ann->color();
140  else
142  ann->dark_color() : ann->bright_color();
143 
144  return QBrush(color);
145  }
146  }
147 
148  return QVariant();
149 }
150 
151 Qt::ItemFlags AnnotationCollectionModel::flags(const QModelIndex& index) const
152 {
153  if (!index.isValid())
154  return Qt::NoItemFlags;
155 
156  return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemNeverHasChildren;
157 }
158 
160 {
161  return first_hidden_column_;
162 }
163 
164 QVariant AnnotationCollectionModel::headerData(int section, Qt::Orientation orientation,
165  int role) const
166 {
167  if ((section < 0) || (section >= (int)header_data_.size()))
168  return QVariant();
169 
170  if ((orientation == Qt::Horizontal) && (role == Qt::DisplayRole))
171  return header_data_[section];
172 
173  return QVariant();
174 }
175 
176 QModelIndex AnnotationCollectionModel::index(int row, int column,
177  const QModelIndex& parent_idx) const
178 {
179  (void)parent_idx;
180  assert(column >= 0);
181 
182  if (!dataset_ || (row < 0))
183  return QModelIndex();
184 
185  QModelIndex idx;
186 
187  if ((size_t)row < dataset_->size())
188  idx = createIndex(row, column, (void*)dataset_->at(row));
189 
190  return idx;
191 }
192 
193 QModelIndex AnnotationCollectionModel::parent(const QModelIndex& index) const
194 {
195  (void)index;
196 
197  return QModelIndex();
198 }
199 
200 int AnnotationCollectionModel::rowCount(const QModelIndex& parent_idx) const
201 {
202  (void)parent_idx;
203 
204  if (!dataset_)
205  return 0;
206 
207  return dataset_->size();
208 }
209 
210 int AnnotationCollectionModel::columnCount(const QModelIndex& parent_idx) const
211 {
212  (void)parent_idx;
213 
214  return header_data_.size();
215 }
216 
218 {
219  layoutAboutToBeChanged();
220 
221  if (!signal) {
222  all_annotations_ = nullptr;
223  dataset_ = nullptr;
224  signal_ = nullptr;
225 
226  dataChanged(QModelIndex(), QModelIndex());
227  layoutChanged();
228  return;
229  }
230 
231  if (signal_)
232  for (const shared_ptr<Decoder>& dec : signal_->decoder_stack())
233  disconnect(dec.get(), nullptr, this, SLOT(on_annotation_visibility_changed()));
234 
235  all_annotations_ = signal->get_all_annotations_by_segment(current_segment);
236  signal_ = signal;
237 
238  for (const shared_ptr<Decoder>& dec : signal_->decoder_stack())
239  connect(dec.get(), SIGNAL(annotation_visibility_changed()),
240  this, SLOT(on_annotation_visibility_changed()));
241 
242  if (hide_hidden_) {
245  } else
247 
248  if (!dataset_ || dataset_->empty()) {
249  prev_segment_ = current_segment;
250  return;
251  }
252 
253  const size_t new_row_count = dataset_->size() - 1;
254 
255  // Force the view associated with this model to update when the segment changes
256  if (prev_segment_ != current_segment) {
257  dataChanged(index(0, 0), index(new_row_count, 0));
258  layoutChanged();
259  } else {
260  // Force the view associated with this model to update when we have more annotations
261  if (prev_last_row_ < new_row_count) {
262  dataChanged(index(prev_last_row_, 0), index(new_row_count, 0));
263  layoutChanged();
264  }
265  }
266 
267  prev_segment_ = current_segment;
268  prev_last_row_ = new_row_count;
269 }
270 
272 {
273  layoutAboutToBeChanged();
274 
275  hide_hidden_ = hide_hidden;
276 
277  if (hide_hidden_) {
280  } else {
282  all_annotations_without_hidden_.clear(); // To conserve memory
283  }
284 
285  if (dataset_)
286  dataChanged(index(0, 0), index(dataset_->size() - 1, 0));
287  else
288  dataChanged(QModelIndex(), QModelIndex());
289 
290  layoutChanged();
291 }
292 
294 {
295  uint64_t count = 0;
296 
297  if (!all_annotations_ || all_annotations_->empty()) {
299  return;
300  }
301 
302  for (const Annotation* ann : *all_annotations_) {
303  if (!ann->visible())
304  continue;
305 
306  if (all_annotations_without_hidden_.size() < (count + 100))
307  all_annotations_without_hidden_.resize(count + 100);
308 
309  all_annotations_without_hidden_[count++] = ann;
310  }
311 
312  all_annotations_without_hidden_.resize(count);
313 }
314 
316  QModelIndex last, int64_t sample_num)
317 {
318  bool has_highlight = false;
319  QModelIndex result;
320 
321  highlight_sample_num_ = sample_num;
322 
323  if (!dataset_ || dataset_->empty())
324  return result;
325 
326  if (sample_num >= 0) {
327  last = last.sibling(last.row() + 1, 0);
328 
329  // Check if there are any annotations visible in the table view that
330  // we would need to highlight - only then do we do so
331  QModelIndex index = first;
332  do {
333  const Annotation* ann = static_cast<const Annotation*>(index.internalPointer());
334  if (!ann) // Can happen if the table is being modified at this exact time
335  return result;
336 
337  if (((int64_t)ann->start_sample() <= sample_num) &&
338  ((int64_t)ann->end_sample() >= sample_num)) {
339  result = index;
340  has_highlight = true;
341  break;
342  }
343 
344  index = index.sibling(index.row() + 1, 0);
345  } while (index != last);
346  }
347 
348  if (has_highlight || had_highlight_before_)
349  dataChanged(first, last);
350 
351  had_highlight_before_ = has_highlight;
352 
353  return result;
354 }
355 
357 {
358  if (!hide_hidden_)
359  return;
360 
361  layoutAboutToBeChanged();
362 
364 
365  if (dataset_)
366  dataChanged(index(0, 0), index(dataset_->size() - 1, 0));
367  else
368  dataChanged(QModelIndex(), QModelIndex());
369 
370  layoutChanged();
371 }
372 
373 } // namespace tabular_decoder
374 } // namespace views
375 } // namespace pv
QModelIndex index(int row, int column, const QModelIndex &parent_idx=QModelIndex()) const override
Definition: model.cpp:176
const vector< shared_ptr< Decoder > > & decoder_stack() const
QVariant data(const QModelIndex &index, int role) const override
Definition: model.cpp:100
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 t(t+t)") define_sfop3(16
QModelIndex parent(const QModelIndex &index) const override
Definition: model.cpp:193
SIPrefix
Definition: util.hpp:50
QString format_time_si(const Timestamp &v, SIPrefix prefix, unsigned int precision, QString unit, bool sign)
Definition: util.cpp:132
const deque< const Annotation * > * all_annotations_
Definition: view.hpp:96
int get_hierarchy_level(const Annotation *ann) const
Definition: model.cpp:67
const QColor bright_color() const
Definition: annotation.cpp:136
const QColor color() const
Definition: annotation.cpp:131
Qt::ItemFlags flags(const QModelIndex &index) const override
Definition: model.cpp:151
uint64_t start_sample() const
Definition: annotation.cpp:77
uint64_t end_sample() const
Definition: annotation.cpp:82
const char * name() const
Definition: decoder.cpp:132
static bool current_theme_is_dark()
const deque< const Annotation * > * get_all_annotations_by_segment(uint32_t segment_id) const
const QString ann_class_description() const
Definition: annotation.cpp:105
QModelIndex update_highlighted_rows(QModelIndex first, QModelIndex last, int64_t sample_num)
Definition: model.cpp:315
virtual double get_samplerate() const
int rowCount(const QModelIndex &parent_idx=QModelIndex()) const override
Definition: model.cpp:200
const QString description() const
Definition: row.cpp:72
const Decoder * decoder() const
Definition: row.cpp:48
int columnCount(const QModelIndex &parent_idx=QModelIndex()) const override
Definition: model.cpp:210
QString format_time_minutes(const Timestamp &t, signed precision, bool sign)
Definition: util.cpp:200
void set_signal_and_segment(data::DecodeSignal *signal, uint32_t current_segment)
Definition: model.cpp:217
deque< const Annotation * > all_annotations_without_hidden_
Definition: view.hpp:97
const Row * row() const
Definition: rowdata.cpp:39
const RowData * row_data() const
Definition: annotation.cpp:67
const Row * row() const
Definition: annotation.cpp:72
AnnotationCollectionModel(QObject *parent=nullptr)
Definition: model.cpp:42
boost::multiprecision::number< boost::multiprecision::cpp_dec_float< 24 >, boost::multiprecision::et_off > Timestamp
Timestamp type providing yoctosecond resolution.
Definition: util.hpp:67
const QColor dark_color() const
Definition: annotation.cpp:141
uint8_t get_stack_level() const
Definition: decoder.cpp:127
const QString longest_annotation() const
Definition: annotation.cpp:118
QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const override
Definition: model.cpp:164
QVariant data_from_ann(const Annotation *ann, int index) const
Definition: model.cpp:77
const deque< const Annotation * > * dataset_
Definition: view.hpp:98