src/qvgui/qvplot.cpp

Go to the documentation of this file.
00001 /*
00002  *      Copyright (C) 2007. PARP Research Group.
00003  *      <http://perception.inf.um.es>
00004  *      University of Murcia, Spain.
00005  *
00006  *      This file is part of the QVision library.
00007  *
00008  *      QVision is free software: you can redistribute it and/or modify
00009  *      it under the terms of the GNU Lesser General Public License as
00010  *      published by the Free Software Foundation, version 3 of the License.
00011  *
00012  *      QVision 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
00018  *      License along with QVision. If not, see <http://www.gnu.org/licenses/>.
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 // auxiliar classes functionality
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         // if its a --help call, do nothing
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         /* In situations, when there is a label at the most right position of the
00167         scale, additional space is needed to display the overlapping part
00168         of the label would be taken by reducing the width of scale and canvas.
00169         To avoid this "jumping canvas" effect, we add a permanent margin.
00170         We don't need to do the same for the left border, because there
00171         is enough space for the overlapping label below the left scale. */
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                 // este tipo de bloqueo pos iteraciones puede retardar al worker
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); // nStep * 10 ms
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         // if there are data of flags, advance the Plot
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         // First must be initied
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                                         // update worker's properties values for its iteration in the "temp" buffer (add the new value)
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                 // If has completed the current iteration column
00358                 if (updateWorkers >= activeWorkers)
00359                         {
00360                         // If has completed nStep iterations
00361                         if ((iterationIndex % nStep) == 0)
00362                                 {
00363                                 // obtain the mean to it
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                                 // Advance the plot
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                         // And reset the column
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         // update data
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--) // scroll the row
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); // and puss the new value
00443                                 }
00444 
00445         // increments the values to scroll the window
00446         if ( dataCount < MAX_HISTORY ) dataCount++;
00447         for ( int j = 0; j < MAX_HISTORY; j++ )
00448                 timeData[j]++;
00449 
00450         // scroll bottom axis
00451         setAxisScale(QwtPlot::xBottom, timeData[MAX_HISTORY - 1], timeData[0]);
00452 
00453         // curve update
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         // adjust left axis (using the visible values's maximum)
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         // and replot
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 

Generated on Thu Jul 17 17:23:28 2008 for QVision by  doxygen 1.5.3