$search
00001 /* 00002 Aseba - an event-based framework for distributed robot control 00003 Copyright (C) 2007--2012: 00004 Stephane Magnenat <stephane at magnenat dot net> 00005 (http://stephane.magnenat.net) 00006 and other contributors, see authors.txt for details 00007 00008 This program is free software: you can redistribute it and/or modify 00009 it under the terms of the GNU Lesser General Public License as published 00010 by the Free Software Foundation, version 3 of the License. 00011 00012 This program is distributed in the hope that it will be useful, 00013 but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00015 GNU Lesser General Public License for more details. 00016 00017 You should have received a copy of the GNU Lesser General Public License 00018 along with this program. If not, see <http://www.gnu.org/licenses/>. 00019 */ 00020 00021 #ifdef HAVE_QWT 00022 00023 #include "EventViewer.h" 00024 #include <QVBoxLayout> 00025 #include <QHBoxLayout> 00026 #include <QCheckBox> 00027 #include <QPushButton> 00028 #include <QDoubleSpinBox> 00029 #include <QFile> 00030 #include <QFileDialog> 00031 #include <QSettings> 00032 #include <QtDebug> 00033 00034 #include <qwt_plot.h> 00035 #include <qwt_plot_curve.h> 00036 #include <qwt_legend.h> 00037 00038 #if QWT_VERSION >= 0x060000 00039 #include <qwt_series_data.h> 00040 #else 00041 #include <qwt_data.h> 00042 #endif 00043 00044 #include <EventViewer.moc> 00045 00046 namespace Aseba 00047 { 00050 00051 #if QWT_VERSION >= 0x060000 00052 class EventDataWrapper : public QwtSeriesData<QPointF> 00053 { 00054 private: 00055 std::deque<double>& _x; 00056 std::deque<sint16>& _y; 00057 00058 public: 00059 EventDataWrapper(std::deque<double>& _x, std::deque<sint16>& _y) : 00060 _x(_x), 00061 _y(_y) 00062 { } 00063 virtual QRectF boundingRect () const { return qwtBoundingRect(*this); } 00064 virtual QPointF sample (size_t i) const { return QPointF(_x[i], double(_y[i])); } 00065 virtual size_t size () const { return _x.size(); } 00066 }; 00067 #else 00068 class EventDataWrapper : public QwtData 00069 { 00070 private: 00071 std::deque<double>& _x; 00072 std::deque<sint16>& _y; 00073 00074 public: 00075 EventDataWrapper(std::deque<double>& _x, std::deque<sint16>& _y) : 00076 _x(_x), 00077 _y(_y) 00078 { } 00079 virtual QwtData * copy () const { return new EventDataWrapper(*this); } 00080 virtual size_t size () const { return _x.size(); } 00081 virtual double x (size_t i) const { return _x[i]; } 00082 virtual double y (size_t i) const { return (double)_y[i]; } 00083 }; 00084 #endif 00085 00086 EventViewer::EventViewer(unsigned eventId, const QString& eventName, unsigned eventVariablesCount, MainWindow::EventViewers* eventsViewers) : 00087 eventId(eventId), 00088 eventsViewers(eventsViewers), 00089 values(eventVariablesCount), 00090 startingTime(QTime::currentTime()) 00091 { 00092 // create plot 00093 plot = new QwtPlot; 00094 plot->setCanvasBackground(Qt::white); 00095 plot->setAxisTitle(plot->xBottom, tr("Time (seconds)")); 00096 plot->setAxisTitle(plot->yLeft, tr("Values")); 00097 00098 QwtLegend *legend = new QwtLegend; 00099 //legend->setItemMode(QwtLegend::CheckableItem); 00100 plot->insertLegend(legend, QwtPlot::BottomLegend); 00101 00102 for (size_t i = 0; i < values.size(); i++) 00103 { 00104 QwtPlotCurve *curve = new QwtPlotCurve(QString("%0").arg(i)); 00105 #if QWT_VERSION >= 0x060000 00106 curve->setData(new EventDataWrapper(timeStamps, values[i])); 00107 #else 00108 curve->setData(EventDataWrapper(timeStamps, values[i])); 00109 #endif 00110 curve->attach(plot); 00111 curve->setPen(QPen(QColor::fromHsv((i * 360) / values.size(), 255, 100), 2)); 00112 } 00113 00114 QVBoxLayout *layout = new QVBoxLayout(this); 00115 layout->addWidget(plot); 00116 00117 // add control 00118 QHBoxLayout *controlLayout = new QHBoxLayout; 00119 00120 status = new QLabel(tr("Recording...")); 00121 controlLayout->addWidget(status); 00122 00123 pauseRunButton = new QPushButton(QPixmap(QString(":/images/pause.png")), tr("&Pause")); 00124 connect(pauseRunButton, SIGNAL(clicked()), SLOT(pauseRunCapture())); 00125 controlLayout->addWidget(pauseRunButton); 00126 00127 QPushButton *clearButton = new QPushButton(QPixmap(QString(":/images/reset.png")), tr("&Clear")); 00128 connect(clearButton, SIGNAL(clicked()), SLOT(clearPlot())); 00129 controlLayout->addWidget(clearButton); 00130 00131 timeWindowCheckBox = new QCheckBox(tr("time &window:")); 00132 controlLayout->addWidget(timeWindowCheckBox); 00133 00134 timeWindowLength = new QDoubleSpinBox; 00135 timeWindowLength->setSuffix("s"); 00136 connect(timeWindowCheckBox, SIGNAL(toggled(bool)), timeWindowLength, SLOT(setEnabled(bool))); 00137 timeWindowLength->setValue(10); 00138 timeWindowLength->setEnabled(false); 00139 controlLayout->addWidget(timeWindowLength); 00140 controlLayout->addStretch(); 00141 00142 QPushButton *saveToFileButton = new QPushButton(QPixmap(QString(":/images/filesaveas.png")), tr("Save &As...")); 00143 connect(saveToFileButton, SIGNAL(clicked()), SLOT(saveToFile())); 00144 controlLayout->addWidget(saveToFileButton); 00145 00146 layout->addLayout(controlLayout); 00147 00148 // receive events 00149 eventsViewers->insert(eventId, this); 00150 isCapturing = true; 00151 } 00152 00153 EventViewer::~EventViewer() 00154 { 00155 if (eventsViewers && isCapturing) 00156 eventsViewers->remove(eventId, this); 00157 } 00158 00159 void EventViewer::addData(const VariablesDataVector& data) 00160 { 00161 const double elapsedTime = (double)startingTime.msecsTo(QTime::currentTime()) / 1000.; 00162 if (timeWindowCheckBox->isChecked()) 00163 { 00164 // remove old data 00165 while ( 00166 (!timeStamps.empty()) && 00167 (elapsedTime - timeStamps[0] > timeWindowLength->value()) 00168 ) 00169 { 00170 timeStamps.pop_front(); 00171 for (size_t i = 0; i < values.size(); i++) 00172 values[i].pop_front(); 00173 } 00174 } 00175 00176 00177 timeStamps.push_back(elapsedTime); 00178 for (size_t i = 0; i < values.size(); i++) 00179 { 00180 if (i < data.size()) 00181 { 00182 values[i].push_back(data[i]); 00183 } 00184 else 00185 { 00186 values[i].push_back(0); 00187 } 00188 } 00189 plot->replot(); 00190 } 00191 00192 void EventViewer::pauseRunCapture() 00193 { 00194 if (isCapturing) 00195 { 00196 if (eventsViewers) 00197 eventsViewers->remove(eventId, this); 00198 isCapturing = false; 00199 status->setText(tr("Paused...")); 00200 pauseRunButton->setIcon(QPixmap(QString(":/images/mix_record.png"))); 00201 pauseRunButton->setText(tr("&Record")); 00202 } 00203 else 00204 { 00205 if (eventsViewers) 00206 eventsViewers->insert(eventId, this); 00207 isCapturing = true; 00208 status->setText(tr("Recording...")); 00209 pauseRunButton->setIcon(QPixmap(QString(":/images/pause.png"))); 00210 pauseRunButton->setText(tr("&Pause")); 00211 } 00212 } 00213 00214 void EventViewer::clearPlot() 00215 { 00216 for (size_t i = 0; i < values.size(); i++) 00217 values[i].clear(); 00218 timeStamps.clear(); 00219 plot->replot(); 00220 } 00221 00222 void EventViewer::saveToFile() 00223 { 00224 QSettings settings; 00225 QString lastFileName(settings.value("EventViewer/exportFileName", "").toString()); 00226 QString fileName = QFileDialog::getSaveFileName(this, tr("Save plot data to file"), lastFileName, "All Files (*);;CSV files (*.csv);;Text files (*.txt)"); 00227 00228 QFile file(fileName); 00229 if (!file.open(QFile::WriteOnly | QFile::Truncate)) 00230 return; 00231 00232 settings.setValue("EventViewer/exportFileName", fileName); 00233 00234 QTextStream out(&file); 00235 for (size_t i = 0; i < timeStamps.size(); ++i) 00236 { 00237 out << timeStamps[i] << " "; 00238 for (size_t j = 0; j < values.size(); ++j) 00239 { 00240 out << values[j][i]; 00241 if (j + 1 < values.size()) 00242 out << " "; 00243 } 00244 out << "\n"; 00245 } 00246 } 00247 00249 }; // Aseba 00250 00251 #endif // HAVE_QWT