Main Page | Namespace List | Class Hierarchy | Class List | Directories | File List | Class Members | File Members

MonitorDataPlotter.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002  *   SpikeStream Application                                               *
00003  *   Copyright (C) 2007 by David Gamez                                     *
00004  *   david@davidgamez.eu                                                   *
00005  *   Version 0.1                                                           *
00006  *                                                                         *
00007  *   This program is free software; you can redistribute it and/or modify  *
00008  *   it under the terms of the GNU General Public License as published by  *
00009  *   the Free Software Foundation; either version 2 of the License, or     *
00010  *   (at your option) any later version.                                   *
00011  *                                                                         *
00012  *   This program 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 General Public License for more details.                          *
00016  *                                                                         *
00017  *   You should have received a copy of the GNU General Public License     *
00018  *   along with this program; if not, write to the                         *
00019  *   Free Software Foundation, Inc.,                                       *
00020  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
00021  ***************************************************************************/
00022 
00023 //SpikeStream includes
00024 #include "MonitorDataPlotter.h"
00025 #include "Debug.h"
00026 #include "MonitorXmlHandler.h"
00027 #include "SpikeStreamMainWindow.h"
00028 #include "SimulationWidget.h"
00029 
00030 //Qt includes
00031 #include <qlayout.h>
00032 #include <qxml.h>
00033 #include <qmessagebox.h>
00034 #include <qcolor.h>
00035 #include <qpainter.h>
00036 
00037 //Qwt includes
00038 #include <qwt_plot_curve.h>
00039 #include <qwt_plot_layout.h>
00040 
00041 //Other includes
00042 #include <iostream>
00043 using namespace std;
00044 
00045 
00046 /*! Default number of items in dataset. This controls the resolution of the X axis in the plot. */
00047 #define DEFAULT_MONITOR_DATASET_SIZE 100
00048 
00049 
00050 /*! Class that draws the background of the plot - easier to put it here than a separate file. */
00051 class Background: public QwtPlotItem {
00052         public:
00053                 Background(){
00054                         setZ(0.0);
00055                         QColor colour(255, 255, 255);
00056                         brush = new QBrush(colour, Qt::SolidPattern);
00057                 }
00058                 ~Background(){
00059                         delete brush;
00060                 }
00061 
00062                 virtual int rtti() const {
00063                         return QwtPlotItem::Rtti_PlotUserItem;
00064                 }
00065 
00066                 virtual void draw(QPainter *painter, const QwtScaleMap&, const QwtScaleMap&, const QRect &rect) const {
00067                         painter->fillRect(rect, *brush);
00068                 }
00069         private:
00070                 QBrush* brush;
00071 };
00072 
00073 
00074 /*! Constructor for monitoring neuron data
00075         The main initialisation work is done when the setUpGraphs method is called after the information
00076         about the monitored data has been received by the simulationManager. */
00077 MonitorDataPlotter::MonitorDataPlotter(QWidget *parent, QString neuronGrpDesc, unsigned int neurGrpID, unsigned int neurID) : QDialog(parent, "NeurMonDlg", false){
00078         neuronMonitoring = true;
00079         
00080         //Store neuron ID and group
00081         neuronID = neurID;
00082         neuronGrpID = neurGrpID;
00083 
00084         //Store parent class as a widget to avoid include problems
00085         simulationWidget = parent;
00086 
00087         //Set caption
00088         QString captionStr = "Monitoring neuron ";
00089         captionStr += QString::number(neurID) += " in neuron group ";
00090         captionStr += neuronGrpDesc;
00091         this->setCaption(captionStr);
00092 
00093         //Create box to organise dialog
00094         verticalBox = new QVBoxLayout(this, 2, 2);
00095 
00096         //Add temporary label explaining that we are waiting for neuron information.
00097         loadingLabel = new QLabel("Loading graphs, please wait.", this);
00098         verticalBox->addWidget(loadingLabel);
00099 
00100         //Create font for axes
00101         axisFont = new QFont( "Arial", 8);
00102         axisTitleFont = new QFont("Arial", 9, QFont::Bold);
00103 
00104         //Initialise variables
00105         parseError = false;
00106 
00107         this->show();
00108 }
00109 
00110 
00111 /*! Constructor for monitoring synapse data
00112         The main initialisation work is done when the setUpGraphs method is called after the information
00113         about the monitored data has been received by the simulationManager. */
00114 MonitorDataPlotter::MonitorDataPlotter(QWidget *parent, unsigned int neurGrpID, unsigned int fromNeurID, unsigned int toNeurID) : QDialog(parent, "MonDlg", false){
00115         neuronMonitoring = false;
00116 
00117         //Store neuron ID and group
00118         fromNeuronID = fromNeurID;
00119         toNeuronID = toNeurID;
00120         neuronGrpID = neurGrpID;
00121 
00122         //Store parent class as a widget to avoid include problems
00123         simulationWidget = parent;
00124 
00125         //Set caption
00126         QString captionStr = "Monitoring synapse from ";
00127         captionStr += QString::number(fromNeuronID) += " to ";
00128         captionStr += QString::number(toNeuronID) += " in neuron group ";
00129         captionStr += QString::number(neuronGrpID);
00130         this->setCaption(captionStr);
00131 
00132         //Create box to organise dialog
00133         verticalBox = new QVBoxLayout(this, 2, 2);
00134 
00135         //Add temporary label explaining that we are waiting for neuron information.
00136         loadingLabel = new QLabel("Loading graphs, please wait.", this);
00137         verticalBox->addWidget(loadingLabel);
00138 
00139         //Create font for axes
00140         axisFont = new QFont( "Arial", 8);
00141         axisTitleFont = new QFont("Arial", 9, QFont::Bold);
00142 
00143         //Initialise variables
00144         parseError = false;
00145 
00146         this->show();
00147 }
00148 
00149 
00150 /*! Destructor. */
00151 MonitorDataPlotter::~MonitorDataPlotter(){
00152         #ifdef MEMORY_DEBUG
00153                 cout<<"DESTROYING MONITOR DATA PLOTTER"<<endl;
00154         #endif//MEMORY_DEBUG
00155 
00156         //Clean up plots. These should destroy their copies of the data structures
00157         for(vector<QwtPlot*>::iterator iter = plotVector.begin(); iter != plotVector.end(); ++iter)
00158                 delete *iter;
00159 
00160         //Clean up data structures
00161         for(vector<MonitorDataset*>::iterator iter = dataVector.begin(); iter != dataVector.end(); ++iter){
00162                 (*iter)->cleanUp();
00163                 delete *iter;
00164         }
00165 }
00166 
00167 
00168 //--------------------------------------------------------------------------------
00169 //----------------------      PUBLIC METHODS       -------------------------------
00170 //--------------------------------------------------------------------------------
00171 
00172 /*! Adds a graph to the display. Usually called by MonitorXmlHandler, which in turn is invoked 
00173         by simulationManager running as a separate thread. */
00174 void MonitorDataPlotter::addGraph(NewGraph newGraph){
00175         #ifdef GRAPH_LOAD_DEBUG
00176                 cout<<"MonitorDataPlotter: Adding graph: "<<newGraph.description<<" range from "<<newGraph.rangeLow<<" to "<<newGraph.rangeHigh<<endl;
00177         #endif//GRAPH_LOAD_DEBUG
00178 
00179         //Lock mutex because this method is invoked by a separate thread
00180         SpikeStreamMainWindow::spikeStreamApplication->lock();
00181 
00182         //Create a plot
00183         QwtPlot* tempPlot = new QwtPlot(this, "Temp Curve");
00184         tempPlot->setMinimumSize(120, 120);
00185 
00186         //Set font of axes
00187         tempPlot->setAxisFont(QwtPlot::xBottom, *axisFont);
00188         tempPlot->setAxisFont(QwtPlot::yLeft, *axisFont);
00189 
00190         //Set up scale on Y axis to prevent replot. Scale on X axis will be set when adding points
00191         tempPlot->setAutoReplot(false);
00192 
00193         //Add to widget
00194         verticalBox->addWidget(tempPlot);
00195 
00196         //Set titles on the axes
00197         QwtText yText(newGraph.description);
00198         yText.setFont(*axisTitleFont);
00199     tempPlot->setAxisTitle(QwtPlot::yLeft, yText);
00200 
00201         QwtText xText("Time (ms)");
00202         xText.setFont(*axisTitleFont);
00203         tempPlot->setAxisTitle(QwtPlot::xBottom, xText);
00204 
00205         //Add background
00206     Background *bg = new Background();
00207     bg->attach(tempPlot);
00208 
00209         //Create a curve for the plot
00210         QwtPlotCurve *tempCurve = new QwtPlotCurve("Curve 1");
00211         QColor redColor(255, 0, 0);
00212         QBrush tmpBrush(redColor, Qt::Dense4Pattern);
00213     tempCurve->setBrush(tmpBrush);
00214         QPen tmpPen(redColor);
00215         tempCurve->setPen(tmpPen);
00216 
00217         //Create a new dataset to manage the data for this graph
00218         MonitorDataset* tempData = new MonitorDataset(DEFAULT_MONITOR_DATASET_SIZE, newGraph.rangeLow, newGraph.rangeHigh);
00219 
00220         //Pass a reference to the dataset to the curve
00221         tempCurve->setData(*tempData);
00222         
00223         //Attach the curve to the plot
00224         tempCurve->attach(tempPlot);
00225         
00226         //Refresh the plot
00227         tempPlot->replot();
00228 
00229         //Make plot visible.
00230         tempPlot->show();
00231 
00232         //Store reference to new dataset
00233         dataVector.push_back(tempData);
00234 
00235         //Store reference to new plot
00236         plotVector.push_back(tempPlot);
00237 
00238         //Unlock mutex
00239         SpikeStreamMainWindow::spikeStreamApplication->unlock();
00240 }
00241 
00242 
00243 /*! Called when the dialog needs to be closed by an external class.
00244         Variable stopMon determines whether monitoring will be stopped when dialog is closed. */
00245 void MonitorDataPlotter::closeDialog(bool stopMon){
00246 
00247         //Stop the monitoring
00248         if(stopMon)
00249                 stopMonitoring();
00250 
00251         //Accept the closure of the dialog
00252         this->accept();
00253 }
00254 
00255 
00256 /*! Called when all graphs have been loaded so that loading label can be hidden. */
00257 void MonitorDataPlotter::loadingComplete(){
00258         #ifdef GRAPH_LOAD_DEBUG
00259                 cout<<"MonitorDataPlotter: Graph loading complete."<<endl;
00260         #endif//GRAPH_LOAD_DEBUG
00261 
00262         loadingLabel->hide();
00263 }
00264 
00265 
00266 /*! Adds the data in the float array to the graphs. */
00267 void MonitorDataPlotter::plotData(double time, const double* dataArray, int arrayLength){
00268         //Lock mutex because this method is invoked by a separate thread
00269         SpikeStreamMainWindow::spikeStreamApplication->lock();
00270 
00271         //Double check that arrayLength is the same as the vector lengths
00272         if((unsigned int)arrayLength != plotVector.size()){
00273                 QMessageBox::critical(this, "Monitor Error", "Data and graphs do not match!");
00274                 return;
00275         }
00276 
00277         //Add the data to the plots and replot the graph
00278         for(int i=0; i<arrayLength; ++i){
00279                 dataVector[i]->addPoint(time, dataArray[i]);
00280 
00281                 //Adjust the X axis to fit
00282                 QwtDoubleRect* bndRect = dataVector[i]->boundingRectRef();
00283                 plotVector[i]->setAxisScale(QwtPlot::xBottom, bndRect->left(), bndRect->left() + bndRect->width());
00284 
00285                 //Replot graph
00286                 plotVector[i]->replot();
00287         }
00288 
00289         //Unlock mutex
00290         SpikeStreamMainWindow::spikeStreamApplication->unlock();
00291 }       
00292 
00293 
00294 /*! Used by other methods to make the dialog visible and restart the monitoring. */
00295 bool MonitorDataPlotter::showDialog(){
00296         if(parseError){//Dialog has already been launched and generated a parsing error
00297                 QMessageBox::critical( 0, "Monitor Error", "Dialog cannot be shown if it has generated a parsing error");
00298                 return false;
00299         }
00300 
00301         bool restartOk = true;
00302 
00303         //Lock mutex in case this method is invoked by a separate thread
00304         SpikeStreamMainWindow::spikeStreamApplication->lock();
00305 
00306         //Tell simulationManager to start monitoring neuron or synapse
00307         if(neuronMonitoring){
00308                 if(!((SimulationWidget*)simulationWidget)->getSimulationManager()->startNeuronMonitoring(neuronGrpID, neuronID, true)){
00309                         cerr<<"MonitorDataPlotter: ERROR RESTARTING NEURON MONITORING FOR NEURON GROUP "<<neuronGrpID<<" AND NEURON "<<neuronID<<endl;
00310                         restartOk = false;
00311                 }
00312         }
00313         else{
00314                 if(!((SimulationWidget*)simulationWidget)->getSimulationManager()->startSynapseMonitoring(neuronGrpID, fromNeuronID, toNeuronID, true)){
00315                         cerr<<"MonitorDataPlotter: ERROR RESTARTING SYNAPSE MONITORING FOR NEURON GROUP "<<neuronGrpID<<" FROM NEURON "<<fromNeuronID<<" TO "<<toNeuronID<<endl;
00316                         restartOk = false;
00317                 }
00318         }
00319 
00320         //Unlock mutex
00321         SpikeStreamMainWindow::spikeStreamApplication->unlock();
00322 
00323         //Show dialog
00324         this->show();
00325 
00326         //Return false if there has been an error resetarting.
00327         return restartOk;
00328 }
00329 
00330 
00331 /*! The char array is an XML file containing the set up information for the graphs.
00332         This method parses the information and creates the graphs. */
00333 void MonitorDataPlotter::setUpGraphs(const char* charArray){
00334         //Lock mutex in case this method is invoked by a separate thread
00335         SpikeStreamMainWindow::spikeStreamApplication->lock();
00336 
00337         #ifdef GRAPH_LOAD_DEBUG
00338                 cout<<"MonitorDataPlotter: Parsing XML: "<<charArray<<endl;
00339         #endif//GRAPH_LOAD_DEBUG
00340 
00341         QString xmlString(charArray);
00342         try{
00343                 QXmlInputSource xmlInput;
00344                 xmlInput.setData(xmlString);
00345                 MonitorXmlHandler monXmlHandler(this);
00346                 QXmlSimpleReader reader;
00347                 reader.setContentHandler(&monXmlHandler);
00348                 reader.setErrorHandler(&monXmlHandler);
00349                 reader.parse(xmlInput);
00350                 if(monXmlHandler.getParseError()){
00351                         //Display error
00352                         cout<<"MonitorDataPlotter: ERROR OCCURRED DURING PARSING \""<<monXmlHandler.getParseErrorString()<<"\""<<endl;
00353                         QString errorString = "Error encountered whilst parsing XML monitor description: \"";
00354                         errorString += monXmlHandler.getParseErrorString();
00355                         errorString += "\"";
00356                         QMessageBox::critical( 0, "Monitor Error", errorString);
00357 
00358                         //Record that we have an error
00359                         parseError = true;
00360 
00361                         //Stop moinitoring
00362                         stopMonitoring();
00363                 
00364                         //Accept the closure of the dialog
00365                         this->accept();
00366                 }
00367         }
00368         catch (std::exception& er) {// Catch-all for any other exceptions
00369                 //Display error
00370                 cerr<<"MonitorDataPlotter: EXCEPTION \""<<er.what()<<"\""<<endl;
00371                 QString errorString = "Exception thrown parsing XML monitor description: \"";
00372                 errorString += er.what();
00373                 errorString += "\"";
00374                 QMessageBox::critical( 0, "Monitor Error", errorString);
00375 
00376                 //Record that we have an error
00377                 parseError = true;
00378 
00379                 //Stop moinitoring
00380                 stopMonitoring();
00381         
00382                 //Accept the closure of the dialog
00383                 this->accept();
00384         }
00385 
00386         //Unlock mutex
00387         SpikeStreamMainWindow::spikeStreamApplication->unlock();
00388 
00389 }
00390 
00391 
00392 //--------------------------------------------------------------------------------
00393 //----------------------      PROTECTED METHODS       ----------------------------
00394 //--------------------------------------------------------------------------------
00395 
00396 /*! Called when user closes the dialog. Instructs simulation to stop sending monitor data. 
00397         Dialog is closed programmatically by the closeDialog() method. */
00398 void MonitorDataPlotter::closeEvent ( QCloseEvent*){
00399 
00400         //Stop moinitoring
00401         stopMonitoring();
00402 
00403         //Accept the closure of the dialog
00404         this->accept();
00405 }
00406 
00407 
00408 //--------------------------------------------------------------------------------
00409 //----------------------      PRIVATE  METHODS       -----------------------------
00410 //--------------------------------------------------------------------------------
00411 
00412 /*! Stops the monitoring associated with this dialog. */
00413 void MonitorDataPlotter::stopMonitoring(){
00414 
00415         //Lock mutex because this method is invoked by a separate thread
00416         SpikeStreamMainWindow::spikeStreamApplication->lock();
00417 
00418         //Tell simulationManager to stop monitoring neuron or synapse
00419         if(neuronMonitoring)
00420                 ((SimulationWidget*)simulationWidget)->getSimulationManager()->stopNeuronMonitoring(neuronGrpID, neuronID);
00421         else
00422                 ((SimulationWidget*)simulationWidget)->getSimulationManager()->stopSynapseMonitoring(neuronGrpID, fromNeuronID, toNeuronID);
00423 
00424         //Unlock mutex
00425         SpikeStreamMainWindow::spikeStreamApplication->unlock();
00426 }
00427 
00428 

Generated on Mon Sep 3 22:29:04 2007 for SpikeStream Application by  doxygen 1.4.4