pv/subwindows/decoder_selector/subwindow.cpp
pv/views/decoder_binary/view.cpp
pv/views/decoder_binary/QHexView.cpp
- pv/views/tabular_decoder/item.cpp
pv/views/tabular_decoder/model.cpp
pv/views/tabular_decoder/view.cpp
pv/views/trace/decodetrace.cpp
Annotation::~Annotation()
{
- if (annotations_)
- delete annotations_;
+ delete annotations_;
}
uint64_t Annotation::start_sample() const
return annotations_;
}
+const QString Annotation::longest_annotation() const
+{
+ return annotations_->front();
+}
+
const Row* Annotation::row() const
{
return row_;
const QString ann_class_name() const;
const vector<QString>* annotations() const;
+ const QString longest_annotation() const;
const Row* row() const;
bool operator<(const Annotation &other) const;
namespace data {
namespace decode {
-Decoder::Decoder(const srd_decoder *const dec) :
+Decoder::Decoder(const srd_decoder *const dec, uint8_t stack_level) :
srd_decoder_(dec),
+ stack_level_(stack_level),
visible_(true),
decoder_inst_(nullptr)
{
return srd_decoder_;
}
+uint8_t Decoder::get_stack_level() const
+{
+ return stack_level_;
+}
+
const char* Decoder::name() const
{
return srd_decoder_->name;
class Decoder
{
public:
- Decoder(const srd_decoder *const dec);
+ Decoder(const srd_decoder *const dec, uint8_t stack_level);
virtual ~Decoder();
const srd_decoder* get_srd_decoder() const;
+ uint8_t get_stack_level() const;
+
const char* name() const;
bool visible() const;
void set_channels(vector<DecodeChannel*> channels);
const map<string, GVariant*>& options() const;
-
void set_option(const char *id, GVariant *value);
void apply_all_options();
private:
const srd_decoder* const srd_decoder_;
+ uint8_t stack_level_;
bool visible_;
}
}
-void RowData::emplace_annotation(srd_proto_data *pdata)
+const Annotation* RowData::emplace_annotation(srd_proto_data *pdata)
{
+ const Annotation* result = nullptr;
+
// We insert the annotation in a way so that the annotation list
// is sorted by start sample. Otherwise, we'd have to sort when
// painting, which is expensive
if (it != annotations_.begin())
it++;
- annotations_.emplace(it, pdata, row_);
+ it = annotations_.emplace(it, pdata, row_);
+ result = &(*it);
} else {
annotations_.emplace_back(pdata, row_);
+ result = &(annotations_.back());
prev_ann_start_sample_ = pdata->start_sample;
}
+
+ return result;
}
} // namespace decode
void get_annotation_subset(deque<const pv::data::decode::Annotation*> &dest,
uint64_t start_sample, uint64_t end_sample) const;
- void emplace_annotation(srd_proto_data *pdata);
+ const Annotation* emplace_annotation(srd_proto_data *pdata);
private:
deque<Annotation> annotations_;
if ((stack_.empty()) || ((stack_.size() > 0) && (name() == prev_dec_name)))
set_name(QString::fromUtf8(decoder->name));
- const shared_ptr<Decoder> dec = make_shared<Decoder>(decoder);
+ const shared_ptr<Decoder> dec = make_shared<Decoder>(decoder, stack_.size());
stack_.push_back(dec);
// Include the newly created decode channels in the channel lists
return nullptr;
}
+const deque<const Annotation*>* DecodeSignal::get_all_annotations_by_segment(
+ uint32_t segment_id) const
+{
+ try {
+ const DecodeSegment *segment = &(segments_.at(segment_id));
+ return &(segment->all_annotations);
+ } catch (out_of_range&) {
+ // Do nothing
+ }
+
+ return nullptr;
+}
+
void DecodeSignal::save_settings(QSettings &settings) const
{
SignalBase::save_settings(settings);
continue;
if (QString::fromUtf8(dec->id) == id) {
- shared_ptr<Decoder> decoder = make_shared<Decoder>(dec);
+ shared_ptr<Decoder> decoder = make_shared<Decoder>(dec, stack_.size());
stack_.push_back(decoder);
decoder->set_visible(settings.value("visible", true).toBool());
void DecodeSignal::create_decode_segment()
{
// Create annotation segment
- segments_.emplace_back(DecodeSegment());
+ segments_.emplace_back();
// Add annotation classes
for (const shared_ptr<Decoder>& dec : stack_)
if (!row)
row = dec->get_row_by_id(0);
- // Add the annotation
- ds->segments_[ds->current_segment_id_].annotation_rows.at(row).emplace_annotation(pdata);
+ RowData& row_data = ds->segments_[ds->current_segment_id_].annotation_rows.at(row);
+
+ // Add the annotation to the row
+ const Annotation* ann = row_data.emplace_annotation(pdata);
+
+ // Add the annotation to the global annotation list
+ deque<const Annotation*>& all_annotations =
+ ds->segments_[ds->current_segment_id_].all_annotations;
+ all_annotations.emplace_back(ann);
}
void DecodeSignal::binary_callback(srd_proto_data *pdata, void *decode_signal)
struct DecodeSegment
{
+ // Constructor is a no-op
+ DecodeSegment() { };
+ // Copy constructor is a no-op
+ DecodeSegment(DecodeSegment&& ds) { (void)ds; };
+
map<const Row*, RowData> annotation_rows;
pv::util::Timestamp start_time;
double samplerate;
int64_t samples_decoded_incl, samples_decoded_excl;
vector<DecodeBinaryClass> binary_classes;
+ deque<const Annotation*> all_annotations;
};
class DecodeSignal : public SignalBase
const DecodeBinaryClass* get_binary_data_class(uint32_t segment_id,
const Decoder* dec, uint32_t bin_class_id) const;
+ const deque<const Annotation*>* get_all_annotations_by_segment(uint32_t segment_id) const;
+
virtual void save_settings(QSettings &settings) const;
virtual void restore_settings(QSettings &settings);
+++ /dev/null
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2020 Soeren Apel <soeren@apelpie.net>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "pv/views/tabular_decoder/view.hpp"
-
-using std::out_of_range;
-
-namespace pv {
-namespace views {
-namespace tabular_decoder {
-
-AnnotationCollectionItem::AnnotationCollectionItem(const vector<QVariant>& data,
- shared_ptr<AnnotationCollectionItem> parent) :
- data_(data),
- parent_(parent)
-{
-}
-
-void AnnotationCollectionItem::appendSubItem(shared_ptr<AnnotationCollectionItem> item)
-{
- subItems_.push_back(item);
-}
-
-shared_ptr<AnnotationCollectionItem> AnnotationCollectionItem::subItem(int row) const
-{
- try {
- return subItems_.at(row);
- } catch (out_of_range&) {
- return nullptr;
- }
-}
-
-shared_ptr<AnnotationCollectionItem> AnnotationCollectionItem::parent() const
-{
- return parent_;
-}
-
-shared_ptr<AnnotationCollectionItem> AnnotationCollectionItem::findSubItem(
- const QVariant& value, int column)
-{
- for (shared_ptr<AnnotationCollectionItem> item : subItems_)
- if (item->data(column) == value)
- return item;
-
- return nullptr;
-}
-
-int AnnotationCollectionItem::subItemCount() const
-{
- return subItems_.size();
-}
-
-int AnnotationCollectionItem::columnCount() const
-{
- return data_.size();
-}
-
-int AnnotationCollectionItem::row() const
-{
- if (parent_)
- for (size_t i = 0; i < parent_->subItems_.size(); i++)
- if (parent_->subItems_.at(i).get() == const_cast<AnnotationCollectionItem*>(this))
- return i;
-
- return 0;
-}
-
-QVariant AnnotationCollectionItem::data(int column) const
-{
- try {
- return data_.at(column);
- } catch (out_of_range&) {
- return QVariant();
- }
-}
-
-} // namespace tabular_decoder
-} // namespace views
-} // namespace pv
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include <QDebug>
#include <QString>
#include "pv/views/tabular_decoder/view.hpp"
namespace tabular_decoder {
AnnotationCollectionModel::AnnotationCollectionModel(QObject* parent) :
- QAbstractItemModel(parent)
+ QAbstractTableModel(parent),
+ all_annotations_(nullptr),
+ prev_segment_(0),
+ prev_last_row_(0)
{
- vector<QVariant> header_data;
- header_data.emplace_back(tr("ID")); // Column #0
- header_data.emplace_back(tr("Start Time")); // Column #1
- header_data.emplace_back(tr("End Time")); // Column #2
- header_data.emplace_back(tr("Ann Row Name")); // Column #3
- header_data.emplace_back(tr("Class Row Name")); // Column #4
- header_data.emplace_back(tr("Value")); // Column #5
- root_ = make_shared<AnnotationCollectionItem>(header_data);
+ // TBD Maybe use empty columns as indentation levels to indicate stacked decoders
+ header_data_.emplace_back(tr("Start Sample")); // Column #0
+ header_data_.emplace_back(tr("Start Time")); // Column #1
+ header_data_.emplace_back(tr("Ann Row Name")); // Column #2
+ header_data_.emplace_back(tr("Ann Class Name")); // Column #3
+ header_data_.emplace_back(tr("Value")); // Column #4
}
QVariant AnnotationCollectionModel::data(const QModelIndex& index, int role) const
if (!index.isValid())
return QVariant();
- if (role == Qt::DisplayRole)
- {
- AnnotationCollectionItem* item =
- static_cast<AnnotationCollectionItem*>(index.internalPointer());
-
- return item->data(index.column());
- }
-
- if ((role == Qt::FontRole) && (index.parent().isValid()) && (index.column() == 0))
- {
- QFont font;
- font.setItalic(true);
- return QVariant(font);
+ if (role == Qt::DisplayRole) {
+ const Annotation* ann =
+ static_cast<const Annotation*>(index.internalPointer());
+
+ switch (index.column()) {
+ case 0: return QVariant((qulonglong)ann->start_sample()); // Column #0, Start Sample
+ case 1: return QVariant(0/*(qulonglong)ann->start_sample()*/); // Column #1, Start Time
+ case 2: return QVariant(ann->row()->title()); // Column #2, Ann Row Name
+ case 3: return QVariant(ann->ann_class_name()); // Column #3, Ann Class Name
+ case 4: return QVariant(ann->longest_annotation()); // Column #4, Value
+ default: return QVariant();
+ }
}
return QVariant();
Qt::ItemFlags AnnotationCollectionModel::flags(const QModelIndex& index) const
{
if (!index.isValid())
- return nullptr;
+ return 0;
return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}
int role) const
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
- return root_->data(section);
+ return header_data_.at(section);
return QVariant();
}
QModelIndex AnnotationCollectionModel::index(int row, int column,
const QModelIndex& parent_idx) const
{
- if (!hasIndex(row, column, parent_idx))
- return QModelIndex();
-
- AnnotationCollectionItem* parent = root_.get();
+ (void)parent_idx;
- if (parent_idx.isValid())
- parent = static_cast<AnnotationCollectionItem*>(parent_idx.internalPointer());
+ if (!all_annotations_)
+ return QModelIndex();
- AnnotationCollectionItem* subItem = parent->subItem(row).get();
+ if ((size_t)row > all_annotations_->size())
+ return QModelIndex();
- return subItem ? createIndex(row, column, subItem) : QModelIndex();
+ return createIndex(row, column, (void*)(all_annotations_->at(row)));
}
QModelIndex AnnotationCollectionModel::parent(const QModelIndex& index) const
{
- if (!index.isValid())
- return QModelIndex();
-
- AnnotationCollectionItem* subItem =
- static_cast<AnnotationCollectionItem*>(index.internalPointer());
+ (void)index;
- shared_ptr<AnnotationCollectionItem> parent = subItem->parent();
-
- return (parent == root_) ? QModelIndex() :
- createIndex(parent->row(), 0, parent.get());
+ return QModelIndex();
}
int AnnotationCollectionModel::rowCount(const QModelIndex& parent_idx) const
{
- AnnotationCollectionItem* parent = root_.get();
+ (void)parent_idx;
- if (parent_idx.column() > 0)
+ if (!all_annotations_)
return 0;
- if (parent_idx.isValid())
- parent = static_cast<AnnotationCollectionItem*>(parent_idx.internalPointer());
-
- return parent->subItemCount();
+ return all_annotations_->size();
}
int AnnotationCollectionModel::columnCount(const QModelIndex& parent_idx) const
{
- if (parent_idx.isValid())
- return static_cast<AnnotationCollectionItem*>(
- parent_idx.internalPointer())->columnCount();
- else
- return root_->columnCount();
+ (void)parent_idx;
+
+ return header_data_.size();
}
+void AnnotationCollectionModel::set_signal_and_segment(data::DecodeSignal* signal, uint32_t current_segment)
+{
+ all_annotations_ = signal->get_all_annotations_by_segment(current_segment);
+
+ if (!all_annotations_ || all_annotations_->empty()) {
+ prev_segment_ = current_segment;
+ return;
+ }
+
+ const size_t new_row_count = all_annotations_->size() - 1;
+
+ // Force the view associated with this model to update when the segment changes
+ if (prev_segment_ != current_segment) {
+ dataChanged(QModelIndex(), QModelIndex());
+ layoutChanged();
+ } else {
+ // Force the view associated with this model to update when we have more annotations
+ if (prev_last_row_ < new_row_count) {
+ dataChanged(index(prev_last_row_, 0, QModelIndex()),
+ index(new_row_count, 0, QModelIndex()));
+ layoutChanged();
+ }
+ }
+
+ prev_segment_ = current_segment;
+ prev_last_row_ = new_row_count;
+}
} // namespace tabular_decoder
} // namespace views
#include <climits>
+#include <QApplication>
#include <QDebug>
#include <QFileDialog>
+#include <QFontMetrics>
+#include <QHeaderView>
#include <QLabel>
#include <QMenu>
#include <QMessageBox>
using pv::data::decode::Decoder;
using pv::util::Timestamp;
+using std::make_shared;
using std::shared_ptr;
namespace pv {
namespace views {
namespace tabular_decoder {
+QSize QCustomTableView::minimumSizeHint() const
+{
+ QSize size(QTableView::sizeHint());
+
+ int width = 0;
+ for (int i = 0; i < horizontalHeader()->count(); i++)
+ if (!horizontalHeader()->isSectionHidden(i))
+ width += horizontalHeader()->sectionSizeHint(i);
+
+ size.setWidth(width + (horizontalHeader()->count() * 1));
+
+ return size;
+}
+
+QSize QCustomTableView::sizeHint() const
+{
+ return minimumSizeHint();
+}
+
View::View(Session &session, bool is_main_view, QMainWindow *parent) :
ViewBase(session, is_main_view, parent),
decoder_selector_(new QComboBox()),
save_button_(new QToolButton()),
save_action_(new QAction(this)),
- table_view_(new QTableView()),
+ table_view_(new QCustomTableView()),
model_(new AnnotationCollectionModel()),
- signal_(nullptr)
+ signal_(nullptr),
+ updating_data_(false)
{
QVBoxLayout *root_layout = new QVBoxLayout(this);
root_layout->setContentsMargins(0, 0, 0, 0);
table_view_->setSortingEnabled(true);
table_view_->sortByColumn(0, Qt::AscendingOrder);
+ const int font_height = QFontMetrics(QApplication::font()).height();
+ table_view_->verticalHeader()->setDefaultSectionSize((font_height * 5) / 4);
+
+ table_view_->horizontalHeader()->setSectionResizeMode(model_->columnCount() - 1, QHeaderView::Stretch);
+
+ table_view_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ parent->setSizePolicy(table_view_->sizePolicy());
+
reset_view_state();
}
connect(signal.get(), SIGNAL(decoder_removed(void*)),
this, SLOT(on_decoder_removed(void*)));
- // Add all decoders provided by this signal
+ // Add the top-level decoder provided by this signal
auto stack = signal->decoder_stack();
- if (stack.size() > 1) {
- for (const shared_ptr<Decoder>& dec : stack) {
- QString title = QString("%1 (%2)").arg(signal->name(), dec->name());
- decoder_selector_->addItem(title, QVariant::fromValue((void*)dec.get()));
- }
- } else
- if (!stack.empty()) {
- shared_ptr<Decoder>& dec = stack.at(0);
- decoder_selector_->addItem(signal->name(), QVariant::fromValue((void*)dec.get()));
- }
+ if (!stack.empty()) {
+ shared_ptr<Decoder>& dec = stack.at(0);
+ decoder_selector_->addItem(signal->name(), QVariant::fromValue((void*)dec.get()));
+ }
}
void View::remove_decode_signal(shared_ptr<data::DecodeSignal> signal)
if (!signal_)
return;
- // TBD
+ if (updating_data_) {
+ if (!delayed_view_updater_.isActive())
+ delayed_view_updater_.start();
+ return;
+ }
+
+ updating_data_ = true;
+
+ table_view_->setRootIndex(model_->index(1, 0, QModelIndex()));
+ model_->set_signal_and_segment(signal_, current_segment_);
+
+ updating_data_ = false;
}
void View::save_data() const
if (decoder_ == dec.get())
signal_ = ds.get();
- if (signal_) {
+ if (signal_)
connect(signal_, SIGNAL(new_annotations()), this, SLOT(on_new_annotations()));
- }
update_data();
}
DecodeSignal* signal = dynamic_cast<DecodeSignal*>(sb);
assert(signal);
- // Update all decoder entries provided by this signal
+ // Update the top-level decoder provided by this signal
auto stack = signal->decoder_stack();
- if (stack.size() > 1) {
- for (const shared_ptr<Decoder>& dec : stack) {
- QString title = QString("%1 (%2)").arg(signal->name(), dec->name());
- int index = decoder_selector_->findData(QVariant::fromValue((void*)dec.get()));
-
- if (index != -1)
- decoder_selector_->setItemText(index, title);
- }
- } else
- if (!stack.empty()) {
- shared_ptr<Decoder>& dec = stack.at(0);
- int index = decoder_selector_->findData(QVariant::fromValue((void*)dec.get()));
+ if (!stack.empty()) {
+ shared_ptr<Decoder>& dec = stack.at(0);
+ int index = decoder_selector_->findData(QVariant::fromValue((void*)dec.get()));
- if (index != -1)
- decoder_selector_->setItemText(index, signal->name());
- }
+ if (index != -1)
+ decoder_selector_->setItemText(index, signal->name());
+ }
}
void View::on_new_annotations()
void View::on_decoder_stacked(void* decoder)
{
- // TODO This doesn't change existing entries for the same signal - but it should as the naming scheme may change
-
Decoder* d = static_cast<Decoder*>(decoder);
// Find the signal that contains the selected decoder
assert(signal);
- // Add the decoder to the list
- QString title = QString("%1 (%2)").arg(signal->name(), d->name());
- decoder_selector_->addItem(title, QVariant::fromValue((void*)d));
+ if (signal == signal_)
+ update_data();
}
void View::on_decoder_removed(void* decoder)
namespace tabular_decoder {
-class AnnotationCollectionItem
-{
-public:
- AnnotationCollectionItem(const vector<QVariant>& data,
- shared_ptr<AnnotationCollectionItem> parent = nullptr);
-
- void appendSubItem(shared_ptr<AnnotationCollectionItem> item);
-
- shared_ptr<AnnotationCollectionItem> subItem(int row) const;
- shared_ptr<AnnotationCollectionItem> parent() const;
- shared_ptr<AnnotationCollectionItem> findSubItem(const QVariant& value, int column);
-
- int subItemCount() const;
- int columnCount() const;
- int row() const;
- QVariant data(int column) const;
-
-private:
- vector< shared_ptr<AnnotationCollectionItem> > subItems_;
- vector<QVariant> data_;
- shared_ptr<AnnotationCollectionItem> parent_;
-};
-
-
-class AnnotationCollectionModel : public QAbstractItemModel
+class AnnotationCollectionModel : public QAbstractTableModel
{
Q_OBJECT
int rowCount(const QModelIndex& parent_idx = QModelIndex()) const override;
int columnCount(const QModelIndex& parent_idx = QModelIndex()) const override;
+ void set_signal_and_segment(data::DecodeSignal* signal, uint32_t current_segment);
+
private:
- shared_ptr<AnnotationCollectionItem> root_;
+ vector<QVariant> header_data_;
+ const deque<const Annotation*>* all_annotations_;
+ uint32_t prev_segment_;
+ uint64_t prev_last_row_;
+};
+
+
+class QCustomTableView : public QTableView
+{
+ Q_OBJECT
+
+public:
+ QSize minimumSizeHint() const;
+ QSize sizeHint() const;
};
private:
QWidget* parent_;
- QComboBox *decoder_selector_;
+ QComboBox* decoder_selector_;
QToolButton* save_button_;
QAction* save_action_;
- QTableView* table_view_;
+ QCustomTableView* table_view_;
AnnotationCollectionModel* model_;
- data::DecodeSignal *signal_;
- const data::decode::Decoder *decoder_;
+ data::DecodeSignal* signal_;
+ const data::decode::Decoder* decoder_;
+ bool updating_data_;
};
} // namespace tabular_decoder
${PROJECT_SOURCE_DIR}/pv/subwindows/decoder_selector/subwindow.cpp
${PROJECT_SOURCE_DIR}/pv/views/decoder_binary/view.cpp
${PROJECT_SOURCE_DIR}/pv/views/decoder_binary/QHexView.cpp
- ${PROJECT_SOURCE_DIR}/pv/views/tabular_decoder/item.cpp
${PROJECT_SOURCE_DIR}/pv/views/tabular_decoder/model.cpp
${PROJECT_SOURCE_DIR}/pv/views/tabular_decoder/view.cpp
${PROJECT_SOURCE_DIR}/pv/views/trace/decodetrace.cpp