SpikeStream Application Library
0.2
|
00001 //SpikeStream includes 00002 #include "Globals.h" 00003 #include "SpikeRasterWidget.h" 00004 #include "SpikeStreamException.h" 00005 using namespace spikestream; 00006 00007 //Qt includes 00008 #include <QFileDialog> 00009 #include <QMessageBox> 00010 #include <QPainter> 00011 #include <QResizeEvent> 00012 #include <QDebug> 00013 00014 00016 SpikeRasterWidget::SpikeRasterWidget(QList<NeuronGroup*>& neuronGroupList, QWidget* parent) : QWidget(parent){ 00017 //Store list of neuron groups that are being monitored 00018 this->neuronGroupList = neuronGroupList; 00019 00020 //Calculate number of neurons along with their offset and colour 00021 QList<unsigned> hueList = getHueList(neuronGroupList.size()); 00022 numNeurons = 0; 00023 int hueCntr = 0; 00024 for(QList<NeuronGroup*>::iterator iter = neuronGroupList.begin(); iter != neuronGroupList.end(); ++iter){ 00025 neurGrpOffsetMap[(*iter)->getID()] = numNeurons; 00026 neurGrpColorMap[(*iter)->getID()] = hueList.at(hueCntr); 00027 ++hueCntr; 00028 numNeurons += (*iter)->size(); 00029 } 00030 00031 //Initialize display variables 00032 blackAndWhiteMode = false; 00033 xAxisTickLength = 2; 00034 yAxisTickLength = 2; 00035 fontSize = 10; 00036 neurGrpNameFontSize = 10; 00037 yAxisPadding = yAxisTickLength + 1 + fontSize * QString::number(numNeurons).length(); 00038 xAxisPadding = xAxisTickLength + 1 + fontSize; 00039 backgroundColor = qRgb(255,255,255); 00040 axesColor = qRgb(0,0,0); 00041 00042 //Initialize other variables 00043 numTimeSteps = 1000; 00044 minTimeStep = 0; 00045 00046 //Create buffer image 00047 imageWidth = numTimeSteps + yAxisPadding + 1; 00048 imageHeight = numNeurons + xAxisPadding + 1; 00049 bufferImage = new QImage(imageWidth, imageHeight, QImage::Format_RGB32); 00050 bufferImage->fill(backgroundColor); 00051 updateAxes = true; 00052 00053 //Set up widget graphic properties 00054 if(imageWidth > 500) 00055 setMinimumWidth(500); 00056 else 00057 setMinimumWidth(imageWidth); 00058 if(imageHeight > 500) 00059 setMinimumHeight(500); 00060 else 00061 setMinimumHeight(imageHeight); 00062 //setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); 00063 setAttribute(Qt::WA_OpaquePaintEvent); 00064 setAttribute(Qt::WA_PaintOnScreen); 00065 widgetWidth = this->size().width(); 00066 widgetHeight = this->size().height(); 00067 } 00068 00069 00071 SpikeRasterWidget::~SpikeRasterWidget(){ 00072 delete bufferImage; 00073 } 00074 00075 00076 /*----------------------------------------------------------*/ 00077 /*------ PUBLIC METHODS ------*/ 00078 /*----------------------------------------------------------*/ 00079 00081 void SpikeRasterWidget::addSpikes(const QList<unsigned>& firingNeuronIDs, int timeStep){ 00082 int writeLocation = timeStep % numTimeSteps + yAxisPadding + 1; 00083 00084 //Add spikes to the current image 00085 unsigned tmpNeurID; 00086 QList<unsigned>::const_iterator firingNeuronIDsEnd = firingNeuronIDs.end(); 00087 for(QList<unsigned>::const_iterator neurIter = firingNeuronIDs.begin(); neurIter != firingNeuronIDsEnd; ++neurIter){ 00088 for(QList<NeuronGroup*>::iterator neurGrpIter = neuronGroupList.begin(); neurGrpIter != neuronGroupList.end(); ++neurGrpIter){ 00089 if((*neurGrpIter)->contains(*neurIter)){ 00090 tmpNeurID = *neurIter - (*neurGrpIter)->getStartNeuronID() + neurGrpOffsetMap[(*neurGrpIter)->getID()]; 00091 bufferImage->setPixel(writeLocation, imageHeight - xAxisPadding - tmpNeurID - 1, neurGrpColorMap[(*neurGrpIter)->getID()]); 00092 break; 00093 } 00094 } 00095 } 00096 00097 //Advance the time step 00098 increaseTimeStep(timeStep); 00099 repaint(); 00100 } 00101 00102 00104 void SpikeRasterWidget::setBlackAndWhite(bool on){ 00105 blackAndWhiteMode = on; 00106 00107 //Reload hues 00108 QList<unsigned> hueList = getHueList(neuronGroupList.size()); 00109 int hueCntr = 0; 00110 for(QList<NeuronGroup*>::iterator iter = neuronGroupList.begin(); iter != neuronGroupList.end(); ++iter){ 00111 neurGrpColorMap[(*iter)->getID()] = hueList.at(hueCntr); 00112 ++hueCntr; 00113 } 00114 repaint(); 00115 } 00116 00117 00118 /*----------------------------------------------------------*/ 00119 /*------ PROTECTED METHODS ------*/ 00120 /*----------------------------------------------------------*/ 00121 00123 void SpikeRasterWidget::mouseDoubleClickEvent (QMouseEvent*){ 00124 QString fileType = "BMP"; 00125 QString filePath = getFilePath("*." + fileType.toLower()); 00126 00127 //Fix extension 00128 if(!filePath.endsWith(fileType.toLower())) 00129 filePath += "." + fileType.toLower(); 00130 00131 //If file exists, check to see if user wants to overwrite file 00132 if(QFile::exists(filePath)){ 00133 int response = QMessageBox::warning(this, "Overwrite File?", filePath + " already exists.\nAre you sure that you want to overwrite it?", QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Cancel); 00134 if(response != QMessageBox::Ok) 00135 return; 00136 } 00137 00138 //Save file 00139 bufferImage->save(filePath, fileType.toAscii(), 100); 00140 } 00141 00142 00143 //Inherited from QWidget 00144 void SpikeRasterWidget::paintEvent(QPaintEvent*){ 00145 QPainter painter(this); 00146 if(updateAxes){ 00147 QPainter axesPainter(bufferImage); 00148 paintAxes(axesPainter); 00149 paintNeuronGroupNames(axesPainter); 00150 } 00151 00152 painter.drawImage(0, 0, bufferImage->scaled(widgetWidth, widgetHeight)); 00153 painter.end(); 00154 } 00155 00156 00157 //Inherited from QWidget 00158 void SpikeRasterWidget::resizeEvent(QResizeEvent* /*event*/) { 00159 widgetWidth = this->size().width(); 00160 widgetHeight = this->size().height(); 00161 } 00162 00163 00164 /*----------------------------------------------------------*/ 00165 /*------ PRIVATE METHODS ------*/ 00166 /*----------------------------------------------------------*/ 00167 00169 QString SpikeRasterWidget::getFilePath(QString fileFilter){ 00170 QFileDialog dialog(this); 00171 dialog.setDirectory(Globals::getWorkingDirectory()); 00172 dialog.setFileMode(QFileDialog::AnyFile); 00173 dialog.setNameFilter( QString("Image file (" + fileFilter + ")") ); 00174 dialog.setWindowTitle("Save"); 00175 dialog.setViewMode(QFileDialog::Detail); 00176 QStringList fileNames; 00177 if (dialog.exec()) 00178 fileNames = dialog.selectedFiles(); 00179 if(fileNames.size() > 0) 00180 return fileNames[0]; 00181 else 00182 return QString(""); 00183 } 00184 00185 00187 QList<unsigned> SpikeRasterWidget::getHueList(unsigned numItems){ 00188 QList<unsigned> tmpHueList; 00189 00190 //Return black spike colour in black and white mode 00191 if(blackAndWhiteMode){ 00192 for(unsigned i=0; i<numItems; ++i) 00193 tmpHueList.append(qRgb(0, 0, 0)); 00194 return tmpHueList; 00195 } 00196 00197 //Colour mode 00198 unsigned numHues = 1530; 00199 00200 //Sanity check 00201 if(numItems > numHues) 00202 throw SpikeStreamException("Number of requested hues exceeds that available."); 00203 00204 //Prevent divide by zero 00205 if(numItems == 0) 00206 return tmpHueList; 00207 else if(numItems == 1){ 00208 tmpHueList.append(qRgb(255, 0, 0)); 00209 return tmpHueList; 00210 } 00211 00212 for(unsigned i=0; i <= numHues - numHues%(numItems-1); i += numHues/(numItems-1)){ 00213 if(i < 255) 00214 tmpHueList.append(qRgb(255, i, 0)); 00215 else if (i < 510) 00216 tmpHueList.append(qRgb(i-255, 255, 0)); 00217 else if (i < 765) 00218 tmpHueList.append(qRgb(0, 255, i - 510)); 00219 else if (i < 1020) 00220 tmpHueList.append(qRgb(0, i-765, 255)); 00221 else if (i < 1275) 00222 tmpHueList.append(qRgb(i-1020, 0, 255)); 00223 else 00224 tmpHueList.append(qRgb(255, 0, i-1275)); 00225 } 00226 00227 if((int)numItems != tmpHueList.size()) 00228 throw SpikeStreamException("Incorrect number of hues found. NumItems: " + QString::number(numItems) + "; size: " + QString::number(tmpHueList.size())); 00229 00230 return tmpHueList; 00231 } 00232 00233 00235 void SpikeRasterWidget::increaseTimeStep(int currentTimeStep){ 00236 //Check to see if we have moved out of the X axis range 00237 if( ((currentTimeStep /numTimeSteps) * numTimeSteps) != minTimeStep){ 00238 minTimeStep = (currentTimeStep/numTimeSteps) * numTimeSteps; 00239 bufferImage->fill(backgroundColor); 00240 updateAxes = true; 00241 } 00242 } 00243 00244 00246 void SpikeRasterWidget::paintAxes(QPainter& painter) { 00247 painter.setPen( QPen(Qt::black) ); 00248 00249 //Set up font 00250 QFont font; 00251 font.setFamily("Helvetica"); 00252 font.setWeight(QFont::Light); 00253 font.setPixelSize(fontSize); 00254 painter.setFont(font); 00255 00256 //Paint axes 00257 paintYAxis(painter); 00258 paintXAxis(painter); 00259 00260 //Set flag recording that axes are updated 00261 updateAxes = false; 00262 } 00263 00264 00266 void SpikeRasterWidget::paintNeuronGroupNames(QPainter& painter){ 00267 //Set up font 00268 QFont font; 00269 font.setFamily("Helvetica"); 00270 font.setWeight(QFont::Light); 00271 font.setPixelSize(neurGrpNameFontSize); 00272 painter.setFont(font); 00273 00274 for(QList<NeuronGroup*>::iterator neurGrpIter = neuronGroupList.begin(); neurGrpIter != neuronGroupList.end(); ++neurGrpIter){ 00275 painter.setPen( QPen( QColor(neurGrpColorMap[(*neurGrpIter)->getID()]) ) ); 00276 painter.drawText( 00277 imageWidth/2, imageHeight - xAxisPadding - neurGrpOffsetMap[(*neurGrpIter)->getID()], 00278 (*neurGrpIter)->getInfo().getName() 00279 ); 00280 } 00281 } 00282 00283 00285 void SpikeRasterWidget::paintXAxis(QPainter& painter) { 00286 //Draw X axis line 00287 painter.drawLine(0, imageHeight-xAxisPadding, imageWidth, imageHeight-xAxisPadding); 00288 00289 //Draw ticks 00290 for(int i=0; i<numTimeSteps; i += numTimeSteps/10) 00291 paintXAxisTick(painter, i + yAxisPadding, minTimeStep + i); 00292 } 00293 00294 00296 void SpikeRasterWidget::paintXAxisTick(QPainter& painter, int xPos, int labelX) { 00297 painter.drawLine(xPos, 00298 imageHeight - xAxisPadding, 00299 xPos, 00300 imageHeight-xAxisPadding + xAxisTickLength 00301 ); 00302 int textW = fontSize*5; 00303 painter.drawText( 00304 xPos - textW/2, imageHeight-xAxisPadding + xAxisTickLength, 00305 textW, fontSize, 00306 Qt::AlignHCenter, 00307 QString::number(labelX) + "ms" 00308 ); 00309 } 00310 00311 00313 void SpikeRasterWidget::paintYAxisTick(QPainter& painter, int yPos, int label) { 00314 painter.drawLine(yAxisPadding, yPos, yAxisPadding - yAxisTickLength, yPos); 00315 painter.drawText( 00316 0, yPos + fontSize/2, 00317 QString::number(label) 00318 ); 00319 } 00320 00321 00323 void SpikeRasterWidget::paintYAxis(QPainter& painter){ 00324 //Draw Y axis line 00325 painter.drawLine(yAxisPadding, 0, yAxisPadding, imageHeight); 00326 00327 //Draw ticks 00328 for(int i=0; i<numNeurons; i += numNeurons/10){ 00329 paintYAxisTick(painter, imageHeight - xAxisPadding - i, i); 00330 } 00331 } 00332 00333