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