SpikeStream Application Library  0.2
ArchiveWidget.cpp
Go to the documentation of this file.
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 
 All Classes Files Functions Variables Typedefs Friends Defines