00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00024
00025 #include <qvgui/qvplot.h>
00026 #include <qwt_plot_layout.h>
00027 #include <qwt_scale_draw.h>
00028 #include <qwt_scale_widget.h>
00029 #include <qwt_legend.h>
00030 #include <qwt_legend_item.h>
00031 #include <qwt_plot_canvas.h>
00032 #include <qpainter.h>
00033
00034 QColor QVPlot::colors[] = {
00035 Qt::red,
00036 Qt::green,
00037 Qt::blue,
00038 Qt::cyan,
00039 Qt::magenta,
00040 Qt::darkRed,
00041 Qt::darkGreen,
00042 Qt::darkBlue,
00043 Qt::darkCyan,
00044 Qt::darkMagenta
00045 };
00046
00048
00049 QVPlot::PieMarker::PieMarker(QVPlot *plot): qvplot(plot)
00050 {
00051 setZ(1000);
00052 setRenderHint(QwtPlotItem::RenderAntialiased, true);
00053 }
00054
00055 int QVPlot::PieMarker::rtti() const
00056 {
00057 return QwtPlotItem::Rtti_PlotUserItem;
00058 }
00059
00060 void QVPlot::PieMarker::draw(QPainter *p, const QwtScaleMap &, const QwtScaleMap &, const QRect &rect) const
00061 {
00062 const int margin = 5;
00063
00064 QRect pieRect;
00065 pieRect.setX(rect.x() + margin);
00066 pieRect.setY(rect.y() + margin);
00067
00068 int diameter = ( (rect.height() > rect.width()) ? rect.width(): rect.height() ) / 4;
00069 pieRect.setHeight(diameter);
00070 pieRect.setWidth(diameter);
00071
00072 int angle = (int)(5760 * 0.75);
00073
00074 double sum = 0;
00075 for(int i = 0; i < qvplot->linkCont.size(); i++)
00076 for(int j = 0; j < qvplot->linkCont[i].properties.size(); j++)
00077 for(int k = 0; k < qvplot->linkCont[i].properties[j].curves.size(); k++)
00078 if (qvplot->linkCont[i].properties[j].curves[k].plot->style() != QwtPlotCurve::NoCurve)
00079 sum += qvplot->linkCont[i].properties[j].curves[k].history[0];
00080
00081
00082 for(int i = 0; i < qvplot->linkCont.size(); i++)
00083 for(int j = 0; j < qvplot->linkCont[i].properties.size(); j++)
00084 for(int k = 0; k < qvplot->linkCont[i].properties[j].curves.size(); k++)
00085 {
00086 const QwtPlotCurve *curve = qvplot->linkCont[i].properties[j].curves[k].plot;
00087 if ( (curve->style() != QwtPlotCurve::NoCurve) && (curve->dataSize() > 0) )
00088 {
00089 const int value = (int)(5760 * qvplot->linkCont[i].properties[j].curves[k].history[0] / sum);
00090 p->save();
00091 p->setBrush(QBrush(curve->pen().color(), Qt::SolidPattern));
00092 if ( value != 0 )
00093 p->drawPie(pieRect, -angle, -value);
00094 p->restore();
00095
00096 angle += value;
00097 }
00098 }
00099 }
00100
00102
00103 QVPlot::QVPlot(const QString name, bool decorations, bool havePie, bool brush, bool _autoShow, bool time, int step, QWidget *parent): QwtPlot(parent),
00104 QVPropertyContainer(name), byTime(time), iterationIndex(1), decorations(decorations), hPie(havePie), doBrush(brush), nStep(step), initied(false),
00105 autoShow(_autoShow), timer(0), haveCurves(FALSE), activeWorkers(0), dataCount(0), usedColors(0)
00106 {
00107 qDebug() << "QVPlot::QVPlot()";
00108 if (qvApp == NULL)
00109 {
00110 QString str = "QVPlot::QVPlot(): the QVPlot cannot be created before the QVApplication instance. Aborting now.";
00111 std::cerr << qPrintable(str) << std::endl;
00112 exit(1);
00113 }
00114
00115 if (qvApp->forHelp()) return;
00116 else QwtPlot(parent);
00117
00118
00119 setWindowTitle("Plot for " + getName());
00120 resize(400,200);
00121 setAutoReplot(false);
00122
00123 for ( int i = 0; i < MAX_HISTORY; i++ )
00124 timeData[MAX_HISTORY - 1 - i] = i;
00125
00126 plotLayout()->setAlignCanvasToScales(true);
00127
00128 if (decorations)
00129 {
00130 QwtLegend *legend = new QwtLegend;
00131 legend->setItemMode(QwtLegend::CheckableItem);
00132 insertLegend(legend, QwtPlot::RightLegend);
00133 }
00134 else
00135 {
00136 enableAxis(0,false);
00137 enableAxis(1,false);
00138 enableAxis(2,false);
00139 }
00140
00141 class TimeScaleDraw: public QwtScaleDraw
00142 {
00143 public:
00144 TimeScaleDraw(const int stp): baseTime(), step(stp) { }
00145 virtual QwtText label(double v) const { return baseTime.addSecs( (int)((v-MAX_HISTORY)*step/100)).toString(); }
00146 private:
00147 QTime baseTime;
00148 int step;
00149 };
00150 class IterScaleDraw: public QwtScaleDraw
00151 {
00152 public:
00153 IterScaleDraw(const int stp): step(stp) { }
00154 virtual QwtText label(double v) const { return QString::number((int)(v-MAX_HISTORY)*step); }
00155 private:
00156 int step;
00157 };
00158 if (byTime) setAxisScaleDraw(QwtPlot::xBottom, new TimeScaleDraw(nStep));
00159 else setAxisScaleDraw(QwtPlot::xBottom, new IterScaleDraw(nStep));
00160
00161 setAxisScale(QwtPlot::xBottom, 0, MAX_HISTORY);
00162 setAxisLabelRotation(QwtPlot::xBottom, -50.0);
00163 setAxisLabelAlignment(QwtPlot::xBottom, Qt::AlignLeft | Qt::AlignBottom);
00164
00165
00166
00167
00168
00169
00170
00171
00172 QwtScaleWidget *scaleWidget = axisWidget(QwtPlot::xBottom);
00173 const int fmh = QFontMetrics(scaleWidget->font()).height();
00174 scaleWidget->setMinBorderDist(0, fmh / 2);
00175
00176 setAxisTitle(QwtPlot::yLeft, "Value");
00177 if (byTime) setAxisTitle(QwtPlot::xBottom, "Time");
00178 else setAxisTitle(QwtPlot::xBottom, "Iterations");
00179 }
00180
00181 QVPlot::~QVPlot()
00182 {
00183 stop();
00185 }
00186
00187 bool QVPlot::linkProperty(QVWorker &worker, const QString propertyName)
00188 {
00189 if (initied)
00190 {
00191 std::cerr << "Warning: a worker can't be linked before plot was initied." << std::endl;
00192 return false;
00193 }
00194
00195 bool newWorker = false;
00196 if (!pcl_Workers.contains(&worker))
00197 {
00198 pcl_Workers << &worker;
00199 if ( (!byTime) && (!QVPropertyContainer::areSynchronized(pcl_Workers)) )
00200 {
00201 pcl_Workers.removeLast();
00202 std::cerr << "Warning: linked property's worker must be synchronized with previous plot's linked workers." << std::endl;
00203 return false;
00204 }
00205 activeWorkers++;
00206 newWorker = true;
00207 }
00208
00209 QString myPropertyName = QString("%1").arg(worker.getId()) + ": " + propertyName;
00210 addPropertyFromQVariant(myPropertyName, inputFlag, worker.getPropertyQVariantValue(propertyName), worker.getPropertyInfo(propertyName));
00211 bool result = worker.linkProperty(propertyName, this, myPropertyName, QVWorker::AsynchronousLink);
00212
00213 if (newWorker)
00214 {
00215 linkCont << LinkedContainer(worker.getId());
00216
00217
00218 QObject::connect(&worker, SIGNAL(endIteration(uint, int)), this, SLOT(update(uint, int)), Qt::BlockingQueuedConnection);
00219 QObject::connect(&worker, SIGNAL(statusUpdate(QVWorker::TWorkerStatus)), this, SLOT(workerChange(QVWorker::TWorkerStatus)));
00220 }
00221
00222 for (int i = 0; i < linkCont.size(); i++)
00223 if (linkCont[i].id == worker.getId())
00224 {
00225 linkCont[i].properties.append(Property(myPropertyName));
00226 break;
00227 }
00228
00229 return result;
00230 }
00231
00232 bool QVPlot::unlink(QVWorker *worker, const QString propertyName)
00233 {
00234 if (initied)
00235 {
00236 std::cerr << "Warning: a worker can't be linked before plot was initied." << std::endl;
00237 return false;
00238 }
00239
00240 QString myPropertyName = QString("%1").arg(worker->getId()) + ": " + propertyName;
00241 if (worker->unlinkProperty(propertyName, this, myPropertyName))
00242 {
00243 for (int i = 0; i < linkCont.size(); i++)
00244 if (linkCont[i].id == worker->getId())
00245 {
00246 int pos = -1;
00247 for (int j = 0; j < linkCont[i].properties.size(); j++)
00248 if (linkCont[i].properties[j].name == myPropertyName)
00249 pos = j;
00250
00251 if (pos >= 0)
00252 {
00253 linkCont[i].properties.removeAt(pos);
00254 }
00255
00256 if (linkCont[i].properties.size() == 0)
00257 {
00258 linkCont.removeAt(i);
00259 pcl_Workers.removeAll(worker);
00260 activeWorkers--;
00261 QObject::disconnect(worker, SIGNAL(endIteration(uint, int)), this, SLOT(update(uint, int)));
00262 QObject::disconnect(worker, SIGNAL(statusUpdate(QVWorker::TWorkerStatus)), this, SLOT(workerChange(QVWorker::TWorkerStatus)));
00263 }
00264 break;
00265 }
00266 removeProperty(myPropertyName);
00267 return true;
00268 }
00269 return false;
00270 }
00271
00272 void QVPlot::init()
00273 {
00274 if (initied)
00275 {
00276 std::cerr << "Warning: a plot can't be initied more than one time." << std::endl;
00277 return;
00278 }
00279
00280 if (hPie)
00281 {
00282 pie = new PieMarker(this);
00283 pie->attach(this);
00284 }
00285
00286 readInputProperties();
00287 for(int i = 0; i < linkCont.size(); i++)
00288 for(int j = 0; j < linkCont[i].properties.size(); j++)
00289 {
00290 const QStringList curvNames = getPropertyCurvNames(linkCont[i].properties[j].name);
00291
00292 for(int k = curvNames.size()-1; k >= 0; k--)
00293 {
00294 QString finalName = curvNames.at(k) + QString(" (%1)").arg(linkCont[i].id);
00295
00296 QwtPlotCurve * qwtpc = new QwtPlotCurve(finalName);
00297 qwtpc->setRenderHint(QwtPlotItem::RenderAntialiased);
00298 QColor color = nextColor();
00299 qwtpc->setPen(color);
00300 if (doBrush) qwtpc->setBrush(color);
00301 qwtpc->setVisible(true);
00302 qwtpc->attach(this);
00303
00304 if (byTime) linkCont[i].properties[j].curves.prepend(Curve(finalName, qwtpc, 1));
00305 else linkCont[i].properties[j].curves.prepend(Curve(finalName, qwtpc, linkCont.size()+1));
00306 haveCurves = TRUE;
00307 }
00308
00309
00310 updateLegendItems();
00311 }
00312
00313 if (byTime) timer = startTimer(nStep * 10);
00314
00315 initied = true;
00316 }
00317
00318 void QVPlot::stop()
00319 {
00320 if (byTime && initied) killTimer(timer);
00321 initied = false;
00322 }
00323
00324 QColor QVPlot::nextColor()
00325 {
00326 QColor color = colors[usedColors % 10];
00327 usedColors++;
00328 return color;
00329 }
00330
00331 void QVPlot::timerEvent(QTimerEvent *)
00332 {
00333 if (!initied)
00334 {
00335 std::cerr << "Warning: a plot can't be advanced before it was initied." << std::endl;
00336 return;
00337 }
00338
00339
00340 if (haveCurves) advancePlot();
00341 for (int i = 0; i < linkCont.size(); i++) linkCont[i].meanItems = 0;
00342 }
00343
00344 void QVPlot::workerChange(QVWorker::TWorkerStatus status)
00345 {
00346 if ( (status == QVWorker::Finished) && (activeWorkers > 0) ) activeWorkers--;
00347 }
00348
00349 void QVPlot::update(uint id, int)
00350 {
00351
00352 if (!initied)
00353 {
00354 std::cerr << "Warning: a plot can't be advanced before it was initied." << std::endl;
00355 return;
00356 }
00357
00358 if (byTime)
00359 {
00360 readInputProperties();
00361 for(int i = 0; i < linkCont.size(); i++)
00362 if (linkCont[i].id == id)
00363 {
00364 for(int j = 0; j < linkCont[i].properties.size(); j++)
00365 {
00366 insertNewFlags(i, j);
00367
00368 const QList<double> values = getPropertyCurvValues(linkCont[i].properties[j].name);
00369 if (values.size() != linkCont[i].properties[j].curves.size())
00370 {
00371 std::cerr << "QVPlot internal error: flags insert." << std::endl;
00372 return;
00373 }
00374
00375 for (int k = 0; k < linkCont[i].properties[j].curves.size(); k++)
00376 if (linkCont[i].meanItems < 1)
00377 linkCont[i].properties[j].curves[k].temp[0] = values[k];
00378 else
00379 linkCont[i].properties[j].curves[k].temp[0] =
00380 (linkCont[i].properties[j].curves[k].temp[0]*linkCont[i].meanItems+values[k]) / (linkCont[i].meanItems + 1);
00381 linkCont[i].meanItems++;
00382 }
00383 break;
00384 }
00385 }
00386 else
00387 {
00388 readInputProperties();
00389 for(int i = 0; i < linkCont.size(); i++)
00390 if (linkCont[i].id == id)
00391 {
00392 linkCont[i].iter++;
00393 for(int j = 0; j < linkCont[i].properties.size(); j++)
00394 {
00395 insertNewFlags(i, j);
00396
00397 const QList<double> values = getPropertyCurvValues(linkCont[i].properties[j].name);
00398 if (values.size() != linkCont[i].properties[j].curves.size())
00399 {
00400 std::cerr << "QVPlot internal error: flags insert." << std::endl;
00401 return;
00402 }
00403
00404
00405 for (int k = 0; k < linkCont[i].properties[j].curves.size(); k++)
00406 linkCont[i].properties[j].curves[k].temp[(linkCont[i].iter % (linkCont.size()+1))] += values[k];
00407 }
00408 break;
00409 }
00410
00411 int updateWorkers = 0;
00412 for (int i = 0; i < linkCont.size(); i++)
00413 if (linkCont[i].iter >= iterationIndex) updateWorkers++;
00414
00415
00416 if (updateWorkers >= activeWorkers)
00417 {
00418
00419 if ((iterationIndex % nStep) == 0)
00420 {
00421
00422 for(int i = 0; i < linkCont.size(); i++)
00423 {
00424 linkCont[i].meanItems++;
00425 for(int j = 0; j < linkCont[i].properties.size(); j++)
00426 for (int k = 0; k < linkCont[i].properties[j].curves.size(); k++)
00427 linkCont[i].properties[j].curves[k].temp[(iterationIndex % (linkCont.size()+1))] /= linkCont[i].meanItems;
00428 linkCont[i].meanItems = 0;
00429 }
00430
00431
00432 if (haveCurves) advancePlot();
00433 }
00434 else
00435 {
00436 for(int i = 0; i < linkCont.size(); i++)
00437 {
00438 linkCont[i].meanItems++;
00439 for(int j = 0; j < linkCont[i].properties.size(); j++)
00440 for (int k = 0; k < linkCont[i].properties[j].curves.size(); k++)
00441 linkCont[i].properties[j].curves[k].temp[((iterationIndex+1) % (linkCont.size()+1))] +=
00442 linkCont[i].properties[j].curves[k].temp[(iterationIndex % (linkCont.size()+1))];
00443 }
00444 }
00445
00446 for(int i = 0; i < linkCont.size(); i++)
00447 for(int j = 0; j < linkCont[i].properties.size(); j++)
00448 for (int k = 0; k < linkCont[i].properties[j].curves.size(); k++)
00449 linkCont[i].properties[j].curves[k].temp[(iterationIndex % (linkCont.size()+1))] = 0;
00450 iterationIndex++;
00451 }
00452 }
00453 }
00454
00455 void QVPlot::insertNewFlags(int cont, int prop)
00456 {
00457 const QStringList curvNames = getPropertyCurvNames(linkCont[cont].properties[prop].name);
00458 if ( (linkCont.size() > cont) && (linkCont[cont].properties.size() > prop) && (curvNames.size() > linkCont[cont].properties[prop].curves.size()) )
00459 {
00460 const QList<int> curvOrders = getPropertyCurvOrders(linkCont[cont].properties[prop].name);
00461 for (int i = 0; i < curvOrders.size(); i++)
00462 if (curvOrders.at(i) > linkCont[cont].properties[prop].curves.size())
00463 {
00464 QString finalName = curvNames.at(i) + QString(" (%1)").arg(linkCont[cont].id);
00465
00466 QwtPlotCurve * qwtpc = new QwtPlotCurve(finalName);
00467 qwtpc->setRenderHint(QwtPlotItem::RenderAntialiased);
00468 QColor color = nextColor();
00469 qwtpc->setPen(color);
00470 if (doBrush) qwtpc->setBrush(color);
00471 qwtpc->setVisible(true);
00472
00473 if (byTime) linkCont[cont].properties[prop].curves.insert(i, Curve(finalName, qwtpc, 1));
00474 else linkCont[cont].properties[prop].curves.insert(i, Curve(finalName, qwtpc, linkCont.size()+1));
00475 haveCurves = TRUE;
00476 }
00477
00478 for(int i = linkCont.size()-1; i >= 0; i--)
00479 for(int j = linkCont[i].properties.size()-1; j >= 0; j--)
00480 for (int k = linkCont[i].properties[j].curves.size()-1; k >= 0; k--)
00481 {
00482 linkCont[i].properties[j].curves[k].plot->detach();
00483 linkCont[i].properties[j].curves[k].plot->attach(this);
00484 }
00485
00486
00487 updateLegendItems();
00488 }
00489 }
00490
00491 void QVPlot::advancePlot()
00492 {
00493 if (!haveCurves)
00494 {
00495 std::cerr << "QVPlot internal error: early call to advancePlot." << std::endl;
00496 return;
00497 }
00498
00499
00500 if ( dataCount < MAX_HISTORY ) dataCount++;
00501 for ( int j = 0; j < MAX_HISTORY; j++ ) timeData[j]++;
00502
00503 double max = 0;
00504 double brush[MAX_HISTORY]; for (int i = 0; i < MAX_HISTORY; i++) brush[i] = 0;
00505
00506
00507 for(int i = 0; i < linkCont.size(); i++)
00508 for(int j = 0; j < linkCont[i].properties.size(); j++)
00509 for(int k = 0; k < linkCont[i].properties[j].curves.size(); k++) {
00510
00511 for (int l = MAX_HISTORY-1; l > 0; l--)
00512 linkCont[i].properties[j].curves[k].history[l] = linkCont[i].properties[j].curves[k].history[l-1];
00513
00514
00515 if (byTime) linkCont[i].properties[j].curves[k].history[0] = linkCont[i].properties[j].curves[k].temp[0];
00516 else linkCont[i].properties[j].curves[k].history[0] = linkCont[i].properties[j].curves[k].temp[(iterationIndex%(linkCont.size()+1))];
00517
00518
00519 if (linkCont[i].properties[j].curves[k].plot->style() != QwtPlotCurve::NoCurve) {
00520 if (doBrush) {
00521 for (int l = 0; l < MAX_HISTORY; l++) {
00522 brush[l] += linkCont[i].properties[j].curves[k].history[l];
00523 linkCont[i].properties[j].curves[k].brushHistory[l] = brush[l];
00524 if (max < linkCont[i].properties[j].curves[k].brushHistory[l]) max = linkCont[i].properties[j].curves[k].brushHistory[l];
00525 }
00526 linkCont[i].properties[j].curves[k].plot->setRawData(timeData, linkCont[i].properties[j].curves[k].brushHistory, dataCount);
00527 }
00528 else {
00529 for (int l = 0; l < MAX_HISTORY; l++) {
00530 if (max < linkCont[i].properties[j].curves[k].history[l]) max = linkCont[i].properties[j].curves[k].history[l];
00531 }
00532 linkCont[i].properties[j].curves[k].plot->setRawData(timeData, linkCont[i].properties[j].curves[k].history, dataCount);
00533 }
00534 }
00535 }
00536 max = 1.1 *max;
00537
00538 setAxisScale(QwtPlot::yLeft, 0, max);
00539 setAxisScale(QwtPlot::xBottom, timeData[MAX_HISTORY - 1], timeData[0]);
00540
00541
00542 replot();
00543 }
00544
00545
00546
00547
00548
00549
00550
00551
00552
00553
00554 void QVPlot::updateLegendItems()
00555 {
00556 if (decorations)
00557 foreach(QWidget *widgetItem, legend()->legendItems())
00558 {
00559 QwtLegendItem *legendItem;
00560 if((legendItem = dynamic_cast<QwtLegendItem*>(widgetItem)) != NULL)
00561 QObject::connect(legendItem, SIGNAL(checked(bool)), this, SLOT(legendItemCheked(bool)));
00562 }
00563 }
00564
00565 void QVPlot::legendItemCheked(bool)
00566 {
00567 if (decorations)
00568 foreach(QWidget *widgetItem, legend()->legendItems())
00569 {
00570 QwtLegendItem *legendItem;
00571 if ((legendItem = dynamic_cast<QwtLegendItem*>(widgetItem)) != NULL)
00572 if (legendItem->isChecked())
00573 hideStat(legendItem->text().text());
00574 else
00575 showStat(legendItem->text().text());
00576 }
00577 }
00578
00579 void QVPlot::hideStat(const QString labelText)
00580 {
00581 for(int i = 0; i < linkCont.size(); i++)
00582 for(int j = 0; j < linkCont[i].properties.size(); j++)
00583 for(int k = 0; k < linkCont[i].properties[j].curves.size(); k++)
00584 if (linkCont[i].properties[j].curves[k].name == labelText) {
00585 linkCont[i].properties[j].curves[k].plot->setStyle(QwtPlotCurve::NoCurve);
00586 return;
00587 }
00588 }
00589
00590 void QVPlot::showStat(const QString labelText)
00591 {
00592 for(int i = 0; i < linkCont.size(); i++)
00593 for(int j = 0; j < linkCont[i].properties.size(); j++)
00594 for(int k = 0; k < linkCont[i].properties[j].curves.size(); k++)
00595 if (linkCont[i].properties[j].curves[k].name == labelText)
00596 {
00597 linkCont[i].properties[j].curves[k].plot->setStyle(QwtPlotCurve::Lines);
00598 return;
00599 }
00600 }