]> sigrok.org Git - pulseview.git/commitdiff
Implement annotation export for all rows
authorSoeren Apel <redacted>
Mon, 6 Aug 2018 19:25:59 +0000 (21:25 +0200)
committerUwe Hermann <redacted>
Tue, 21 Aug 2018 10:02:38 +0000 (12:02 +0200)
Needed parts are:
1) Annotations must have a reference to the row they belong to
2) DecodeSignal must offer a method to return annotations for all rows
3) Annotations must become comparable to allow for sorting
4) DecodeTrace must handle the different annotation export cases

pv/data/decode/annotation.cpp
pv/data/decode/annotation.hpp
pv/data/decode/rowdata.cpp
pv/data/decode/rowdata.hpp
pv/data/decodesignal.cpp
pv/data/decodesignal.hpp
pv/views/trace/decodetrace.cpp
pv/views/trace/decodetrace.hpp

index 96a594c3c6ce57a07f621ce9ddf9cd439fed1acd..e983b0df1c3e971ba7942c9ccadb86ed10e09bbb 100644 (file)
@@ -32,9 +32,10 @@ namespace pv {
 namespace data {
 namespace decode {
 
-Annotation::Annotation(const srd_proto_data *const pdata) :
+Annotation::Annotation(const srd_proto_data *const pdata, const Row *row) :
        start_sample_(pdata->start_sample),
-       end_sample_(pdata->end_sample)
+       end_sample_(pdata->end_sample),
+       row_(row)
 {
        assert(pdata);
        const srd_proto_data_annotation *const pda =
@@ -70,6 +71,16 @@ const vector<QString>& Annotation::annotations() const
        return annotations_;
 }
 
+const Row* Annotation::row() const
+{
+       return row_;
+}
+
+bool Annotation::operator<(const Annotation &other) const
+{
+       return (start_sample_ < other.start_sample_);
+}
+
 } // namespace decode
 } // namespace data
 } // namespace pv
index a107e0efeed7426584cbded2169ca639ffc47288..8b91c4f47e066605e307ed0301fbd1871d59436d 100644 (file)
@@ -33,24 +33,30 @@ namespace pv {
 namespace data {
 namespace decode {
 
+class Row;
+
 class Annotation
 {
 public:
        typedef uint32_t Class;
 
 public:
-       Annotation(const srd_proto_data *const pdata);
+       Annotation(const srd_proto_data *const pdata, const Row *row);
 
        uint64_t start_sample() const;
        uint64_t end_sample() const;
        Class ann_class() const;
        const vector<QString>& annotations() const;
+       const Row* row() const;
+
+       bool operator<(const Annotation &other) const;
 
 private:
        uint64_t start_sample_;
        uint64_t end_sample_;
        Class ann_class_;
        vector<QString> annotations_;
+       const Row *row_;
 };
 
 } // namespace decode
index aff4a26b333b68b2672af073c87b8ad5c392d406..2a26169eb6cc0f14adb5a107726c40ee891838b6 100644 (file)
@@ -42,9 +42,9 @@ void RowData::get_annotation_subset(
                        dest.push_back(annotation);
 }
 
-void RowData::emplace_annotation(srd_proto_data *pdata)
+void RowData::emplace_annotation(srd_proto_data *pdata, const Row *row)
 {
-       annotations_.emplace_back(pdata);
+       annotations_.emplace_back(pdata, row);
 }
 
 }  // namespace decode
index f2c6793bbbb789c286aa5e90b3519ddad8753a2c..0589ec894eb444fad4f2d0d759c98a1eabc13fc7 100644 (file)
@@ -32,6 +32,8 @@ namespace pv {
 namespace data {
 namespace decode {
 
+class Row;
+
 class RowData
 {
 public:
@@ -49,7 +51,7 @@ public:
                vector<pv::data::decode::Annotation> &dest,
                uint64_t start_sample, uint64_t end_sample) const;
 
-       void emplace_annotation(srd_proto_data *pdata);
+       void emplace_annotation(srd_proto_data *pdata, const Row *row);
 
 private:
        vector<Annotation> annotations_;
index 9da7de6766c418e759ea1f02d88ba8ab8614c551..05dc3428ab5ce8e9f2bcb964175b55fceb3557d2 100644 (file)
@@ -17,6 +17,7 @@
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <forward_list>
 #include <limits>
 
 #include <QDebug>
@@ -32,6 +33,7 @@
 #include <pv/globalsettings.hpp>
 #include <pv/session.hpp>
 
+using std::forward_list;
 using std::lock_guard;
 using std::make_pair;
 using std::make_shared;
@@ -471,6 +473,33 @@ void DecodeSignal::get_annotation_subset(
        }
 }
 
+void DecodeSignal::get_annotation_subset(
+       vector<pv::data::decode::Annotation> &dest,
+       uint32_t segment_id, uint64_t start_sample, uint64_t end_sample) const
+{
+       // Note: We put all vectors and lists on the heap, not the stack
+
+       const vector<Row> rows = visible_rows();
+
+       // Use forward_lists for faster merging
+       forward_list<Annotation> *all_ann_list = new forward_list<Annotation>();
+
+       for (const Row& row : rows) {
+               vector<Annotation> *ann_vector = new vector<Annotation>();
+               get_annotation_subset(*ann_vector, row, segment_id, start_sample, end_sample);
+
+               forward_list<Annotation> *ann_list =
+                       new forward_list<Annotation>(ann_vector->begin(), ann_vector->end());
+               delete ann_vector;
+
+               all_ann_list->merge(*ann_list);
+               delete ann_list;
+       }
+
+       move(all_ann_list->begin(), all_ann_list->end(), back_inserter(dest));
+       delete all_ann_list;
+}
+
 void DecodeSignal::save_settings(QSettings &settings) const
 {
        SignalBase::save_settings(settings);
@@ -1225,7 +1254,7 @@ void DecodeSignal::annotation_callback(srd_proto_data *pdata, void *decode_signa
        }
 
        // Add the annotation
-       (*row_iter).second.emplace_annotation(pdata);
+       (*row_iter).second.emplace_annotation(pdata, &((*row_iter).first));
 }
 
 void DecodeSignal::on_capture_state_changed(int state)
index 9bbdd2c4e2c9d68e21d69782f5d2e78ecc0e7000..f00a9f1cabaefc35de74ea66ede5544f15d99a56 100644 (file)
@@ -134,8 +134,8 @@ public:
        vector<decode::Row> visible_rows() const;
 
        /**
-        * Extracts annotations between the given sample range into a vector.
-        * Note: The annotations are unsorted and only annotations that fully
+        * Extracts annotations from a single row into a vector.
+        * Note: The annotations may be unsorted and only annotations that fully
         * fit into the sample range are considered.
         */
        void get_annotation_subset(
@@ -143,6 +143,15 @@ public:
                const decode::Row &row, uint32_t segment_id, uint64_t start_sample,
                uint64_t end_sample) const;
 
+       /**
+        * Extracts annotations from all rows into a vector.
+        * Note: The annotations may be unsorted and only annotations that fully
+        * fit into the sample range are considered.
+        */
+       void get_annotation_subset(
+               vector<pv::data::decode::Annotation> &dest,
+               uint32_t segment_id, uint64_t start_sample, uint64_t end_sample) const;
+
        virtual void save_settings(QSettings &settings) const;
 
        virtual void restore_settings(QSettings &settings);
index 865c492604fea09e0647defbb6bd5fb95f123d5c..dd964b5a7cc578cc208ac1e7a413f1b046fa9cb3 100644 (file)
@@ -339,20 +339,34 @@ QMenu* DecodeTrace::create_view_context_menu(QWidget *parent, QPoint &click_pos)
 
        QMenu *const menu = new QMenu(parent);
 
+       QAction *const export_all_rows =
+                       new QAction(tr("Export all annotations"), this);
+       export_all_rows->setIcon(QIcon::fromTheme("document-save-as",
+               QIcon(":/icons/document-save-as.png")));
+       connect(export_all_rows, SIGNAL(triggered()), this, SLOT(on_export_all_rows()));
+       menu->addAction(export_all_rows);
+
        QAction *const export_row =
                        new QAction(tr("Export all annotations for this row"), this);
        export_row->setIcon(QIcon::fromTheme("document-save-as",
                QIcon(":/icons/document-save-as.png")));
-       connect(export_row, SIGNAL(triggered()),
-               this, SLOT(on_export_row()));
+       connect(export_row, SIGNAL(triggered()), this, SLOT(on_export_row()));
        menu->addAction(export_row);
 
+       menu->addSeparator();
+
+       QAction *const export_all_rows_from_here =
+               new QAction(tr("Export all annotations, starting here"), this);
+       export_all_rows_from_here->setIcon(QIcon::fromTheme("document-save-as",
+               QIcon(":/icons/document-save-as.png")));
+       connect(export_all_rows_from_here, SIGNAL(triggered()), this, SLOT(on_export_all_rows_from_here()));
+       menu->addAction(export_all_rows_from_here);
+
        QAction *const export_row_from_here =
                new QAction(tr("Export annotations for this row, starting here"), this);
        export_row_from_here->setIcon(QIcon::fromTheme("document-save-as",
                QIcon(":/icons/document-save-as.png")));
-       connect(export_row_from_here, SIGNAL(triggered()),
-               this, SLOT(on_export_row_from_here()));
+       connect(export_row_from_here, SIGNAL(triggered()), this, SLOT(on_export_row_from_here()));
        menu->addAction(export_row_from_here);
 
        return menu;
@@ -914,6 +928,62 @@ QComboBox* DecodeTrace::create_channel_selector_init_state(QWidget *parent,
        return selector;
 }
 
+void DecodeTrace::export_annotations(vector<Annotation> *annotations) const
+{
+       using namespace pv::data::decode;
+
+       GlobalSettings settings;
+       const QString dir = settings.value("MainWindow/SaveDirectory").toString();
+
+       const QString file_name = QFileDialog::getSaveFileName(
+               owner_->view(), tr("Export annotations"), dir, tr("Text Files (*.txt);;All Files (*)"));
+
+       if (file_name.isEmpty())
+               return;
+
+       QString format = settings.value(GlobalSettings::Key_Dec_ExportFormat).toString();
+       const QString quote = format.contains("%q") ? "\"" : "";
+       format = format.remove("%q");
+
+       QFile file(file_name);
+       if (file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) {
+               QTextStream out_stream(&file);
+
+               for (Annotation &ann : *annotations) {
+                       const QString sample_range = QString("%1-%2") \
+                               .arg(QString::number(ann.start_sample()), QString::number(ann.end_sample()));
+
+                       const QString class_name = quote + ann.row()->class_name() + quote;
+
+                       QString all_ann_text;
+                       for (const QString &s : ann.annotations())
+                               all_ann_text = all_ann_text + quote + s + quote + ",";
+                       all_ann_text.chop(1);
+
+                       const QString first_ann_text = quote + ann.annotations().front() + quote;
+
+                       QString out_text = format;
+                       out_text = out_text.replace("%s", sample_range);
+                       out_text = out_text.replace("%d",
+                               quote + QString::fromUtf8(ann.row()->decoder()->name) + quote);
+                       out_text = out_text.replace("%c", class_name);
+                       out_text = out_text.replace("%1", first_ann_text);
+                       out_text = out_text.replace("%a", all_ann_text);
+                       out_stream << out_text << '\n';
+               }
+
+               if (out_stream.status() == QTextStream::Ok)
+                       return;
+       }
+
+       QMessageBox msg(owner_->view());
+       msg.setText(tr("Error"));
+       msg.setInformativeText(tr("File %1 could not be written to.").arg(file_name));
+       msg.setStandardButtons(QMessageBox::Ok);
+       msg.setIcon(QMessageBox::Warning);
+       msg.exec();
+}
+
 void DecodeTrace::on_new_annotations()
 {
        if (!delayed_trace_updater_.isActive())
@@ -1026,6 +1096,12 @@ void DecodeTrace::on_export_row()
        on_export_row_from_here();
 }
 
+void DecodeTrace::on_export_all_rows()
+{
+       selected_samplepos_ = 0;
+       on_export_all_rows_from_here();
+}
+
 void DecodeTrace::on_export_row_from_here()
 {
        using namespace pv::data::decode;
@@ -1033,61 +1109,31 @@ void DecodeTrace::on_export_row_from_here()
        if (!selected_row_)
                return;
 
-       vector<Annotation> annotations;
+       vector<Annotation> *annotations = new vector<Annotation>();
 
-       decode_signal_->get_annotation_subset(annotations, *selected_row_,
+       decode_signal_->get_annotation_subset(*annotations, *selected_row_,
                current_segment_, selected_samplepos_, ULLONG_MAX);
 
-       if (annotations.empty())
+       if (annotations->empty())
                return;
 
-       GlobalSettings settings;
-       const QString dir = settings.value("MainWindow/SaveDirectory").toString();
-
-       const QString file_name = QFileDialog::getSaveFileName(
-               owner_->view(), tr("Export annotations"), dir, tr("Text Files (*.txt);;All Files (*)"));
-
-       if (file_name.isEmpty())
-               return;
-
-       const QString format = settings.value(GlobalSettings::Key_Dec_ExportFormat).toString();
-       const QString quote = format.contains("%q") ? "\"" : "";
-       const QString class_name = selected_row_->class_name();
-
-       QFile file(file_name);
-       if (file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) {
-               QTextStream out_stream(&file);
-
-               for (Annotation &ann : annotations) {
-                       const QString sample_range = QString("%1-%2").arg(ann.start_sample()).arg(
-                               ann.end_sample());
+       export_annotations(annotations);
+       delete annotations;
+}
 
-                       QString all_ann_text;
-                       for (const QString &s : ann.annotations())
-                               all_ann_text = all_ann_text + quote + s + quote + ",";
-                       all_ann_text.chop(1);
+void DecodeTrace::on_export_all_rows_from_here()
+{
+       using namespace pv::data::decode;
 
-                       const QString first_ann_text = quote + ann.annotations().front() + quote;
+       vector<Annotation> *annotations = new vector<Annotation>();
 
-                       QString out_text = format;
-                       out_text = out_text.replace("%s", sample_range);
-                       out_text = out_text.replace("%d", decode_signal_->name());
-                       out_text = out_text.replace("%c", class_name);
-                       out_text = out_text.replace("%1", first_ann_text);
-                       out_text = out_text.replace("%a", all_ann_text);
-                       out_stream << out_text << '\n';
-               }
+       decode_signal_->get_annotation_subset(*annotations, current_segment_,
+               selected_samplepos_, ULLONG_MAX);
 
-               if (out_stream.status() == QTextStream::Ok)
-                       return;
-       }
+       if (!annotations->empty())
+               export_annotations(annotations);
 
-       QMessageBox msg(owner_->view());
-       msg.setText(tr("Error"));
-       msg.setInformativeText(tr("File %1 could not be written to.").arg(file_name));
-       msg.setStandardButtons(QMessageBox::Ok);
-       msg.setIcon(QMessageBox::Warning);
-       msg.exec();
+       delete annotations;
 }
 
 } // namespace trace
index 8f8765c17dc8857423901e353e9d574282f84960..b30fc433fa3a6d2e74148109f1119ed72d4149a9 100644 (file)
@@ -177,6 +177,8 @@ private:
        QComboBox* create_channel_selector_init_state(QWidget *parent,
                const data::DecodeChannel *ch);
 
+       void export_annotations(vector<data::decode::Annotation> *annotations) const;
+
 public:
        virtual void hover_point_changed(const QPoint &hp);
 
@@ -201,7 +203,9 @@ private Q_SLOTS:
        void on_show_hide_decoder(int index);
 
        void on_export_row();
+       void on_export_all_rows();
        void on_export_row_from_here();
+       void on_export_all_rows_from_here();
 
 private:
        pv::Session &session_;