00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include <QApplication>
00020 #include <QFile>
00021 #include <QTextStream>
00022
00023 #include <rqt_multiplot/PlotCursor.h>
00024 #include <rqt_multiplot/PlotWidget.h>
00025
00026 #include "rqt_multiplot/PlotTableWidget.h"
00027
00028 namespace rqt_multiplot {
00029
00030
00031
00032
00033
00034 PlotTableWidget::PlotTableWidget(QWidget* parent) :
00035 QWidget(parent),
00036 layout_(new QGridLayout(this)),
00037 config_(0),
00038 registry_(new MessageSubscriberRegistry(this)),
00039 bagReader_(new BagReader(this)) {
00040 setLayout(layout_);
00041 setAutoFillBackground(true);
00042
00043 layout_->setHorizontalSpacing(20);
00044 layout_->setVerticalSpacing(20);
00045
00046 connect(bagReader_, SIGNAL(readingStarted()), this,
00047 SLOT(bagReaderReadingStarted()));
00048 connect(bagReader_, SIGNAL(readingProgressChanged(double)), this,
00049 SLOT(bagReaderReadingProgressChanged(double)));
00050 connect(bagReader_, SIGNAL(readingFinished()), this,
00051 SLOT(bagReaderReadingFinished()));
00052 connect(bagReader_, SIGNAL(readingFailed(const QString&)), this,
00053 SLOT(bagReaderReadingFailed(const QString&)));
00054 }
00055
00056 PlotTableWidget::~PlotTableWidget() {
00057 }
00058
00059
00060
00061
00062
00063 void PlotTableWidget::setConfig(PlotTableConfig* config) {
00064 if (config != config_) {
00065 if (config_) {
00066 disconnect(config_, SIGNAL(backgroundColorChanged(const QColor&)),
00067 this, SLOT(configBackgroundColorChanged(const QColor&)));
00068 disconnect(config_, SIGNAL(foregroundColorChanged(const QColor&)),
00069 this, SLOT(configForegroundColorChanged(const QColor&)));
00070 disconnect(config_, SIGNAL(numPlotsChanged(size_t, size_t)), this,
00071 SLOT(configNumPlotsChanged(size_t, size_t)));
00072 disconnect(config_, SIGNAL(linkScaleChanged(bool)), this,
00073 SLOT(configLinkScaleChanged(bool)));
00074 disconnect(config_, SIGNAL(trackPointsChanged(bool)), this,
00075 SLOT(configTrackPointsChanged(bool)));
00076 }
00077
00078 config_ = config;
00079
00080 if (config) {
00081 connect(config, SIGNAL(backgroundColorChanged(const QColor&)),
00082 this, SLOT(configBackgroundColorChanged(const QColor&)));
00083 connect(config, SIGNAL(foregroundColorChanged(const QColor&)),
00084 this, SLOT(configForegroundColorChanged(const QColor&)));
00085 connect(config, SIGNAL(numPlotsChanged(size_t, size_t)), this,
00086 SLOT(configNumPlotsChanged(size_t, size_t)));
00087 connect(config, SIGNAL(linkScaleChanged(bool)), this,
00088 SLOT(configLinkScaleChanged(bool)));
00089 connect(config, SIGNAL(trackPointsChanged(bool)), this,
00090 SLOT(configTrackPointsChanged(bool)));
00091
00092 configBackgroundColorChanged(config->getBackgroundColor());
00093 configForegroundColorChanged(config->getForegroundColor());
00094 configNumPlotsChanged(config->getNumRows(), config->getNumColumns());
00095 configLinkScaleChanged(config->isScaleLinked());
00096 configTrackPointsChanged(config->arePointsTracked());
00097 }
00098 }
00099 }
00100
00101 PlotTableConfig* PlotTableWidget::getConfig() const {
00102 return config_;
00103 }
00104
00105 size_t PlotTableWidget::getNumRows() const {
00106 return plotWidgets_.count();
00107 }
00108
00109 size_t PlotTableWidget::getNumColumns() const {
00110 if (!plotWidgets_.isEmpty())
00111 return plotWidgets_[0].count();
00112 else
00113 return 0;
00114 }
00115
00116 PlotWidget* PlotTableWidget::getPlotWidget(size_t row, size_t column) const {
00117 return plotWidgets_[row][column];
00118 }
00119
00120 MessageSubscriberRegistry* PlotTableWidget::getRegistry() const {
00121 return registry_;
00122 }
00123
00124 BagReader* PlotTableWidget::getBagReader() const {
00125 return bagReader_;
00126 }
00127
00128
00129
00130
00131
00132 void PlotTableWidget::runPlots() {
00133 for (size_t row = 0; row < plotWidgets_.count(); ++row)
00134 for (size_t column = 0; column < plotWidgets_[row].count(); ++ column)
00135 plotWidgets_[row][column]->run();
00136 }
00137
00138 void PlotTableWidget::pausePlots() {
00139 for (size_t row = 0; row < plotWidgets_.count(); ++row)
00140 for (size_t column = 0; column < plotWidgets_[row].count(); ++ column)
00141 plotWidgets_[row][column]->pause();
00142 }
00143
00144 void PlotTableWidget::clearPlots() {
00145 for (size_t row = 0; row < plotWidgets_.count(); ++row)
00146 for (size_t column = 0; column < plotWidgets_[row].count(); ++ column)
00147 plotWidgets_[row][column]->clear();
00148 }
00149
00150 void PlotTableWidget::requestReplot() {
00151 for (size_t row = 0; row < plotWidgets_.count(); ++row)
00152 for (size_t column = 0; column < plotWidgets_[row].count(); ++ column)
00153 plotWidgets_[row][column]->requestReplot();
00154 }
00155
00156 void PlotTableWidget::forceReplot() {
00157 for (size_t row = 0; row < plotWidgets_.count(); ++row)
00158 for (size_t column = 0; column < plotWidgets_[row].count(); ++ column)
00159 plotWidgets_[row][column]->forceReplot();
00160 }
00161
00162 void PlotTableWidget::renderToPixmap(QPixmap& pixmap) {
00163 size_t numRows = getNumRows();
00164 size_t numColumns = getNumColumns();
00165
00166 if (numRows && numColumns) {
00167 double plotWidth = (pixmap.width()-20.0*(numColumns-1.0))/numColumns;
00168 double plotHeight = (pixmap.height()-20.0*(numRows-1.0))/numRows;
00169
00170 double y = 0.0;
00171 for (size_t row = 0; row < plotWidgets_.count();
00172 ++row, y += plotHeight+20.0) {
00173 double x = 0.0;
00174
00175 for (size_t column = 0; column < plotWidgets_[row].count();
00176 ++column, x += plotWidth+20.0)
00177 plotWidgets_[row][column]->renderToPixmap(pixmap,
00178 QRectF(x, y, plotWidth, plotHeight));
00179 }
00180 }
00181 }
00182
00183 void PlotTableWidget::writeFormattedCurveAxisTitles(QStringList&
00184 formattedAxisTitles) {
00185 formattedAxisTitles.clear();
00186
00187 for (size_t row = 0; row < plotWidgets_.count(); ++row) {
00188 for (size_t column = 0; column < plotWidgets_[row].count(); ++column) {
00189 QStringList formattedCurveAxisTitles;
00190
00191 plotWidgets_[row][column]->writeFormattedCurveAxisTitles(
00192 formattedCurveAxisTitles);
00193
00194 formattedAxisTitles.append(formattedCurveAxisTitles);
00195 }
00196 }
00197 }
00198
00199 void PlotTableWidget::writeFormattedCurveData(QList<QStringList>&
00200 formattedData) {
00201 formattedData.clear();
00202
00203 for (size_t row = 0; row < plotWidgets_.count(); ++row) {
00204 for (size_t column = 0; column < plotWidgets_[row].count(); ++column) {
00205 QList<QStringList> formattedCurveData;
00206
00207 plotWidgets_[row][column]->writeFormattedCurveData(
00208 formattedCurveData);
00209
00210 formattedData.append(formattedCurveData);
00211 }
00212 }
00213 }
00214
00215 void PlotTableWidget::loadFromBagFile(const QString& fileName) {
00216 clearPlots();
00217
00218 for (size_t row = 0; row < plotWidgets_.count(); ++row)
00219 for (size_t column = 0; column < plotWidgets_[row].count(); ++column)
00220 plotWidgets_[row][column]->setBroker(bagReader_);
00221
00222 runPlots();
00223
00224 bagReader_->read(fileName);
00225 }
00226
00227 void PlotTableWidget::saveToImageFile(const QString& fileName) {
00228 QPixmap pixmap(1280, 1024);
00229
00230 pixmap.fill(Qt::transparent);
00231 renderToPixmap(pixmap);
00232
00233 pixmap.save(fileName, "PNG");
00234 }
00235
00236 void PlotTableWidget::saveToTextFile(const QString& fileName) {
00237 QFile file(fileName);
00238
00239 if (file.open(QIODevice::WriteOnly)) {
00240 QStringList formattedAxisTitles;
00241 QList<QStringList> formattedData;
00242
00243 writeFormattedCurveAxisTitles(formattedAxisTitles);
00244 writeFormattedCurveData(formattedData);
00245
00246 QTextStream stream(&file);
00247
00248 stream << "# " << formattedAxisTitles.join(", ") << "\n";
00249
00250 size_t row = 0;
00251
00252 while (true) {
00253 QStringList dataLineParts;
00254 bool finished = true;
00255
00256 for (size_t column = 0; column < formattedData.count(); ++column) {
00257 if (row < formattedData[column].count()) {
00258 dataLineParts.append(formattedData[column][row]);
00259 finished &= false;
00260 }
00261 else
00262 dataLineParts.append(QString());
00263 }
00264
00265 if (!finished) {
00266 stream << dataLineParts.join(", ") << "\n";
00267 row++;
00268 }
00269 else
00270 break;
00271 }
00272 }
00273 }
00274
00275 void PlotTableWidget::updatePlotScale(const BoundingRectangle& bounds,
00276 PlotWidget* excluded) {
00277 BoundingRectangle validBounds = bounds;
00278
00279 if (!bounds.isValid()) {
00280 BoundingRectangle currentBounds;
00281
00282 for (size_t row = 0; row < plotWidgets_.count(); ++row)
00283 for (size_t column = 0; column < plotWidgets_[row].count(); ++column)
00284 currentBounds += plotWidgets_[row][column]->getCurrentScale();
00285
00286 if (bounds.getMaximum().x() <= bounds.getMinimum().x()) {
00287 validBounds.getMinimum().setX(currentBounds.getMinimum().x());
00288 validBounds.getMaximum().setX(currentBounds.getMaximum().x());
00289 }
00290
00291 if (bounds.getMaximum().y() <= bounds.getMinimum().y()) {
00292 validBounds.getMinimum().setY(currentBounds.getMinimum().y());
00293 validBounds.getMaximum().setY(currentBounds.getMaximum().y());
00294 }
00295 }
00296
00297 for (size_t row = 0; row < plotWidgets_.count(); ++row) {
00298 for (size_t column = 0; column < plotWidgets_[row].count(); ++column) {
00299 if (excluded != plotWidgets_[row][column])
00300 plotWidgets_[row][column]->setCurrentScale(validBounds);
00301 }
00302 }
00303 }
00304
00305
00306
00307
00308
00309 void PlotTableWidget::configBackgroundColorChanged(const QColor& color) {
00310 QPalette currentPalette = palette();
00311
00312 currentPalette.setColor(QPalette::Window, color);
00313 currentPalette.setColor(QPalette::Base, color);
00314
00315 setPalette(currentPalette);
00316
00317 forceReplot();
00318 }
00319
00320 void PlotTableWidget::configForegroundColorChanged(const QColor& color) {
00321 QPalette currentPalette = palette();
00322
00323 currentPalette.setColor(QPalette::WindowText, color);
00324 currentPalette.setColor(QPalette::Text, color);
00325
00326 setPalette(currentPalette);
00327 }
00328
00329 void PlotTableWidget::configNumPlotsChanged(size_t numRows, size_t
00330 numColumns) {
00331 size_t oldNumRows = plotWidgets_.count();
00332 size_t oldNumColumns = oldNumRows ? plotWidgets_[0].count() : 0;
00333
00334 if (!numRows || !numColumns) {
00335 numRows = 0;
00336 numColumns = 0;
00337 }
00338
00339 QVector<QVector<PlotWidget* > > plotWidgets(numRows);
00340 QGridLayout* layout = new QGridLayout();
00341
00342 layout->setHorizontalSpacing(20);
00343 layout->setVerticalSpacing(20);
00344
00345 for (size_t row = 0; row < numRows; ++row) {
00346 plotWidgets[row].resize(numColumns);
00347
00348 for (size_t column = 0; column < numColumns; ++column) {
00349 if ((row < oldNumRows) && (column < oldNumColumns))
00350 plotWidgets[row][column] = plotWidgets_[row][column];
00351 else {
00352 plotWidgets[row][column] = new PlotWidget(this);
00353
00354 connect(plotWidgets[row][column], SIGNAL(preferredScaleChanged(
00355 const BoundingRectangle&)), this, SLOT(plotPreferredScaleChanged(
00356 const BoundingRectangle&)));
00357 connect(plotWidgets[row][column], SIGNAL(currentScaleChanged(
00358 const BoundingRectangle&)), this, SLOT(plotCurrentScaleChanged(
00359 const BoundingRectangle&)));
00360 connect(plotWidgets[row][column]->getCursor(), SIGNAL(
00361 activeChanged(bool)), this, SLOT(plotCursorActiveChanged(bool)));
00362 connect(plotWidgets[row][column]->getCursor(), SIGNAL(
00363 currentPositionChanged(const QPointF&)), this, SLOT(
00364 plotCursorCurrentPositionChanged(const QPointF&)));
00365 connect(plotWidgets[row][column], SIGNAL(pausedChanged(bool)),
00366 this, SLOT(plotPausedChanged(bool)));
00367 connect(plotWidgets[row][column], SIGNAL(stateChanged(int)),
00368 this, SLOT(plotStateChanged(int)));
00369 }
00370
00371 plotWidgets[row][column]->setConfig(config_->getPlotConfig(
00372 row, column));
00373 plotWidgets[row][column]->setBroker(registry_);
00374
00375 if (config_->isScaleLinked())
00376 plotWidgets[row][column]->setCurrentScale(plotWidgets[0][0]->
00377 getCurrentScale());
00378
00379 plotWidgets[row][column]->getCursor()->setTrackPoints(
00380 config_->arePointsTracked());
00381
00382 layout->addWidget(plotWidgets[row][column], row, column);
00383 }
00384 }
00385
00386 if ((numRows == 1) && (numColumns == 1))
00387 plotWidgets[0][0]->setCanChangeState(false);
00388 else
00389 plotWidgets[0][0]->setCanChangeState(true);
00390
00391 for (size_t row = 0; row < oldNumRows; ++row)
00392 for (size_t column = 0; column < oldNumColumns; ++column)
00393 if ((row >= numRows) || (column >= numColumns))
00394 delete plotWidgets_[row][column];
00395
00396 plotWidgets_ = plotWidgets;
00397
00398 delete layout_;
00399 layout_ = layout;
00400 setLayout(layout);
00401
00402 emit plotPausedChanged();
00403 }
00404
00405 void PlotTableWidget::configLinkScaleChanged(bool link) {
00406 if (link) {
00407 BoundingRectangle bounds;
00408
00409 for (size_t row = 0; row < plotWidgets_.count(); ++row)
00410 for (size_t column = 0; column < plotWidgets_[row].count(); ++ column)
00411 bounds += plotWidgets_[row][column]->getPreferredScale();
00412
00413 updatePlotScale(bounds);
00414 }
00415 }
00416
00417 void PlotTableWidget::configTrackPointsChanged(bool track) {
00418 for (size_t row = 0; row < plotWidgets_.count(); ++row)
00419 for (size_t column = 0; column < plotWidgets_[row].count(); ++ column)
00420 plotWidgets_[row][column]->getCursor()->setTrackPoints(track);
00421 }
00422
00423 void PlotTableWidget::bagReaderReadingStarted() {
00424 emit jobStarted("Reading bag from [file://"+
00425 bagReader_->getFileName()+"]...");
00426 }
00427
00428 void PlotTableWidget::bagReaderReadingProgressChanged(double progress) {
00429 emit jobProgressChanged(progress);
00430 }
00431
00432 void PlotTableWidget::bagReaderReadingFinished() {
00433 pausePlots();
00434
00435 for (size_t row = 0; row < plotWidgets_.count(); ++row)
00436 for (size_t column = 0; column < plotWidgets_[row].count(); ++column)
00437 plotWidgets_[row][column]->setBroker(registry_);
00438
00439 emit jobFinished("Read bag from [file://"+
00440 bagReader_->getFileName()+"]");
00441 }
00442
00443 void PlotTableWidget::bagReaderReadingFailed(const QString& error) {
00444 pausePlots();
00445
00446 for (size_t row = 0; row < plotWidgets_.count(); ++row)
00447 for (size_t column = 0; column < plotWidgets_[row].count(); ++column)
00448 plotWidgets_[row][column]->setBroker(registry_);
00449
00450 emit jobFailed("Failed to read bag from [file://"+
00451 bagReader_->getFileName()+"]");
00452 }
00453
00454 void PlotTableWidget::plotPreferredScaleChanged(const BoundingRectangle&
00455 bounds) {
00456 if (config_) {
00457 if (config_->isScaleLinked()) {
00458 BoundingRectangle bounds;
00459
00460 for (size_t row = 0; row < plotWidgets_.count(); ++row)
00461 for (size_t column = 0; column < plotWidgets_[row].count(); ++ column)
00462 bounds += plotWidgets_[row][column]->getPreferredScale();
00463
00464 updatePlotScale(bounds);
00465 }
00466 else
00467 static_cast<PlotWidget*>(sender())->setCurrentScale(bounds);
00468 }
00469 }
00470
00471 void PlotTableWidget::plotCurrentScaleChanged(const BoundingRectangle&
00472 bounds) {
00473 if (config_ && config_->isScaleLinked())
00474 updatePlotScale(bounds, static_cast<PlotWidget*>(sender()));
00475 }
00476
00477 void PlotTableWidget::plotCursorActiveChanged(bool active) {
00478 if (config_ && config_->isCursorLinked()) {
00479 for (size_t row = 0; row < plotWidgets_.count(); ++row)
00480 for (size_t column = 0; column < plotWidgets_[row].count(); ++ column)
00481 if (sender() != plotWidgets_[row][column])
00482 plotWidgets_[row][column]->getCursor()->setActive(active);
00483 }
00484 }
00485
00486 void PlotTableWidget::plotCursorCurrentPositionChanged(const QPointF&
00487 position) {
00488 if (config_ && config_->isCursorLinked()) {
00489 for (size_t row = 0; row < plotWidgets_.count(); ++row)
00490 for (size_t column = 0; column < plotWidgets_[row].count(); ++ column)
00491 if (sender() != plotWidgets_[row][column])
00492 plotWidgets_[row][column]->getCursor()->setCurrentPosition(
00493 position);
00494 }
00495 }
00496
00497 void PlotTableWidget::plotPausedChanged(bool paused) {
00498 emit plotPausedChanged();
00499 }
00500
00501 void PlotTableWidget::plotStateChanged(int state) {
00502 for (size_t row = 0; row < plotWidgets_.count(); ++row) {
00503 for (size_t column = 0; column < plotWidgets_[row].count(); ++ column) {
00504 if (state == PlotWidget::Maximized) {
00505 if (sender() != plotWidgets_[row][column])
00506 plotWidgets_[row][column]->hide();
00507 }
00508 else if (state == PlotWidget::Normal)
00509 plotWidgets_[row][column]->show();
00510 }
00511 }
00512 }
00513
00514 }