diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -89,6 +89,8 @@ add_executable(${PROGRAM_NAME} WIN32 src/dataformatpanel.cpp src/tooltipfilter.cpp src/sneakylineedit.cpp + src/channelmanager.cpp + src/framebufferseries.cpp misc/windows_icon.rc ${UI_FILES} ${RES_FILES} diff --git a/serialplot.pro b/serialplot.pro --- a/serialplot.pro +++ b/serialplot.pro @@ -52,7 +52,9 @@ SOURCES += \ src/commandedit.cpp \ src/dataformatpanel.cpp \ src/tooltipfilter.cpp \ - src/sneakylineedit.cpp + src/sneakylineedit.cpp \ + src/channelmanager.cpp \ + src/framebufferseries.cpp HEADERS += \ src/mainwindow.h \ @@ -75,7 +77,9 @@ HEADERS += \ src/commandedit.h \ src/dataformatpanel.h \ src/tooltipfilter.h \ - src/sneakylineedit.h + src/sneakylineedit.h \ + src/channelmanager.h \ + src/framebufferseries.h FORMS += \ src/mainwindow.ui \ diff --git a/src/channelmanager.cpp b/src/channelmanager.cpp new file mode 100644 --- /dev/null +++ b/src/channelmanager.cpp @@ -0,0 +1,145 @@ +/* + Copyright © 2016 Hasan Yavuz Özderya + + This file is part of serialplot. + + serialplot 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 3 of the License, or + (at your option) any later version. + + serialplot 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 serialplot. If not, see . +*/ + +#include "channelmanager.h" + +#include +#include + +ChannelManager::ChannelManager(unsigned numberOfChannels, unsigned numberOfSamples, QObject *parent) : + QObject(parent) +{ + _numOfChannels = numberOfChannels; + _numOfSamples = numberOfSamples; + + QStringList channelNamesList; + + for (unsigned int i = 0; i < numberOfChannels; i++) + { + channelBuffers.append(new FrameBuffer(numberOfSamples)); + channelNamesList << QString("Channel %1").arg(i+1); + } + + _channelNames.setStringList(channelNamesList); + + connect(&_channelNames, &QStringListModel::dataChanged, + this, &ChannelManager::onChannelNameDataChange); +} + +ChannelManager::~ChannelManager() +{ + for (auto buffer : channelBuffers) + { + delete buffer; + } +} + +unsigned ChannelManager::numOfChannels() +{ + return channelBuffers.size(); +} + +unsigned ChannelManager::numOfSamples() +{ + return _numOfSamples; +} + +void ChannelManager::setNumOfChannels(unsigned number) +{ + unsigned int oldNum = channelBuffers.size(); + + if (number > oldNum) + { + // add new channels + for (unsigned int i = 0; i < number - oldNum; i++) + { + channelBuffers.append(new FrameBuffer(_numOfSamples)); + addChannelName(QString("Channel %1").arg(oldNum+i+1)); + } + } + else if(number < oldNum) + { + // remove channels + for (unsigned int i = oldNum-1; i > number-1; i--) + { + delete channelBuffers.takeLast(); + _channelNames.removeRow(i); + } + } + + emit numOfChannelsChanged(number); +} + +void ChannelManager::setNumOfSamples(unsigned number) +{ + _numOfSamples = number; + + for (int ci = 0; ci < channelBuffers.size(); ci++) + { + channelBuffers[ci]->resize(_numOfSamples); + } + + emit numOfSamplesChanged(number); +} + +FrameBuffer* ChannelManager::channelBuffer(unsigned channel) +{ + return channelBuffers[channel]; +} + +QStringListModel* ChannelManager::channelNames() +{ + return &_channelNames; +} + +QString ChannelManager::channelName(unsigned channel) +{ + return _channelNames.data(_channelNames.index(channel, 0), Qt::DisplayRole).toString(); +} + +void ChannelManager::setChannelName(unsigned channel, QString name) +{ + _channelNames.setData(_channelNames.index(channel, 0), QVariant(name), Qt::DisplayRole); +} + +void ChannelManager::addChannelName(QString name) +{ + _channelNames.insertRow(_channelNames.rowCount()); + setChannelName(_channelNames.rowCount()-1, name); +} + +void ChannelManager::onChannelNameDataChange(const QModelIndex & topLeft, + const QModelIndex & bottomRight, + const QVector & roles) +{ + Q_UNUSED(roles); + int start = topLeft.row(); + int end = bottomRight.row(); + + // TODO: maybe check `roles` parameter, can't think of a reason for current use case + for (int i = start; i <= end; i++) + { + emit channelNameChanged(i, channelName(i)); + } +} + +void ChannelManager::addChannelData(unsigned channel, double* data, unsigned size) +{ + channelBuffer(channel)->addSamples(data, size); +} diff --git a/src/channelmanager.h b/src/channelmanager.h new file mode 100644 --- /dev/null +++ b/src/channelmanager.h @@ -0,0 +1,68 @@ +/* + Copyright © 2016 Hasan Yavuz Özderya + + This file is part of serialplot. + + serialplot 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 3 of the License, or + (at your option) any later version. + + serialplot 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 serialplot. If not, see . +*/ + +#ifndef CHANNELMANAGER_H +#define CHANNELMANAGER_H + +#include +#include +#include +#include + +#include "framebuffer.h" + +class ChannelManager : public QObject +{ + Q_OBJECT +public: + explicit ChannelManager(unsigned numberOfChannels, unsigned numberOfSamples, QObject *parent = 0); + ~ChannelManager(); + + unsigned numOfChannels(); + unsigned numOfSamples(); + FrameBuffer* channelBuffer(unsigned channel); + QStringListModel* channelNames(); + QString channelName(unsigned channel); + +signals: + void numOfChannelsChanged(unsigned value); + void numOfSamplesChanged(unsigned value); + void channelNameChanged(unsigned channel, QString name); + +public slots: + void setNumOfChannels(unsigned number); + void setNumOfSamples(unsigned number); + void setChannelName(unsigned channel, QString name); + void addChannelData(unsigned channel, double* data, unsigned size); + +private: + unsigned _numOfChannels; + unsigned _numOfSamples; + QList channelBuffers; + QStringListModel _channelNames; + + void addChannelName(QString name); ///< appends a new channel name at the end of list + +private slots: + void onChannelNameDataChange(const QModelIndex & topLeft, + const QModelIndex & bottomRight, + const QVector & roles = QVector ()); +}; + +#endif // CHANNELMANAGER_H diff --git a/src/dataformatpanel.cpp b/src/dataformatpanel.cpp --- a/src/dataformatpanel.cpp +++ b/src/dataformatpanel.cpp @@ -21,12 +21,13 @@ #include "ui_dataformatpanel.h" #include +#include #include "utils.h" #include "floatswap.h" DataFormatPanel::DataFormatPanel(QSerialPort* port, - QList* channelBuffers, + ChannelManager* channelMan, QWidget *parent) : QWidget(parent), ui(new Ui::DataFormatPanel) @@ -34,7 +35,7 @@ DataFormatPanel::DataFormatPanel(QSerial ui->setupUi(this); serialPort = port; - _channelBuffers = channelBuffers; + _channelMan = channelMan; paused = false; // setup number format buttons @@ -319,6 +320,6 @@ template double DataFormatPa void DataFormatPanel::addChannelData(unsigned int channel, double* data, unsigned size) { - (*_channelBuffers)[channel]->addSamples(data, size); + _channelMan->addChannelData(channel, data, size); sampleCount += size; } diff --git a/src/dataformatpanel.h b/src/dataformatpanel.h --- a/src/dataformatpanel.h +++ b/src/dataformatpanel.h @@ -28,6 +28,7 @@ #include #include "framebuffer.h" +#include "channelmanager.h" namespace Ui { class DataFormatPanel; @@ -39,7 +40,7 @@ class DataFormatPanel : public QWidget public: explicit DataFormatPanel(QSerialPort* port, - QList* channelBuffers, + ChannelManager* channelMan, QWidget *parent = 0); ~DataFormatPanel(); @@ -77,7 +78,7 @@ private: QButtonGroup numberFormatButtons; QSerialPort* serialPort; - QList* _channelBuffers; + ChannelManager* _channelMan; unsigned int _numOfChannels; NumberFormat numberFormat; diff --git a/src/framebuffer.cpp b/src/framebuffer.cpp --- a/src/framebuffer.cpp +++ b/src/framebuffer.cpp @@ -45,7 +45,7 @@ void FrameBuffer::resize(size_t size) for (int i = fill_start; i < int(size); i++) { - newData[i] = _sample(i - offset); + newData[i] = sample(i - offset); } // fill the beginning of the new data @@ -141,17 +141,12 @@ size_t FrameBuffer::size() const return _size; } -QPointF FrameBuffer::sample(size_t i) const -{ - return QPointF(i, _sample(i)); -} - QRectF FrameBuffer::boundingRect() const { return _boundingRect; } -double FrameBuffer::_sample(size_t i) const +double FrameBuffer::sample(size_t i) const { size_t index = headIndex + i; if (index >= _size) index -= _size; diff --git a/src/framebuffer.h b/src/framebuffer.h --- a/src/framebuffer.h +++ b/src/framebuffer.h @@ -20,11 +20,10 @@ #ifndef FRAMEBUFFER_H #define FRAMEBUFFER_H -#include #include #include -class FrameBuffer : public QwtSeriesData +class FrameBuffer { public: FrameBuffer(size_t size); @@ -34,10 +33,10 @@ public: void addSamples(double* samples, size_t size); void clear(); // fill 0 - // QwtSeriesData implementations + // QwtSeriesData related implementations size_t size() const; - QPointF sample(size_t i) const; QRectF boundingRect() const; + double sample(size_t i) const; private: size_t _size; // size of `data` @@ -45,8 +44,6 @@ private: size_t headIndex; // indicates the actual `0` index of the ring buffer QRectF _boundingRect; - - double _sample(size_t i) const; }; #endif // FRAMEBUFFER_H diff --git a/src/framebufferseries.cpp b/src/framebufferseries.cpp new file mode 100644 --- /dev/null +++ b/src/framebufferseries.cpp @@ -0,0 +1,40 @@ +/* + Copyright © 2016 Hasan Yavuz Özderya + + This file is part of serialplot. + + serialplot 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 3 of the License, or + (at your option) any later version. + + serialplot 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 serialplot. If not, see . +*/ + +#include "framebufferseries.h" + +FrameBufferSeries::FrameBufferSeries(FrameBuffer* buffer) +{ + _buffer = buffer; +} + +size_t FrameBufferSeries::size() const +{ + return _buffer->size(); +} + +QPointF FrameBufferSeries::sample(size_t i) const +{ + return QPointF(i, _buffer->sample(i)); +} + +QRectF FrameBufferSeries::boundingRect() const +{ + return _buffer->boundingRect(); +} diff --git a/src/framebufferseries.h b/src/framebufferseries.h new file mode 100644 --- /dev/null +++ b/src/framebufferseries.h @@ -0,0 +1,49 @@ +/* + Copyright © 2016 Hasan Yavuz Özderya + + This file is part of serialplot. + + serialplot 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 3 of the License, or + (at your option) any later version. + + serialplot 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 serialplot. If not, see . +*/ + +#ifndef FRAMEBUFFERSERIES_H +#define FRAMEBUFFERSERIES_H + +#include +#include +#include + +#include "framebuffer.h" + +/** + * This class provides an interface for actual FrameBuffer + * object. That way we can keep our data structures relatively + * isolated from Qwt. Otherwise QwtPlotCurve owns FrameBuffer + * structures. + */ +class FrameBufferSeries : public QwtSeriesData +{ +public: + FrameBufferSeries(FrameBuffer* buffer); + + // QwtSeriesData implementations + size_t size() const; + QPointF sample(size_t i) const; + QRectF boundingRect() const; + +private: + FrameBuffer* _buffer; +}; + +#endif // FRAMEBUFFERSERIES_H diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -34,6 +34,7 @@ #include +#include "framebufferseries.h" #include "utils.h" #include "defines.h" #include "version.h" @@ -56,9 +57,10 @@ MainWindow::MainWindow(QWidget *parent) ui(new Ui::MainWindow), aboutDialog(this), portControl(&serialPort), + channelMan(1, 1, this), commandPanel(&serialPort), - dataFormatPanel(&serialPort, &channelBuffers), - snapshotMan(this, &channelBuffers) + dataFormatPanel(&serialPort, &channelMan), + snapshotMan(this, &channelMan) { ui->setupUi(this); ui->tabWidget->insertTab(0, &portControl, "Port"); @@ -153,17 +155,26 @@ MainWindow::MainWindow(QWidget *parent) numOfSamples = ui->spNumOfSamples->value(); unsigned numOfChannels = dataFormatPanel.numOfChannels(); - QObject::connect(&dataFormatPanel, - &DataFormatPanel::numOfChannelsChanged, - this, - &MainWindow::onNumOfChannelsChanged); + channelMan.setNumOfSamples(ui->spNumOfSamples->value()); + channelMan.setNumOfChannels(dataFormatPanel.numOfChannels()); + + connect(&dataFormatPanel, &DataFormatPanel::numOfChannelsChanged, + &channelMan, &ChannelManager::setNumOfChannels); - // init channel data and curve list + connect(&channelMan, &ChannelManager::numOfChannelsChanged, + this, &MainWindow::onNumOfChannelsChanged); + + connect(&channelMan, &ChannelManager::channelNameChanged, + this, &MainWindow::onChannelNameChanged); + + ui->lvChannelNames->setModel(channelMan.channelNames()); + + // init curve list for (unsigned int i = 0; i < numOfChannels; i++) { - channelBuffers.append(new FrameBuffer(numOfSamples)); - curves.append(new QwtPlotCurve(QString("Channel %1").arg(i+1))); - curves[i]->setSamples(channelBuffers[i]); + curves.append(new QwtPlotCurve(channelMan.channelName(i))); + curves[i]->setSamples( + new FrameBufferSeries(channelMan.channelBuffer(i))); curves[i]->setPen(Plot::makeColor(i)); curves[i]->attach(ui->plot); } @@ -319,9 +330,9 @@ required privileges or device is already void MainWindow::clearPlot() { - for (int ci = 0; ci < channelBuffers.size(); ci++) + for (unsigned ci = 0; ci < channelMan.numOfChannels(); ci++) { - channelBuffers[ci]->clear(); + channelMan.channelBuffer(ci)->clear(); } ui->plot->replot(); } @@ -329,30 +340,26 @@ void MainWindow::clearPlot() void MainWindow::onNumOfSamplesChanged(int value) { numOfSamples = value; - - for (int ci = 0; ci < channelBuffers.size(); ci++) - { - channelBuffers[ci]->resize(numOfSamples); - } - + channelMan.setNumOfSamples(value); ui->plot->replot(); } void MainWindow::onNumOfChannelsChanged(unsigned value) { - unsigned int oldNum = channelBuffers.size(); + unsigned int oldNum = curves.size(); unsigned numOfChannels = value; if (numOfChannels > oldNum) { // add new channels - for (unsigned int i = 0; i < numOfChannels - oldNum; i++) + for (unsigned int i = oldNum; i < numOfChannels; i++) { - channelBuffers.append(new FrameBuffer(numOfSamples)); - curves.append(new QwtPlotCurve(QString("Channel %1").arg(oldNum+i+1))); - curves.last()->setSamples(channelBuffers.last()); - curves.last()->setPen(Plot::makeColor(curves.length()-1)); - curves.last()->attach(ui->plot); + QwtPlotCurve* curve = new QwtPlotCurve(channelMan.channelName(i)); + curve->setSamples( + new FrameBufferSeries(channelMan.channelBuffer(i))); + curve->setPen(Plot::makeColor(i)); + curve->attach(ui->plot); + curves.append(curve); } } else if(numOfChannels < oldNum) @@ -360,11 +367,23 @@ void MainWindow::onNumOfChannelsChanged( // remove channels for (unsigned int i = 0; i < oldNum - numOfChannels; i++) { - // also deletes owned FrameBuffer delete curves.takeLast(); - channelBuffers.removeLast(); } } + + ui->plot->replot(); +} + +void MainWindow::onChannelNameChanged(unsigned channel, QString name) +{ + // This slot is triggered also when a new channel is added, in + // this case curve list doesn't contain said channel. No worries, + // since `onNumOfChannelsChanged` slot will update curve list. + if ((int) channel < curves.size()) // check if channel exists in curve list + { + curves[channel]->setTitle(name); + ui->plot->replot(); + } } void MainWindow::onAutoScaleChecked(bool checked) @@ -454,7 +473,7 @@ void MainWindow::onExportCsv() { QTextStream fileStream(&file); - unsigned numOfChannels = channelBuffers.size(); + unsigned numOfChannels = channelMan.numOfChannels(); for (unsigned int ci = 0; ci < numOfChannels; ci++) { fileStream << "Channel " << ci; @@ -466,7 +485,7 @@ void MainWindow::onExportCsv() { for (unsigned int ci = 0; ci < numOfChannels; ci++) { - fileStream << channelBuffers[ci]->sample(i).y(); + fileStream << channelMan.channelBuffer(ci)->sample(i); if (ci != numOfChannels-1) fileStream << ","; } fileStream << '\n'; diff --git a/src/mainwindow.h b/src/mainwindow.h --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -39,6 +39,7 @@ #include "dataformatpanel.h" #include "ui_about_dialog.h" #include "framebuffer.h" +#include "channelmanager.h" #include "snapshotmanager.h" namespace Ui { @@ -69,7 +70,8 @@ private: QList curves; // Note: FrameBuffer s are owned by their respective QwtPlotCurve s. - QList channelBuffers; + // QList channelBuffers; + ChannelManager channelMan; QLabel spsLabel; @@ -90,6 +92,7 @@ private slots: void onYScaleChanged(); void onRangeSelected(); void onNumOfChannelsChanged(unsigned value); + void onChannelNameChanged(unsigned channel, QString name); void clearPlot(); diff --git a/src/mainwindow.ui b/src/mainwindow.ui --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -57,17 +57,28 @@ - - - Qt::Horizontal - - - - 370 - 20 - - - + + + + + font-weight: bold; + + + Channel Names: + + + + + + + + 16777215 + 170 + + + + + diff --git a/src/snapshot.cpp b/src/snapshot.cpp --- a/src/snapshot.cpp +++ b/src/snapshot.cpp @@ -92,6 +92,16 @@ void Snapshot::setName(QString name) emit nameChanged(this); } +void Snapshot::setChannelNames(QStringList names) +{ + _channelNames = names; +} + +QString Snapshot::channelName(unsigned channel) +{ + return _channelNames[channel]; +} + void Snapshot::save(QString fileName) { // TODO: remove code duplication (MainWindow::onExportCsv) @@ -107,7 +117,7 @@ void Snapshot::save(QString fileName) // print header for (unsigned int ci = 0; ci < numOfChannels; ci++) { - fileStream << "Channel " << ci; + fileStream << channelName(ci); if (ci != numOfChannels-1) fileStream << ","; } fileStream << '\n'; diff --git a/src/snapshot.h b/src/snapshot.h --- a/src/snapshot.h +++ b/src/snapshot.h @@ -25,6 +25,7 @@ #include #include #include +#include class SnapshotView; @@ -42,6 +43,8 @@ public: QString name(); void setName(QString name); + void setChannelNames(QStringList names); // must be called when setting data! + QString channelName(unsigned channel); void save(QString fileName); /// save snapshot data as CSV @@ -51,6 +54,7 @@ signals: private: QString _name; + QStringList _channelNames; QAction _showAction; QAction _deleteAction; QMainWindow* mainWindow; diff --git a/src/snapshotmanager.cpp b/src/snapshotmanager.cpp --- a/src/snapshotmanager.cpp +++ b/src/snapshotmanager.cpp @@ -24,18 +24,19 @@ #include #include #include +#include #include "snapshotmanager.h" SnapshotManager::SnapshotManager(QMainWindow* mainWindow, - QList* channelBuffers) : + ChannelManager* channelMan) : _menu("Snapshots"), _takeSnapshotAction("Take Snapshot", this), loadSnapshotAction("Load Snapshots", this), clearAction("Clear Snapshots", this) { _mainWindow = mainWindow; - _channelBuffers = channelBuffers; + _channelMan = channelMan; _takeSnapshotAction.setToolTip("Take a snapshot of current plot"); _takeSnapshotAction.setShortcut(QKeySequence("F5")); @@ -64,17 +65,18 @@ void SnapshotManager::takeSnapshot() QString name = QTime::currentTime().toString("'Snapshot ['HH:mm:ss']'"); auto snapshot = new Snapshot(_mainWindow, name); - unsigned numOfChannels = _channelBuffers->size(); - unsigned numOfSamples = _channelBuffers->at(0)->size(); + unsigned numOfChannels = _channelMan->numOfChannels(); + unsigned numOfSamples = _channelMan->numOfSamples(); for (unsigned ci = 0; ci < numOfChannels; ci++) { snapshot->data.append(QVector(numOfSamples)); for (unsigned i = 0; i < numOfSamples; i++) { - snapshot->data[ci][i] = _channelBuffers->at(ci)->sample(i); + snapshot->data[ci][i] = QPointF(i, _channelMan->channelBuffer(ci)->sample(i)); } } + snapshot->setChannelNames(_channelMan->channelNames()->stringList()); addSnapshot(snapshot); } @@ -145,7 +147,8 @@ void SnapshotManager::loadSnapshotFromFi // read first row as headlines and determine number of channels auto headLine = QString(file.readLine()); - unsigned numOfChannels = headLine.split(',').size(); + QStringList channelNames = headLine.split(','); + unsigned numOfChannels = channelNames.size(); // read data QVector> data(numOfChannels); @@ -183,6 +186,7 @@ void SnapshotManager::loadSnapshotFromFi auto snapshot = new Snapshot(_mainWindow, QFileInfo(fileName).baseName()); snapshot->data = data; + snapshot->setChannelNames(channelNames); addSnapshot(snapshot, false); } diff --git a/src/snapshotmanager.h b/src/snapshotmanager.h --- a/src/snapshotmanager.h +++ b/src/snapshotmanager.h @@ -25,6 +25,7 @@ #include #include "framebuffer.h" +#include "channelmanager.h" #include "snapshot.h" class SnapshotManager : public QObject @@ -32,7 +33,7 @@ class SnapshotManager : public QObject Q_OBJECT public: - SnapshotManager(QMainWindow* mainWindow, QList* channelBuffers); + SnapshotManager(QMainWindow* mainWindow, ChannelManager* channelMan); ~SnapshotManager(); QMenu* menu(); @@ -40,7 +41,7 @@ public: private: QMainWindow* _mainWindow; - QList* _channelBuffers; + ChannelManager* _channelMan; QList snapshots; diff --git a/src/snapshotview.cpp b/src/snapshotview.cpp --- a/src/snapshotview.cpp +++ b/src/snapshotview.cpp @@ -35,7 +35,7 @@ SnapshotView::SnapshotView(QWidget *pare for (unsigned ci = 0; ci < numOfChannels; ci++) { - QwtPlotCurve* curve = new QwtPlotCurve(); + QwtPlotCurve* curve = new QwtPlotCurve(snapshot->channelName(ci)); curves.append(curve); curve->setSamples(snapshot->data[ci]); curve->setPen(Plot::makeColor(ci));