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