From: Soeren Apel Date: Thu, 23 Apr 2020 21:00:00 +0000 (+0200) Subject: TabularDecView: Implement saving as CSV X-Git-Url: https://sigrok.org/gitaction?a=commitdiff_plain;h=be0f59038035f7a560082feba35524d418a9153a;p=pulseview.git TabularDecView: Implement saving as CSV --- diff --git a/pv/views/tabular_decoder/view.cpp b/pv/views/tabular_decoder/view.cpp index b5024d92..5446ff8e 100644 --- a/pv/views/tabular_decoder/view.cpp +++ b/pv/views/tabular_decoder/view.cpp @@ -51,6 +51,11 @@ namespace pv { namespace views { namespace tabular_decoder { +const char* SaveTypeNames[SaveTypeCount] = { + "CSV, commas escaped", + "CSV, fields quoted" +}; + QSize QCustomTableView::minimumSizeHint() const { QSize size(QTableView::sizeHint()); @@ -117,6 +122,11 @@ View::View(Session &session, bool is_main_view, QMainWindow *parent) : connect(save_menu, SIGNAL(triggered(QAction*)), this, SLOT(on_actionSave_triggered(QAction*))); + for (int i = 0; i < SaveTypeCount; i++) { + QAction *const action = save_menu->addAction(tr(SaveTypeNames[i])); + action->setData(qVariantFromValue(i)); + } + save_button_->setMenu(save_menu); save_button_->setDefaultAction(save_action_); save_button_->setPopupMode(QToolButton::MenuButtonPopup); @@ -124,7 +134,7 @@ View::View(Session &session, bool is_main_view, QMainWindow *parent) : // Set up the table view table_view_->setModel(model_); table_view_->setSelectionBehavior(QAbstractItemView::SelectRows); - table_view_->setSelectionMode(QAbstractItemView::SingleSelection); + table_view_->setSelectionMode(QAbstractItemView::ContiguousSelection); table_view_->setSortingEnabled(true); table_view_->sortByColumn(0, Qt::AscendingOrder); @@ -245,44 +255,94 @@ void View::update_data() updating_data_ = false; } -void View::save_data() const +void View::save_data_as_csv(unsigned int save_type) const { + // Note: We try to follow RFC 4180 (https://tools.ietf.org/html/rfc4180) + assert(decoder_); assert(signal_); if (!signal_) return; -/* GlobalSettings settings; + const bool save_all = !table_view_->selectionModel()->hasSelection(); + + GlobalSettings settings; const QString dir = settings.value("MainWindow/SaveDirectory").toString(); const QString file_name = QFileDialog::getSaveFileName( - parent_, tr("Save Binary Data"), dir, tr("Binary Data Files (*.bin);;All Files (*)")); + parent_, tr("Save Annotations as CSV"), dir, tr("CSV Files (*.csv);;Text Files (*.txt);;All Files (*)")); if (file_name.isEmpty()) return; QFile file(file_name); if (file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { - pair selection = hex_view_->get_selection(); + QTextStream out_stream(&file); + + if (save_all) + table_view_->selectAll(); + + // Write out header columns in visual order, not logical order + for (int i = 0; i < table_view_->horizontalHeader()->count(); i++) { + int column = table_view_->horizontalHeader()->logicalIndex(i); + + if (table_view_->horizontalHeader()->isSectionHidden(column)) + continue; + + const QString title = model_->headerData(column, Qt::Horizontal, Qt::DisplayRole).toString(); + + if (save_type == SaveTypeCSVEscaped) + out_stream << title; + else + out_stream << '"' << title << '"'; + + if (i < (table_view_->horizontalHeader()->count() - 1)) + out_stream << ","; + } + out_stream << '\r' << '\n'; - vector data; - data.resize(selection.second - selection.first + 1); - signal_->get_merged_binary_data_chunks_by_offset(current_segment_, decoder_, - bin_class_id_, selection.first, selection.second, &data); + QModelIndexList selected_rows = table_view_->selectionModel()->selectedRows(); - int64_t bytes_written = file.write((const char*)data.data(), data.size()); + for (int i = 0; i < selected_rows.size(); i++) { + const int row = selected_rows.at(i).row(); + + // Write out columns in visual order, not logical order + for (int c = 0; c < table_view_->horizontalHeader()->count(); c++) { + const int column = table_view_->horizontalHeader()->logicalIndex(c); + + if (table_view_->horizontalHeader()->isSectionHidden(column)) + continue; + + const QModelIndex idx = model_->index(row, column, QModelIndex()); + QString s = model_->data(idx, Qt::DisplayRole).toString(); + + if (save_type == SaveTypeCSVEscaped) + out_stream << s.replace(",", "\\,"); + else + out_stream << '"' << s.replace("\"", "\"\"") << '"'; + + if (c < (table_view_->horizontalHeader()->count() - 1)) + out_stream << ","; + } + + out_stream << '\r' << '\n'; + } + + if (out_stream.status() == QTextStream::Ok) { + if (save_all) + table_view_->clearSelection(); - if ((bytes_written == -1) || ((uint64_t)bytes_written != data.size())) { - QMessageBox msg(parent_); - msg.setText(tr("Error") + "\n\n" + tr("File %1 could not be written to.").arg(file_name)); - msg.setStandardButtons(QMessageBox::Ok); - msg.setIcon(QMessageBox::Warning); - msg.exec(); return; } - } */ + } + + QMessageBox msg(parent_); + msg.setText(tr("Error") + "\n\n" + tr("File %1 could not be written to.").arg(file_name)); + msg.setStandardButtons(QMessageBox::Ok); + msg.setIcon(QMessageBox::Warning); + msg.exec(); } void View::on_selected_decoder_changed(int index) @@ -389,9 +449,12 @@ void View::on_decoder_removed(void* decoder) void View::on_actionSave_triggered(QAction* action) { - (void)action; + int save_type = SaveTypeCSVQuoted; + + if (action) + save_type = action->data().toInt(); - save_data(); + save_data_as_csv(save_type); } void View::on_table_item_clicked(const QModelIndex& index) diff --git a/pv/views/tabular_decoder/view.hpp b/pv/views/tabular_decoder/view.hpp index 484b81be..c08b7136 100644 --- a/pv/views/tabular_decoder/view.hpp +++ b/pv/views/tabular_decoder/view.hpp @@ -36,6 +36,16 @@ namespace views { namespace tabular_decoder { +// When adding an entry here, don't forget to update SaveTypeNames as well +enum SaveType { + SaveTypeCSVEscaped, + SaveTypeCSVQuoted, + SaveTypeCount // Indicates how many save types there are, must always be last +}; + +extern const char* SaveTypeNames[SaveTypeCount]; + + class AnnotationCollectionModel : public QAbstractTableModel, public GlobalSettingsInterface { Q_OBJECT @@ -107,7 +117,7 @@ private: void reset_data(); void update_data(); - void save_data() const; + void save_data_as_csv(unsigned int save_type) const; private Q_SLOTS: void on_selected_decoder_changed(int index);