SpikeStream Application Library
0.2
|
00001 //SpikeStream includes 00002 #include "ArchiveWidget.h" 00003 #include "DescriptionDialog.h" 00004 #include "Globals.h" 00005 #include "SpikeStreamException.h" 00006 #include "Util.h" 00007 using namespace spikestream; 00008 00009 //Qt includes 00010 #include <QLabel> 00011 #include <QMessageBox> 00012 #include <QPixmap> 00013 #include <QPushButton> 00014 00015 00017 ArchiveWidget::ArchiveWidget(QWidget* parent) : QWidget(parent){ 00018 QVBoxLayout* verticalBox = new QVBoxLayout(this); 00019 00020 //Register types to enable signals and slots to work 00021 qRegisterMetaType< QList<unsigned> >("QList<unsigned>"); 00022 00023 //Add tool bar 00024 toolBar = getToolBar(); 00025 verticalBox->addWidget(toolBar); 00026 00027 //List of available archives 00028 gridLayout = new QGridLayout(); 00029 gridLayout->setMargin(10); 00030 gridLayout->setColumnMinimumWidth(idCol, 50);//Archive ID 00031 gridLayout->setColumnMinimumWidth(netIDCol, 50);//NetworkID 00032 gridLayout->setColumnMinimumWidth(dateCol, 150);//Date and time 00033 gridLayout->setColumnMinimumWidth(descCol, 250);//Description 00034 00035 QHBoxLayout* gridLayoutHolder = new QHBoxLayout(); 00036 gridLayoutHolder->addLayout(gridLayout); 00037 gridLayoutHolder->addStretch(5); 00038 verticalBox->addLayout(gridLayoutHolder); 00039 00040 //Load the current set of archives, if they exist, into the grid layout 00041 loadArchiveList(); 00042 00043 //Pad out with some stretch 00044 verticalBox->addStretch(1); 00045 00046 //Listen for changes in the network and archive 00047 connect(Globals::getEventRouter(), SIGNAL(networkChangedSignal()), this, SLOT(networkChanged())); 00048 connect(Globals::getEventRouter(), SIGNAL(archiveListChangedSignal()), this, SLOT(loadArchiveList())); 00049 00050 //Inform other classes when a different archive is loaded 00051 connect(this, SIGNAL(archiveChanged()), Globals::getEventRouter(), SLOT(archiveChangedSlot())); 00052 00053 //Create class to load archive data in a separate thread 00054 archivePlayer = new ArchivePlayerThread(Globals::getArchiveDao()->getDBInfo()); 00055 connect(archivePlayer, SIGNAL(finished()), this, SLOT(archivePlayerStopped())); 00056 connect(archivePlayer, SIGNAL(timeStepChanged(unsigned, const QList<unsigned>&)), this, SLOT(updateTimeStep(unsigned, const QList<unsigned>&)), Qt::QueuedConnection); 00057 00058 //Initialise variables 00059 ignoreButton = false; 00060 00061 setMinimumHeight(200); 00062 } 00063 00064 00066 ArchiveWidget::~ArchiveWidget(){ 00067 delete archivePlayer; 00068 } 00069 00070 00071 /*----------------------------------------------------------*/ 00072 /*----- PRIVATE SLOTS -----*/ 00073 /*----------------------------------------------------------*/ 00074 00076 void ArchiveWidget::deleteArchive(){ 00077 //Get the ID of the archive to be deleted 00078 unsigned int archiveID = Util::getUInt(sender()->objectName()); 00079 if(!archiveInfoMap.contains(archiveID)){ 00080 qCritical()<<"Archive with ID "<<archiveID<<" cannot be found."; 00081 return; 00082 } 00083 00084 //Check to see if the current archive is playing 00085 if(Globals::isArchivePlaying() && Globals::getArchive()->getID() == archiveID){ 00086 qWarning()<<"This current archive cannot be deleted because it is playing. Stop playback and try again."; 00087 return; 00088 } 00089 00090 //Confirm that user wants to take this action. 00091 QMessageBox msgBox; 00092 msgBox.setText("Deleting Archive"); 00093 msgBox.setInformativeText("Are you sure that you want to delete archive with ID " + QString::number(archiveID) + "? This step cannot be undone."); 00094 msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); 00095 msgBox.setDefaultButton(QMessageBox::Cancel); 00096 int ret = msgBox.exec(); 00097 if(ret != QMessageBox::Ok) 00098 return; 00099 00100 //Delete the archive from the database 00101 try{ 00102 Globals::getArchiveDao()->deleteArchive(archiveID); 00103 } 00104 catch(SpikeStreamException& ex){ 00105 qCritical()<<ex.getMessage(); 00106 } 00107 00108 /* If we have deleted the current archive, use event router to inform other classes that the archive has changed. 00109 This will automatically reload the archive list. */ 00110 if(Globals::archiveLoaded() && Globals::getArchive()->getID() == archiveID){ 00111 Globals::setArchive(NULL); 00112 emit archiveChanged(); 00113 QTimer::singleShot(500, this, SLOT(loadArchiveList())); 00114 } 00115 else{//Otherwise, just reload the archive list 00116 QTimer::singleShot(500, this, SLOT(loadArchiveList())); 00117 } 00118 } 00119 00120 00122 void ArchiveWidget::loadArchive(){ 00123 unsigned int archiveID = sender()->objectName().toUInt(); 00124 if(!archiveInfoMap.contains(archiveID)){ 00125 qCritical()<<"Archive with ID "<<archiveID<<" cannot be found."; 00126 return; 00127 } 00128 00129 //load the archive 00130 loadArchive(archiveInfoMap[archiveID]); 00131 } 00132 00133 00135 void ArchiveWidget::loadArchiveList(){ 00136 //Reset widget 00137 reset(); 00138 00139 //If no network is loaded, show no network loaded message and return 00140 if(!Globals::networkLoaded()){ 00141 gridLayout->addWidget(new QLabel("No network loaded."), 0, 0); 00142 return; 00143 } 00144 00145 //Get a list of the archives in the database 00146 ArchiveDao* archiveDao = Globals::getArchiveDao(); 00147 QList<ArchiveInfo> archiveInfoList; 00148 try{ 00149 archiveInfoList = archiveDao->getArchivesInfo(Globals::getNetwork()->getID()); 00150 } 00151 catch(SpikeStreamException& ex){ 00152 qCritical()<<ex.getMessage(); 00153 return; 00154 } 00155 00156 //Show "no archive" message if list is empty 00157 if(archiveInfoList.size() == 0){ 00158 gridLayout->addWidget(new QLabel("No archives in database"), 0, 0); 00159 } 00160 00161 //Copy archive infos into map 00162 for(int i=0; i<archiveInfoList.size(); ++i){ 00163 archiveInfoMap[archiveInfoList[i].getID()] = archiveInfoList[i]; 00164 } 00165 00166 /* If the current archive is in the archive list, then set this as the one loaded 00167 Otherwise currentArchiveID is set to zero and the user has to choose the loaded archive */ 00168 unsigned int currentArchiveID = 0; 00169 if(Globals::archiveLoaded() && archiveInfoMap.contains(Globals::getArchive()->getID())){ 00170 currentArchiveID = Globals::getArchive()->getID(); 00171 } 00172 00173 00174 //Display the list in the widget 00175 for(int i=0; i<archiveInfoList.size(); ++i){ 00176 ArchiveInfo archInfo = archiveInfoList[i]; 00177 00178 //Property button 00179 QPushButton* propButton = new QPushButton("P"); 00180 propButton->setMaximumWidth(20); 00181 propButton->setObjectName(QString::number(archInfo.getID())); 00182 connect ( propButton, SIGNAL(clicked()), this, SLOT( setArchiveProperties() ) ); 00183 gridLayout->addWidget(propButton, i, propCol); 00184 00185 //Archive ID, network ID, date and description 00186 QLabel* idLabel = new QLabel(QString::number(archInfo.getID())); 00187 QLabel* networkIDLabel = new QLabel(QString::number(archInfo.getNetworkID())); 00188 QLabel* dateLabel = new QLabel(archInfo.getDateTime().toString()); 00189 QLabel* descriptionLabel = new QLabel(archInfo.getDescription()); 00190 00191 //Load button and name it with the object id so we can tell which button was invoked 00192 QPushButton* loadButton = new QPushButton("Load"); 00193 loadButton->setObjectName(QString::number(archInfo.getID())); 00194 connect ( loadButton, SIGNAL(clicked()), this, SLOT( loadArchive() ) ); 00195 00196 //Create the delete button and name it with the object id so we can tell which button was invoked 00197 QPushButton* deleteButton = new QPushButton(QIcon(Globals::getSpikeStreamRoot() + "/images/trash_can.png"), ""); 00198 deleteButton->setObjectName(QString::number(archInfo.getID())); 00199 connect ( deleteButton, SIGNAL(clicked()), this, SLOT( deleteArchive() ) ); 00200 00201 //Set labels and buttons depending on whether it is the current archive 00202 if(currentArchiveID == archInfo.getID()){//The curently loaded archive 00203 idLabel->setStyleSheet( "QLabel { color: #008000; font-weight: bold; }"); 00204 networkIDLabel->setStyleSheet( "QLabel { color: #008000; font-weight: bold; }"); 00205 dateLabel->setStyleSheet( "QLabel { color: #008000; font-weight: bold; }"); 00206 descriptionLabel->setStyleSheet( "QLabel { color: #008000; font-weight: bold; }"); 00207 loadButton->setEnabled(false); 00208 } 00209 else{//An archive that is not loaded 00210 idLabel->setStyleSheet( "QLabel { color: #777777; }"); 00211 networkIDLabel->setStyleSheet( "QLabel { color: #777777; }"); 00212 dateLabel->setStyleSheet( "QLabel { color: #777777; }"); 00213 descriptionLabel->setStyleSheet( "QLabel { color: #777777; }"); 00214 } 00215 00216 //Add the widgets to the layout 00217 gridLayout->addWidget(idLabel, i, idCol); 00218 gridLayout->addWidget(networkIDLabel, i, netIDCol); 00219 gridLayout->addWidget(dateLabel, i, dateCol); 00220 gridLayout->addWidget(descriptionLabel, i, descCol); 00221 gridLayout->addWidget(loadButton, i, loadButCol); 00222 gridLayout->addWidget(deleteButton, i, delButCol); 00223 //gridLayout->setRowMinimumHeight(i, 20); 00224 } 00225 00226 00227 //List of archives is empty or current archive is not found in this list 00228 if(archiveInfoList.size() == 0 || currentArchiveID == 0){ 00229 //Unload the current archive and inform other classes 00230 if(Globals::archiveLoaded()){ 00231 Globals::setArchive(0); 00232 emit archiveChanged(); 00233 } 00234 } 00235 00236 //FIXME: HACK TO GET IT TO DISPLAY PROPERLY 00237 this->setMinimumHeight(archiveInfoList.size() * 100); 00238 } 00239 00240 00242 void ArchiveWidget::networkChanged(){ 00243 loadArchiveList(); 00244 00245 //Reset time step labels 00246 timeStepLabel->setText("0"); 00247 maxTimeStepLabel->setText("0"); 00248 } 00249 00250 00252 void ArchiveWidget::rewindButtonPressed(){ 00253 //Stop thread if it is running. Rewind will be done when thread finishes 00254 if(archivePlayer->isRunning()){ 00255 archivePlayer->stop(); 00256 rewind = true; 00257 } 00258 //Archive player not running, so just rewind 00259 else{ 00260 rewindArchive(); 00261 playAction->setEnabled(true); 00262 stopAction->setEnabled(false); 00263 fastForwardAction->setEnabled(true); 00264 } 00265 } 00266 00267 00269 void ArchiveWidget::playButtonPressed(){ 00270 //Reset variables 00271 rewind = false; 00272 step = false; 00273 archiveOpen = true; 00274 00275 //Do nothing if no archive is loaded 00276 if(!Globals::archiveLoaded()){ 00277 return; 00278 } 00279 00280 //If archive is already playing, set the frame rate to its regular frame rate and return 00281 if(archivePlayer->isRunning()){ 00282 archivePlayer->setFrameRate(Util::getUInt(frameRateCombo->currentText())); 00283 fastForwardAction->setEnabled(true); 00284 playAction->setEnabled(false); 00285 return; 00286 } 00287 00288 //Update the maximum time step label in case we are playing from an archive connected to a simulator 00289 setMaxTimeStepLabel(); 00290 00291 //Start the player thread playing 00292 unsigned int frameRate = Util::getUInt(frameRateCombo->currentText()); 00293 unsigned int startTimeStep = Globals::getArchive()->getTimeStep(); 00294 archivePlayer->play(startTimeStep, Globals::getArchive()->getID(), frameRate); 00295 00296 //Fix the actions 00297 playAction->setEnabled(false); 00298 fastForwardAction->setEnabled(true); 00299 stopAction->setEnabled(true); 00300 } 00301 00302 00304 void ArchiveWidget::stepButtonPressed(){ 00305 //Stop thread if it is running. Step will be done when thread finishes 00306 if(archivePlayer->isRunning()){ 00307 archivePlayer->stop(); 00308 step = true; 00309 } 00310 00311 //Archive player not running, so just step 00312 else{ 00313 stepArchive(); 00314 } 00315 } 00316 00317 00319 void ArchiveWidget::fastForwardButtonPressed(){ 00320 //Do nothing if no archive is loaded 00321 if(!Globals::archiveLoaded()){ 00322 return; 00323 } 00324 00325 //Set higher frame rate if archive player is already running 00326 if(archivePlayer->isRunning()){ 00327 archivePlayer->setFrameRate(25); 00328 } 00329 //Otherwise start archive player with higher frame rate 00330 else{ 00331 archiveOpen = true; 00332 unsigned int startTimeStep = Globals::getArchive()->getTimeStep(); 00333 archivePlayer->play(startTimeStep, Globals::getArchive()->getID(), 25); 00334 } 00335 playAction->setEnabled(true); 00336 stopAction->setEnabled(true); 00337 fastForwardAction->setEnabled(false); 00338 } 00339 00340 00342 void ArchiveWidget::stopButtonPressed(){ 00343 archivePlayer->stop(); 00344 } 00345 00346 00348 void ArchiveWidget::frameRateComboChanged(int){ 00349 unsigned int frameRate = Util::getUInt(frameRateCombo->currentText()); 00350 archivePlayer->setFrameRate(frameRate); 00351 } 00352 00353 00356 void ArchiveWidget::archivePlayerStopped(){ 00357 //Rewind if this has been set 00358 if(rewind){ 00359 rewindArchive(); 00360 rewind = false; 00361 } 00362 00363 //Step if this has been set 00364 if(step){ 00365 stepArchive(); 00366 step = false; 00367 } 00368 00369 //Reset the state of all the buttons 00370 playAction->setEnabled(true); 00371 fastForwardAction->setEnabled(true); 00372 stopAction->setEnabled(false); 00373 } 00374 00375 00377 void ArchiveWidget::setArchiveProperties(){ 00378 unsigned int archiveID = sender()->objectName().toUInt(); 00379 if(!archiveInfoMap.contains(archiveID)){ 00380 qCritical()<<"Archive with ID "<<archiveID<<" cannot be found."; 00381 return; 00382 } 00383 try{ 00384 DescriptionDialog* descDialog = new DescriptionDialog(archiveInfoMap[archiveID].getDescription(), this); 00385 if(descDialog->exec() == QDialog::Accepted){ 00386 Globals::getArchiveDao()->setArchiveProperties(archiveID, descDialog->getDescription()); 00387 loadArchiveList(); 00388 } 00389 delete descDialog; 00390 } 00391 catch(SpikeStreamException& ex){ 00392 qCritical()<<ex.getMessage(); 00393 } 00394 } 00395 00396 00398 void ArchiveWidget::updateTimeStep(unsigned timeStep, const QList<unsigned>& neuronIDList){ 00399 //Update the time step counter and the time step in the archive 00400 Globals::getArchive()->setTimeStep(timeStep); 00401 timeStepLabel->setText(QString::number(timeStep)); 00402 00403 //Build new highlight map from list of IDs 00404 QHash<unsigned int, RGBColor*>* newHighlightMap = new QHash<unsigned int, RGBColor*>(); 00405 RGBColor* neuronColor = Globals::getNetworkDisplay()->getArchiveFiringNeuronColor(); 00406 foreach(unsigned tmpNeurID, neuronIDList){ 00407 (*newHighlightMap)[tmpNeurID] = neuronColor; 00408 } 00409 00410 //Set the colour map - this should automatically delete the old one. 00411 Globals::getNetworkDisplay()->setNeuronColorMap(newHighlightMap); 00412 00413 //Instruct thread to continue with next time step 00414 archivePlayer->clearWaitForGraphics(); 00415 } 00416 00417 00418 /*----------------------------------------------------------*/ 00419 /*----- PRIVATE METHODS -----*/ 00420 /*----------------------------------------------------------*/ 00421 00423 QToolBar* ArchiveWidget::getToolBar(){ 00424 QToolBar* tmpToolBar = new QToolBar(this); 00425 00426 QAction* tmpAction = new QAction(QIcon(Globals::getSpikeStreamRoot() + "/images/rewind.png"), "Rewind", this); 00427 connect(tmpAction, SIGNAL(triggered()), this, SLOT(rewindButtonPressed())); 00428 tmpToolBar->addAction (tmpAction); 00429 00430 playAction = new QAction(QIcon(Globals::getSpikeStreamRoot() + "/images/play.png"), "Play", this); 00431 connect(playAction, SIGNAL(triggered()), this, SLOT(playButtonPressed())); 00432 tmpToolBar->addAction (playAction); 00433 00434 tmpAction = new QAction(QIcon(Globals::getSpikeStreamRoot() + "/images/step.png"), "Step", this); 00435 connect(tmpAction, SIGNAL(triggered()), this, SLOT(stepButtonPressed())); 00436 tmpToolBar->addAction (tmpAction); 00437 00438 fastForwardAction = new QAction(QIcon(Globals::getSpikeStreamRoot() + "/images/forward.png"), "Fast forward", this); 00439 connect(fastForwardAction, SIGNAL(triggered()), this, SLOT(fastForwardButtonPressed())); 00440 tmpToolBar->addAction (fastForwardAction); 00441 00442 stopAction = new QAction(QIcon(Globals::getSpikeStreamRoot() + "/images/stop.png"), "Stop", this); 00443 connect(stopAction, SIGNAL(triggered()), this, SLOT(stopButtonPressed())); 00444 tmpToolBar->addAction (stopAction); 00445 00446 frameRateCombo = new QComboBox(); 00447 frameRateCombo->addItem("1"); 00448 frameRateCombo->addItem("5"); 00449 frameRateCombo->addItem("10"); 00450 frameRateCombo->addItem("15"); 00451 frameRateCombo->addItem("20"); 00452 frameRateCombo->addItem("25"); 00453 connect(frameRateCombo, SIGNAL(activated(int)), this, SLOT(frameRateComboChanged(int))); 00454 tmpToolBar->addWidget(frameRateCombo); 00455 00456 timeStepLabel = new QLabel ("0"); 00457 timeStepLabel->setStyleSheet( "QLabel { margin-left: 5px; background-color: #ffffff; border-color: #555555; border-width: 2px; border-style: outset; font-weight: bold;}"); 00458 timeStepLabel->setMinimumSize(50, 20); 00459 timeStepLabel->setMaximumSize(50, 20); 00460 timeStepLabel->setAlignment(Qt::AlignCenter); 00461 tmpToolBar->addWidget(timeStepLabel); 00462 maxTimeStepLabel = new QLabel("0"); 00463 maxTimeStepLabel->setStyleSheet( "QLabel { margin-left: 5px; background-color: #ffffff; border-color: #555555; border-width: 2px; border-style: outset; font-weight: bold;}"); 00464 maxTimeStepLabel->setMinimumSize(50, 20); 00465 maxTimeStepLabel->setMaximumSize(50, 20); 00466 maxTimeStepLabel->setAlignment(Qt::AlignCenter); 00467 tmpToolBar->addWidget(maxTimeStepLabel); 00468 00469 //Disable widget and all children - will be re-enabled when an archive is loaded 00470 tmpToolBar->setEnabled(false); 00471 00472 //Return the completed tool bar 00473 return tmpToolBar; 00474 } 00475 00476 00478 void ArchiveWidget::loadArchive(ArchiveInfo& archiveInfo){ 00479 if(!archiveInfoMap.contains(archiveInfo.getID())){ 00480 qCritical()<<"Archive with ID "<<archiveInfo.getID()<<" cannot be found."; 00481 return; 00482 } 00483 00484 // Create new archive 00485 Archive* newArchive = new Archive(archiveInfo); 00486 int minTimeStep = Globals::getArchiveDao()->getMinTimeStep(newArchive->getID()); 00487 newArchive->setTimeStep(minTimeStep); 00488 Globals::setArchive(newArchive); 00489 00490 //Inform classes about the change 00491 emit archiveChanged(); 00492 00493 //Set the time step labels 00494 timeStepLabel->setText( QString::number(minTimeStep) ); 00495 setMaxTimeStepLabel(); 00496 00497 //Enable the transport controls 00498 toolBar->setEnabled(true); 00499 playAction->setEnabled(true); 00500 stopAction->setEnabled(false); 00501 fastForwardAction->setEnabled(true); 00502 00503 //Nothing has yet been displayed of the archive firing patterns 00504 archiveOpen = false; 00505 00506 //Schedule reloading of archive after event loop has completed 00507 QTimer::singleShot(500, this, SLOT(loadArchiveList())); 00508 } 00509 00510 00513 void ArchiveWidget::reset(){ 00514 //Remove no archives label if it exists 00515 if(archiveInfoMap.size() == 0){ 00516 QLayoutItem* item = gridLayout->itemAtPosition(0, 0); 00517 if(item != 0){ 00518 delete item->widget(); 00519 } 00520 return; 00521 } 00522 00523 //Remove list of archives 00524 for(int rowIndx=0; rowIndx<archiveInfoMap.size(); ++rowIndx){ 00525 for(int colIndx=0; colIndx < numCols; ++colIndx){ 00526 QLayoutItem* item = gridLayout->itemAtPosition(rowIndx, colIndx); 00527 if(item != 0){ 00528 item->widget()->deleteLater(); 00529 } 00530 } 00531 } 00532 archiveInfoMap.clear(); 00533 00534 //Disable the transport controls 00535 if(!Globals::archiveLoaded()) 00536 toolBar->setEnabled(false); 00537 } 00538 00539 00541 void ArchiveWidget::rewindArchive(){ 00542 if(!Globals::archiveLoaded()){ 00543 qCritical()<<"No archive loaded,so cannot rewind archive."; 00544 return; 00545 } 00546 00547 unsigned minTimeStep = Globals::getArchiveDao()->getMinTimeStep(Globals::getArchive()->getID()); 00548 Globals::getArchive()->setTimeStep(minTimeStep); 00549 timeStepLabel->setText(QString::number(minTimeStep)); 00550 Globals::getNetworkDisplay()->setNeuronColorMap(new QHash<unsigned int, RGBColor*>()); 00551 archiveOpen = false; 00552 } 00553 00554 00556 void ArchiveWidget::setMaxTimeStepLabel(){ 00557 if(!Globals::archiveLoaded()){ 00558 qCritical()<<"Cannot set time step labels when no archive is loaded"; 00559 return; 00560 } 00561 unsigned int tmpArchiveID = Globals::getArchive()->getID(); 00562 maxTimeStepLabel->setText( QString::number(Globals::getArchiveDao()->getMaxTimeStep(tmpArchiveID)) ); 00563 } 00564 00565 00567 void ArchiveWidget::stepArchive(){ 00568 if(!Globals::archiveLoaded()){ 00569 qCritical()<<"No archive loaded,so cannot step archive."; 00570 return; 00571 } 00572 00573 unsigned int startTimeStep = Globals::getArchive()->getTimeStep(); 00574 //Step has been pressed before play, so nothing has been displayed of the archive 00575 if(!archiveOpen){ 00576 archivePlayer->step(startTimeStep, Globals::getArchive()->getID()); 00577 archiveOpen = true; 00578 } 00579 else{ 00580 archivePlayer->step(startTimeStep+1, Globals::getArchive()->getID()); 00581 } 00582 } 00583 00584