00001 #ifndef MAINWINDOW_C 00002 #define MAINWINDOW_C 00003 00004 #include "NeovisionII/NeoAnnotate/MainWindow.qt.H" 00005 #include "NeovisionII/NeoAnnotate/AnnotationObject.qt.H" 00006 #include "NeovisionII/NeoAnnotate/AnimationDelegate.qt.H" 00007 #include "NeovisionII/NeoAnnotate/AnnotationObjectMgrDelegate.qt.H" 00008 #include <Qt/QtXml> 00009 00010 //###################################################################### 00011 void MainWindow::createMenuBar() 00012 { 00013 QMenu * fileMenu = menuBar()->addMenu(tr("&File")); 00014 00015 00016 QAction * createDBEntryAction = fileMenu->addAction(tr("&New Annotation")); 00017 createDBEntryAction->setShortcut(tr("Ctrl+N")); 00018 connect(createDBEntryAction, SIGNAL(triggered()), this, SLOT(createDBEntry())); 00019 00020 QAction * saveToDBAction = fileMenu->addAction("&Save Annotation"); 00021 saveToDBAction->setShortcut(tr("Ctrl+S")); 00022 connect(saveToDBAction, SIGNAL(triggered()), this, SLOT(saveAnnotationToDB())); 00023 00024 QAction * openFromDBAction = fileMenu->addAction("&Open Existing Annotation"); 00025 openFromDBAction->setShortcut(tr("Ctrl+O")); 00026 connect(openFromDBAction, SIGNAL(triggered()), this, SLOT(openAnnotationFromDB())); 00027 00028 QAction * preferencesAction = fileMenu->addAction("Preferences..."); 00029 connect(preferencesAction, SIGNAL(triggered()), this, SLOT(openPrefsDialog())); 00030 00031 QMenu * connectionMenu = menuBar()->addMenu(tr("&Connection")); 00032 00033 QAction * connectToDBAction = connectionMenu->addAction("Connect To &Database"); 00034 connect(connectToDBAction, SIGNAL(triggered()), &itsDBManager, SLOT(connectToDb())); 00035 00036 QAction * chooseAnnotationSourceAction = connectionMenu->addAction("Choose Annotation &Source"); 00037 connect(chooseAnnotationSourceAction, SIGNAL(triggered()), &itsDBManager, SLOT(chooseAnnotationSource())); 00038 } 00039 00040 //###################################################################### 00041 void MainWindow::createToolbar() 00042 { 00043 //Initialize all of the qaction buttons with appropriate icons and tooltips 00044 //Icons from http://www.visualpharm.com/ 00045 QPushButton * zoomInAction = new QPushButton(QIcon("src/NeovisionII/NeoAnnotate/icons/Zoom-In-icon.png"),"", this); 00046 QPushButton * zoomOutAction = new QPushButton(QIcon("src/NeovisionII/NeoAnnotate/icons/Zoom-Out-icon.png"),"", this); 00047 QPushButton * cursorAction = new QPushButton(QIcon("src/NeovisionII/NeoAnnotate/icons/cursor-arrow.png"),"", this); 00048 QPushButton * addVertexAction = new QPushButton(QIcon("src/NeovisionII/NeoAnnotate/icons/cursor-add.png"),"", this); 00049 QPushButton * remVertexAction = new QPushButton(QIcon("src/NeovisionII/NeoAnnotate/icons/cursor-rem.png"),"", this); 00050 QPushButton * rotateAction = new QPushButton(QIcon("src/NeovisionII/NeoAnnotate/icons/rotate-icon.jpg"),"", this); 00051 00052 zoomInAction->setToolTip("Zoom In"); 00053 zoomOutAction->setToolTip("Zoom Out"); 00054 cursorAction->setToolTip("Edit Objects"); 00055 addVertexAction->setToolTip("Add Vertex"); 00056 remVertexAction->setToolTip("Remove Vertex"); 00057 00058 zoomInAction->setIconSize(QSize(24,24)); 00059 zoomOutAction->setIconSize(QSize(24,24)); 00060 cursorAction->setIconSize(QSize(24,24)); 00061 addVertexAction->setIconSize(QSize(24,24)); 00062 remVertexAction->setIconSize(QSize(24,24)); 00063 00064 QButtonGroup * cursorGroup = new QButtonGroup(this); 00065 cursorGroup->addButton(cursorAction); 00066 cursorGroup->addButton(addVertexAction); 00067 cursorGroup->addButton(remVertexAction); 00068 cursorGroup->addButton(rotateAction); 00069 00070 cursorAction->setCheckable(true); 00071 addVertexAction->setCheckable(true); 00072 remVertexAction->setCheckable(true); 00073 rotateAction->setCheckable(true); 00074 00075 //Create the opacity slider mechanism 00076 QHBoxLayout * opacityLayout = new QHBoxLayout(this); 00077 QLabel * opacityNameLbl = new QLabel(this); 00078 QSlider * opacitySlider = new QSlider(Qt::Horizontal, this); 00079 QLabel * opacityAmtLbl = new QLabel(this); 00080 QLabel * opacityPerLbl = new QLabel(this); 00081 connect(opacitySlider, SIGNAL(valueChanged(int)), opacityAmtLbl, SLOT(setNum(int))); 00082 connect(opacitySlider, SIGNAL(valueChanged(int)), itsObjectManager, SLOT(setOpacity(int))); 00083 opacitySlider->setValue(50); 00084 opacitySlider->setRange(0, 100); 00085 opacityNameLbl->setText("Opacity: "); 00086 opacityPerLbl->setText("%"); 00087 opacityLayout->addWidget(opacityNameLbl); 00088 opacityLayout->addWidget(opacitySlider); 00089 opacityLayout->addWidget(opacityAmtLbl); 00090 opacityLayout->addWidget(opacityPerLbl); 00091 QGroupBox * opacityBox = new QGroupBox(this); 00092 opacityBox->setMaximumWidth(200); 00093 opacityBox->setLayout(opacityLayout); 00094 00095 //Create the toolbar 00096 QToolBar * toolbar = addToolBar(tr("Edit")); 00097 00098 //Add all of the action buttons to the toolbar 00099 toolbar->addWidget(zoomInAction); 00100 toolbar->addWidget(zoomOutAction); 00101 toolbar->addSeparator(); 00102 toolbar->addSeparator(); 00103 toolbar->addWidget(cursorAction); 00104 toolbar->addWidget(addVertexAction); 00105 toolbar->addWidget(remVertexAction); 00106 toolbar->addWidget(rotateAction); 00107 toolbar->addSeparator(); 00108 toolbar->addSeparator(); 00109 toolbar->addWidget(opacityBox); 00110 toolbar->addSeparator(); 00111 00112 //Connect each action's triggered signal to the appropriate action 00113 connect(zoomInAction, SIGNAL(clicked()), itsMainDisplay, SLOT(zoomIn())); 00114 connect(zoomOutAction, SIGNAL(clicked()), itsMainDisplay, SLOT(zoomOut())); 00115 connect(cursorAction, SIGNAL(clicked()), itsMainDisplay, SLOT(setActionMode_Cursor())); 00116 connect(addVertexAction, SIGNAL(clicked()), itsMainDisplay, SLOT(setActionMode_AddVertex())); 00117 connect(remVertexAction, SIGNAL(clicked()), itsMainDisplay, SLOT(setActionMode_RemVertex())); 00118 connect(rotateAction, SIGNAL(clicked()), itsMainDisplay, SLOT(setActionMode_Rotate())); 00119 00120 //Trigger the default cursor action 00121 cursorAction->click(); 00122 } 00123 00124 //###################################################################### 00125 QWidget* MainWindow::createObjectList() 00126 { 00127 //Create a layout for the object list and associated controls 00128 QTableView *objectList = new QTableView; 00129 objectList->setSelectionBehavior(QAbstractItemView::SelectRows); 00130 objectList->setSelectionMode(QAbstractItemView::SingleSelection); 00131 objectList->setModel(itsObjectManager); 00132 QVBoxLayout *objectListLayout = new QVBoxLayout; 00133 objectListLayout->addWidget(objectList); 00134 00135 AnnotationObjectMgrDelegate* objMgrDel = new AnnotationObjectMgrDelegate(this); 00136 objMgrDel->setObjCategories(itsDBManager.getObjCategories()); 00137 objectList->setItemDelegate(objMgrDel); 00138 00139 //When a user clicks on an object in the list, the manager should highlight it, etc. 00140 connect(objectList, SIGNAL(clicked(const QModelIndex &)), itsObjectManager, SLOT(select(const QModelIndex &))); 00141 00142 //When the manager selects an object, the object list should highlight the appropriate row 00143 connect(itsObjectManager, SIGNAL(selectingObject(int)), objectList, SLOT(selectRow(int))); 00144 00145 QHBoxLayout *objectButtonLayout = new QHBoxLayout; 00146 00147 QPushButton *addObjectButton = new QPushButton(QIcon("src/NeovisionII/NeoAnnotate/icons/Add-icon.png"),""); 00148 QPushButton *delObjectButton = new QPushButton(QIcon("src/NeovisionII/NeoAnnotate/icons/Remove-icon.png"),""); 00149 connect(addObjectButton, SIGNAL(clicked()), this, SLOT(addObject())); 00150 connect(delObjectButton, SIGNAL(clicked()), itsObjectManager, SLOT(removeObject())); 00151 00152 objectButtonLayout->addWidget(addObjectButton); 00153 objectButtonLayout->addWidget(delObjectButton); 00154 objectButtonLayout->addStretch(); 00155 00156 objectListLayout->addLayout(objectButtonLayout); 00157 QWidget *objectListWidget = new QWidget; 00158 objectListWidget->setLayout(objectListLayout); 00159 00160 return objectListWidget; 00161 } 00162 00163 //###################################################################### 00164 QTableView* MainWindow::createAnimationControls() 00165 { 00166 itsAnimationView = new QTableView; 00167 itsAnimationView->setMouseTracking(true); 00168 itsAnimationView->setSelectionMode(QAbstractItemView::ExtendedSelection); 00169 itsAnimationView->setSelectionBehavior(QAbstractItemView::SelectItems); 00170 itsAnimationView->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); 00171 itsAnimationView->horizontalHeader()->setResizeMode(QHeaderView::Fixed); 00172 itsAnimationView->horizontalHeader()->setDefaultSectionSize(10); 00173 itsAnimationView->horizontalHeader()->hide(); 00174 00175 itsAnimationView->verticalHeader()->setResizeMode(QHeaderView::Fixed); 00176 itsAnimationView->verticalHeader()->setDefaultSectionSize(25); 00177 00178 AnimationDelegate *delegate = new AnimationDelegate(this); 00179 itsAnimationView->setItemDelegate(delegate); 00180 00181 itsAnimationView->setDragDropMode(QAbstractItemView::DragDrop); 00182 itsAnimationView->setDragEnabled(true); 00183 00184 itsAnimationView->viewport()->installEventFilter(this); 00185 00186 itsAnimationView->setContextMenuPolicy(Qt::CustomContextMenu); 00187 connect(itsAnimationView, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(animationViewPopup(const QPoint &))); 00188 00189 //When the frame changes, the animation view should show a column as selected 00190 connect(this, SIGNAL(frameIndexChanged(int)), delegate, SLOT(frameIndexChanged(int))); 00191 00192 //When a column is selected, the animation frame should change 00193 connect(itsAnimationView, SIGNAL(clicked(const QModelIndex &)), this, SLOT(animationFrameSelected(const QModelIndex &))); 00194 00195 return itsAnimationView; 00196 } 00197 00198 //###################################################################### 00199 MainWindow::MainWindow() : 00200 itsDBManager(this), 00201 itsCachedFrameLoader(new CachedFrameLoader), 00202 itsFramerate(10.0), 00203 itsPrefsDialog(this), 00204 itsSettings(new QSettings("iLab", "NeoAnnotate")) 00205 { 00206 itsDBManager.updateSettings(itsSettings); 00207 00208 connect(&itsDBManager, SIGNAL(openVideo(QString)), this, SLOT(openVideo(QString))); 00209 00210 statusBar()->insertPermanentWidget(0, &itsDBStatusLabel); 00211 statusBar()->insertPermanentWidget(1, new QLabel(" | ")); 00212 statusBar()->insertPermanentWidget(2, &itsAnnotatorLabel); 00213 itsDBStatusLabel.setText(tr("Not Connected To DB")); 00214 itsAnnotatorLabel.setText(tr("No Annotator Selected")); 00215 00216 itsDBManager.connectToDb(); 00217 while(itsDBManager.isConnected() == false) 00218 { 00219 QMessageBox msgBox(QMessageBox::Critical, "Not Connected To Database", 00220 "You must connect to a database to use this application", 00221 QMessageBox::Abort | QMessageBox::Retry); 00222 if(msgBox.exec() == QMessageBox::Retry) 00223 { 00224 itsDBManager.connectToDb(); 00225 } 00226 else 00227 { 00228 qDebug() << "Exiting. "; 00229 QCoreApplication::exit(0); 00230 exit(0); 00231 } 00232 } 00233 00234 buildWindow(); 00235 } 00236 00237 //###################################################################### 00238 void MainWindow::setDBStatusLabel(QString text) 00239 { 00240 itsDBStatusLabel.setText(text); 00241 } 00242 00243 //###################################################################### 00244 void MainWindow::setAnnotatorLabel(QString text) 00245 { 00246 itsAnnotatorLabel.setText(text); 00247 } 00248 00249 //###################################################################### 00250 void MainWindow::createTimeline() 00251 { 00252 //Construct the new timeline using the calculated duration 00253 itsTimeline = new QTimeLine(1, this); 00254 00255 itsTimeline->setCurveShape(QTimeLine::LinearCurve); 00256 } 00257 00258 //###################################################################### 00259 QGroupBox* MainWindow::createTransport() 00260 { 00261 //Clicking the push button will start the progress bar animation 00262 itsPlayButton = new QPushButton(QIcon("src/NeovisionII/NeoAnnotate/icons/Play-icon.png"),""); 00263 connect(itsPlayButton, SIGNAL(clicked()), this, SLOT(playPushed())); 00264 00265 //Create a slider to show/modify the current frame 00266 itsProgressBar = new QSlider(this); 00267 itsProgressBar->setOrientation(Qt::Horizontal); 00268 itsProgressBar->setRange(0,0); 00269 00270 connect(itsProgressBar, SIGNAL(sliderMoved(int)), this, SLOT(changeTime(int))); 00271 connect(itsProgressBar, SIGNAL(sliderPressed()), this, SLOT(sliderPressed())); 00272 connect(itsProgressBar, SIGNAL(sliderReleased()), this, SLOT(sliderReleased())); 00273 00274 itsFrameLabel = new QLabel(this); 00275 itsFrameLabel->setFrameStyle(QFrame::Panel | QFrame::Sunken); 00276 itsFrameLabel->setMaximumSize(90, 30); 00277 itsFrameLabel->setMinimumSize(90, 30); 00278 00279 //Create a layout to put the start button 00280 //and slider next to each other 00281 QHBoxLayout *transportLayout = new QHBoxLayout; 00282 transportLayout->addWidget(itsPlayButton); 00283 transportLayout->addWidget(itsProgressBar); 00284 transportLayout->addWidget(itsFrameLabel); 00285 00286 //Create a group box to hold this layout 00287 QGroupBox *transportBox = new QGroupBox("Transport"); 00288 transportBox->setLayout(transportLayout); 00289 transportBox->setFlat(false); 00290 00291 return transportBox; 00292 } 00293 00294 00295 //###################################################################### 00296 void MainWindow::buildWindow() 00297 { 00298 itsMainDisplay = new MainDisplay(this); 00299 itsObjectManager = new AnnotationObjectManager(this); 00300 00301 connect(itsMainDisplay, SIGNAL(addVertex(QPointF)), itsObjectManager, SLOT(addVertex(QPointF))); 00302 connect(itsMainDisplay, SIGNAL(removeVertex(QPointF)), itsObjectManager, SLOT(removeVertex(QPointF))); 00303 00304 createMenuBar(); 00305 createTimeline(); 00306 createToolbar(); 00307 QGroupBox* transport = createTransport(); 00308 QWidget* objectList = createObjectList(); 00309 00310 //Create the animation controls 00311 QTableView* animationControls = createAnimationControls(); 00312 itsObjectManager->setAnimationView(animationControls); 00313 00314 //Create a new layout/widget to hold the object list and transport box 00315 QVBoxLayout * bottomToolsLayout = new QVBoxLayout; 00316 bottomToolsLayout->addWidget(animationControls); 00317 00318 bottomToolsLayout->addWidget(transport); 00319 QWidget* bottomToolsWidget = new QWidget; 00320 bottomToolsWidget->setLayout(bottomToolsLayout); 00321 00322 QDockWidget * ObjectListToolbar = new QDockWidget("Object List"); 00323 ObjectListToolbar->setWidget(objectList); 00324 ObjectListToolbar->setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable); 00325 addDockWidget(Qt::LeftDockWidgetArea, ObjectListToolbar); 00326 00327 //Create a splitter to split the window between the main display, and the controls 00328 QSplitter *mainSplitter = new QSplitter(this); 00329 mainSplitter->setOrientation(Qt::Vertical); 00330 mainSplitter->addWidget(itsMainDisplay); 00331 mainSplitter->addWidget(bottomToolsWidget); 00332 QList<int> sizesList; 00333 sizesList.push_back(400); 00334 sizesList.push_back(100); 00335 mainSplitter->setSizes(sizesList); 00336 00337 //Set up this splitter as the only item in the main layout 00338 QVBoxLayout* mainLayout = new QVBoxLayout; 00339 mainLayout->addWidget(mainSplitter); 00340 00341 //Perform some kind of Qt magic to display the main layout 00342 QWidget *centralWidget = new QWidget; 00343 setCentralWidget(centralWidget); 00344 centralWidget->setLayout(mainLayout); 00345 00346 //Connect the play button 00347 connect(itsTimeline, SIGNAL(frameChanged(int)), itsProgressBar, SLOT(setValue(int))); 00348 connect(itsTimeline, SIGNAL(frameChanged(int)), this, SLOT(updateFrame(int))); 00349 connect(this, SIGNAL(pausePlayback(bool)), itsTimeline, SLOT(setPaused(bool))); 00350 00351 setWindowTitle(tr("NeoVision II Video Annotation Tool")); 00352 00353 //Grab and paint the first frame 00354 //updateFrame(itsCachedFrameLoader->getFrameRange().getFirst()); 00355 } 00356 00357 //////////////////////////////////////////////////////////////////////// 00358 // SLOTS 00359 //////////////////////////////////////////////////////////////////////// 00360 00361 //###################################################################### 00362 void MainWindow::updateFrame(int frameNum) 00363 { 00364 itsCurrentFrame = frameNum; 00365 itsFrameLabel->setText(QString("Frame: %1").arg(frameNum, 3, 10, QChar(' '))); 00366 itsMainDisplay->setImage(itsCachedFrameLoader->getFrame(frameNum)); 00367 00368 emit(frameIndexChanged(frameNum-itsCachedFrameLoader->getFrameRange().getFirst())); 00369 00370 // Scroll the animation view so that the current frame is always in the middle 00371 if(itsTimeline->state() == QTimeLine::Running) 00372 { 00373 QModelIndex root = itsAnimationView->indexAt(QPoint(0,0)); 00374 QModelIndex currIndex = root.child(1, frameNum); 00375 itsAnimationView->scrollTo(currIndex, QAbstractItemView::PositionAtCenter); 00376 itsAnimationView->scrollToTop(); 00377 } 00378 } 00379 00380 00381 //###################################################################### 00382 void MainWindow::addObject() 00383 { 00384 //Find the center of the current viewport 00385 QTransform t = itsMainDisplay->viewportTransform(); 00386 00387 float w = itsMainDisplay->width(); 00388 float h = itsMainDisplay->height(); 00389 00390 float sx = t.m11(); 00391 float sy = t.m22(); 00392 float tx = -t.m31(); 00393 float ty = -t.m32(); 00394 00395 QPointF center_view((w/2.0)/sx, (h/2.0)/sy); 00396 QPointF topLeft(tx/sx, ty/sy); 00397 QPointF center = center_view + topLeft; 00398 00399 //Create a new AnnotationObject, and put it at the center of the viewport, 00400 //starting at the current frame 00401 AnnotationObject * obj = new AnnotationObject( 00402 itsCurrentFrame, 00403 itsCachedFrameLoader->getFrameRange(), 00404 center 00405 ); 00406 00407 obj->setPos(center); 00408 00409 //Let the object know about the current frame 00410 obj->frameChanged(itsTimeline->currentFrame()); 00411 00412 itsObjectManager->addObject(obj); 00413 itsMainDisplay->addObject(obj); 00414 00415 connect(itsTimeline, SIGNAL(frameChanged(int)), obj, SLOT(frameChanged(int))); 00416 } 00417 00418 void MainWindow::playPushed() 00419 { 00420 if(itsTimeline->state() == QTimeLine::NotRunning) 00421 00422 { 00423 itsPlayButton->setDown(true); 00424 itsTimeline->start(); 00425 } 00426 else if(itsTimeline->state() == QTimeLine::Paused) 00427 { 00428 itsPlayButton->setDown(true); 00429 itsTimeline->resume(); 00430 } 00431 else if(itsTimeline->state() == QTimeLine::Running) 00432 { 00433 itsPlayButton->setDown(false); 00434 itsTimeline->setPaused(true); 00435 } 00436 } 00437 00438 void MainWindow::changeTime(int frameNum) 00439 { 00440 //Convert from frame number to milliseconds 00441 int ms = 1.0/itsFramerate * qreal(frameNum - itsCachedFrameLoader->getFrameRange().getFirst()) * 1000.0; 00442 00443 itsTimeline->setCurrentTime(ms); 00444 } 00445 00446 void MainWindow::sliderPressed() 00447 { 00448 if(itsTimeline->state() == QTimeLine::Running) 00449 { 00450 timelineWasRunning = true; 00451 itsTimeline->setPaused(true); 00452 } 00453 else 00454 { 00455 timelineWasRunning = false; 00456 } 00457 } 00458 00459 void MainWindow::sliderReleased() 00460 { 00461 if(timelineWasRunning) 00462 { 00463 //itsTimeline->setPaused(false); 00464 itsTimeline->start(); 00465 } 00466 } 00467 00468 00469 void MainWindow::animationViewPopup(const QPoint & pos) 00470 { 00471 QPoint globalPos = itsAnimationView->viewport()->mapToGlobal(pos); 00472 int column = itsAnimationView->indexAt(pos).column(); 00473 int row = itsAnimationView->indexAt(pos).row(); 00474 00475 itsObjectManager->constructAnimationContextMenu(globalPos, row, column); 00476 } 00477 00478 bool MainWindow::eventFilter(QObject * watched, QEvent * event) 00479 { 00480 if(watched == itsAnimationView->viewport()) 00481 { 00482 if(event->type() == QEvent::MouseButtonPress) 00483 { 00484 //The user has pressed the mouse button over the animation view 00485 00486 //Grab the index that was clicked on 00487 QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event); 00488 QPoint pos = mouseEvent->pos(); 00489 QModelIndex index = itsAnimationView->indexAt(pos); 00490 00491 //Change the framenumber 00492 int frameNumber = index.column() + itsCachedFrameLoader->getFrameRange().getFirst(); 00493 this->changeTime(frameNumber); 00494 00495 //Inform the manager that a cell was clicked on so that it can highlight 00496 //the proper row, and inform the animationmodel of the click in case 00497 //there is a drag and drop 00498 itsObjectManager->setLastAnimViewClick(index); 00499 00500 return false; 00501 00502 } 00503 else if(event->type() == QEvent::MouseButtonRelease) 00504 { 00505 //The user has released the mouse button over the animation view 00506 00507 //Grab the index that was clicked on 00508 QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event); 00509 QPoint pos = mouseEvent->pos(); 00510 int column = itsAnimationView->indexAt(pos).column(); 00511 int frameNumber = column + itsCachedFrameLoader->getFrameRange().getFirst(); 00512 00513 //Change the frame number 00514 this->changeTime(frameNumber); 00515 00516 return false; 00517 } 00518 else if(event->type() == QEvent::KeyPress) 00519 { 00520 00521 } 00522 } 00523 return false; 00524 } 00525 00526 void MainWindow::animationFrameSelected(const QModelIndex & index) 00527 { 00528 int frameNumber = index.column() + itsCachedFrameLoader->getFrameRange().getFirst(); 00529 updateFrame(frameNumber); 00530 changeTime(frameNumber); 00531 } 00532 00533 //void MainWindow::saveAnnotation() 00534 //{ 00535 // //Get the filename from a save file dialog 00536 // //QString filename = QFileDialog::getSaveFileName(this, "Export Annotation as XML", "", "XML (*.xml)"); 00537 // QFileDialog dialog(this, "Choose a directory"); 00538 // dialog.setFileMode(QFileDialog::DirectoryOnly); 00539 // if(dialog.exec()) 00540 // { 00541 // QString directoryName = dialog.selectedFiles()[0]; 00542 // 00543 // std::map<int, std::map<int, AnnotationObjectFrame > > animation = itsObjectManager->renderAnimations(); 00544 // 00545 // saveAnnotationToXML(directoryName, animation); 00546 // } 00547 //} 00548 00549 //void MainWindow::loadAnnotation() 00550 //{ 00551 // LINFO("Loading..."); 00552 // QFileDialog dialog(this, "Choose a directory"); 00553 // dialog.setFileMode(QFileDialog::DirectoryOnly); 00554 // if(dialog.exec()) 00555 // { 00556 // QString directoryName = dialog.selectedFiles()[0]; 00557 // qDebug() << "Directory Name: " << directoryName; 00558 // 00559 // loadAnnotationFromXML(directoryName); 00560 // } 00561 //} 00562 00563 void MainWindow::openVideo(QString fileName) 00564 { 00565 if(fileName == "") return; 00566 00567 itsFileName = fileName; 00568 itsCachedFrameLoader->loadVideo(itsFileName); 00569 00570 updateFrame(0); 00571 00572 //Grab the frame range from the framegrabber 00573 FrameRange frameRange = itsCachedFrameLoader->getFrameRange(); 00574 00575 //Compute the total number of frames 00576 int numFrames = frameRange.getLast() - frameRange.getFirst() - 1; 00577 00578 //Compute the total duration of the movie 00579 qreal duration_s = 1.0/(itsFramerate / qreal(numFrames)); 00580 qreal duration_ms = 1000.0 * duration_s; 00581 00582 itsTimeline->setDuration(duration_ms); 00583 00584 //Set the timelines first and last frames 00585 itsTimeline->setFrameRange(frameRange.getFirst(),frameRange.getLast()-1); 00586 00587 itsProgressBar->setRange(itsTimeline->startFrame(),itsTimeline->endFrame()); 00588 } 00589 00590 void MainWindow::openVideo() 00591 { 00592 QFileDialog dialog(this, tr("Choose a .mgzJ file")); 00593 dialog.setFileMode(QFileDialog::ExistingFile); 00594 dialog.setNameFilter(tr("mgzJ Files (*.mgzJ)")); 00595 00596 if(dialog.exec()) 00597 { 00598 openVideo(dialog.selectedFiles()[0]); 00599 } 00600 } 00601 00602 /* 00603 //TODO: This should be moved out of here into it's own class which inherits from some kind of general state importer/exporter class. 00604 //TODO: Implement a compact binary output for this data in addition to the bulky xml 00605 void MainWindow::saveAnnotationToXML(QString directoryName, std::map<int, std::map<int, AnnotationObjectFrame > > animation) 00606 { 00607 std::vector<QString> frame_filenames; 00608 00609 std::map<int, std::map<int, AnnotationObjectFrame> >::iterator animIt; 00610 00611 for(animIt = animation.begin(); animIt != animation.end(); animIt++) 00612 { 00613 00614 int fnum = animIt->first; 00615 00616 //Open the file for this frame 00617 QString filename = directoryName + QString("/frame%1.xml").arg(fnum); 00618 00619 QString frame_filename = QString("frame%1").arg(fnum); 00620 00621 QDomDocument frameDoc("frame"); 00622 00623 QDomElement node_scenes = frameDoc.createElement("scenes"); 00624 frameDoc.appendChild(node_scenes); 00625 00626 QDomElement node_annotation = frameDoc.createElement("annotation"); 00627 node_scenes.appendChild(node_annotation); 00628 00629 //TODO: This should be replaced with the real frame filename 00630 QDomElement node_filename = frameDoc.createElement("filename"); 00631 node_annotation.appendChild(node_filename); 00632 QDomText node_filename_t = frameDoc.createTextNode(QString("%1").arg(fnum)); 00633 node_filename.appendChild(node_filename_t); 00634 00635 QDomElement node_framenumber = frameDoc.createElement("framenumber"); 00636 node_annotation.appendChild(node_framenumber); 00637 QDomText node_framenumber_t = frameDoc.createTextNode(QString("%1").arg(fnum)); 00638 node_framenumber.appendChild(node_framenumber_t); 00639 00640 //TODO: What's this? 00641 QDomElement node_source = frameDoc.createElement("source"); 00642 node_annotation.appendChild(node_source); 00643 QDomText node_source_t = frameDoc.createTextNode("NeoVision"); 00644 node_source.appendChild(node_source_t); 00645 00646 //Grab all of the objects in this frame 00647 std::map<int, AnnotationObjectFrame> objects = animIt->second; 00648 std::map<int, AnnotationObjectFrame>::iterator objectsIt; 00649 00650 //Loop through all of the objects and put them into the xml tree 00651 for(objectsIt = objects.begin(); objectsIt != objects.end(); objectsIt++) 00652 { 00653 AnnotationObjectFrame f = objectsIt->second; 00654 00655 QDomElement node_object = frameDoc.createElement("object"); 00656 node_object.setAttribute("vis", f.ObjectFrameState.visible?"1":"0"); 00657 node_object.setAttribute("key", f.ObjectFrameState.is_keyframe?"1":"0"); 00658 node_annotation.appendChild(node_object); 00659 00660 QDomElement node_name = frameDoc.createElement("name"); 00661 node_object.appendChild(node_name); 00662 QDomText node_name_t = frameDoc.createTextNode(f.ObjectName); 00663 node_name.appendChild(node_name_t); 00664 00665 QDomElement node_id = frameDoc.createElement("id"); 00666 node_object.appendChild(node_id); 00667 QDomText node_id_t = frameDoc.createTextNode(QString("%1").arg(f.ObjectId)); 00668 node_id.appendChild(node_id_t); 00669 00670 QDomElement node_description = frameDoc.createElement("description"); 00671 node_object.appendChild(node_description); 00672 QDomText node_description_t = frameDoc.createTextNode(f.ObjectType); 00673 node_description.appendChild(node_description_t); 00674 00675 //Insert the position of the object into the file. Note that this is 00676 //_only_ so that we can reload this file to get an exact representation 00677 //of the original keyframing. This position is absolute, and all of the 00678 //vertex positions are absolute as well so that when we reload, we will 00679 //need to subtract the object position from the vertex positions to make 00680 //them relative again. 00681 QDomElement node_x = frameDoc.createElement("x"); 00682 node_object.appendChild(node_x); 00683 QDomText node_x_t = frameDoc.createTextNode(QString("%1").arg(f.ObjectFrameState.pos.x())); 00684 node_x.appendChild(node_x_t); 00685 QDomElement node_y = frameDoc.createElement("y"); 00686 node_object.appendChild(node_y); 00687 QDomText node_y_t = frameDoc.createTextNode(QString("%1").arg(f.ObjectFrameState.pos.y())); 00688 node_y.appendChild(node_y_t); 00689 00690 QDomElement node_polygon = frameDoc.createElement("polygon"); 00691 node_object.appendChild(node_polygon); 00692 00693 //Loop through all of the vertices and append them to the object branch 00694 std::map<int, ObjectAnimation::FrameState>::iterator vIt; 00695 for(vIt = f.VertexFrames.begin(); vIt != f.VertexFrames.end(); vIt++) 00696 { 00697 ObjectAnimation::FrameState vState = vIt->second; 00698 QDomElement node_pt = frameDoc.createElement("pt"); 00699 node_pt.setAttribute("vis", vState.visible?"1":"0"); 00700 node_pt.setAttribute("key", vState.is_keyframe?"1":"0"); 00701 node_polygon.appendChild(node_pt); 00702 00703 QDomElement node_x = frameDoc.createElement("x"); 00704 node_pt.appendChild(node_x); 00705 QDomText node_x_t = frameDoc.createTextNode(QString("%1").arg(vState.pos.x())); 00706 node_x.appendChild(node_x_t); 00707 00708 QDomElement node_y = frameDoc.createElement("y"); 00709 node_pt.appendChild(node_y); 00710 QDomText node_y_t = frameDoc.createTextNode(QString("%1").arg(vState.pos.y())); 00711 node_y.appendChild(node_y_t); 00712 00713 QDomElement node_id = frameDoc.createElement("id"); 00714 node_pt.appendChild(node_id); 00715 QDomText node_id_t = frameDoc.createTextNode(QString("%1").arg(vIt->first)); 00716 node_id.appendChild(node_id_t); 00717 } 00718 } 00719 00720 //Write the file to disk 00721 QString fullFrameFilename = directoryName + "/" + frame_filename + ".xml"; 00722 QFile frame_file(fullFrameFilename); 00723 frame_filenames.push_back(fullFrameFilename); 00724 if(!frame_file.open(QIODevice::WriteOnly | QIODevice::Text)) 00725 { 00726 qDebug() << "Couldn't write " << fullFrameFilename << "!"; 00727 return; 00728 } 00729 QTextStream out(&frame_file); 00730 out << frameDoc.toString(); 00731 } 00732 00733 00734 QDomDocument indexDoc("index"); 00735 00736 QDomElement node_scenes = indexDoc.createElement("scenes"); 00737 indexDoc.appendChild(node_scenes); 00738 00739 std::vector<QString>::iterator includeIt; 00740 for(includeIt = frame_filenames.begin(); includeIt != frame_filenames.end(); includeIt++) 00741 { 00742 QDomElement node_include = indexDoc.createElement("include"); 00743 node_include.setAttribute("filename", *includeIt); 00744 node_scenes.appendChild(node_include); 00745 } 00746 00747 //Write the file to disk 00748 QString indexFilename = directoryName + "/index.xml"; 00749 QFile index_file(indexFilename); 00750 if(!index_file.open(QIODevice::WriteOnly | QIODevice::Text)) 00751 { 00752 qDebug() << "Couldn't write " << indexFilename << "!"; 00753 return; 00754 } 00755 QTextStream out(&index_file); 00756 out << indexDoc.toString(); 00757 } 00758 00759 00760 void MainWindow::loadAnnotationFromXML(QString Directory) 00761 { 00762 QString indexFileName = Directory + "/index.xml"; 00763 QDomDocument indexDoc("index"); 00764 QFile file(indexFileName); 00765 if(!file.open(QIODevice::ReadOnly)) 00766 { 00767 LINFO("Could Not Open %s", indexFileName.toStdString().c_str()); 00768 return; 00769 } 00770 if(!indexDoc.setContent(&file)) 00771 { 00772 LINFO("Could Not Open %s - Bad File", indexFileName.toStdString().c_str()); 00773 file.close(); 00774 return; 00775 } 00776 file.close(); 00777 00778 //Load the document, and grab the scenes branch 00779 QDomElement indexDocElem = indexDoc.documentElement(); 00780 QDomNode node_scenes = indexDoc.elementsByTagName("scenes").item(0); 00781 if(node_scenes.isNull()) 00782 { 00783 LINFO("Error - malformed index.xml file! Line: %d", __LINE__); 00784 return; 00785 } 00786 00787 //Clear the Annotation Object Manager 00788 itsObjectManager->clear(); 00789 00790 //Get the frame range from the framegrabber so that we can only load 00791 //animation for the relavent frames. 00792 FrameRange videoFrameRange = itsCachedFrameLoader->getFrameRange(); 00793 00794 //Loop through each include node (frame number) in the scenes 00795 //TODO: Parse out the frame number and just continue if we are out of the frame range 00796 QDomNodeList fileList = node_scenes.childNodes(); 00797 for(int fileIdx = 0; fileIdx < fileList.size(); fileIdx++) 00798 { 00799 //Load the XML file included in the index 00800 QDomNode node_file = fileList.at(fileIdx); 00801 QString frameFileName = node_file.attributes().namedItem("filename").toAttr().value(); 00802 QDomDocument frameDoc("frame"); 00803 QFile file(frameFileName); 00804 if(!file.open(QIODevice::ReadOnly)) 00805 { 00806 LINFO("Could Not Open %s", frameFileName.toStdString().c_str()); 00807 return; 00808 } 00809 if(!frameDoc.setContent(&file)) 00810 { 00811 LINFO("Could Not Open %s - Bad File", frameFileName.toStdString().c_str()); 00812 file.close(); 00813 return; 00814 } 00815 file.close(); 00816 00817 //Load the document 00818 QDomElement frameDocElem = frameDoc.documentElement(); 00819 00820 //Find the framenumber for this document 00821 QDomNode node_framenumber = frameDocElem.elementsByTagName("framenumber").item(0); 00822 if(node_framenumber.isNull()) 00823 { LINFO("Error - malformed xml file! Line: %d", __LINE__); return; } 00824 int fnum = node_framenumber.firstChild().nodeValue().toInt(); 00825 00826 //Loop through all of the objects in the document 00827 QDomNodeList objects = frameDocElem.elementsByTagName("object"); 00828 for(int objIdx=0; objIdx<objects.size(); objIdx++) 00829 { 00830 QDomNode node_object = objects.at(objIdx); 00831 00832 //Grab the object's ID 00833 int objectId = node_object.firstChildElement("id").firstChild().nodeValue().toInt(); 00834 00835 //Find out if this frame is a keyframe for this object 00836 bool object_isKey = node_object.attributes().namedItem("key").toAttr().value().toInt(); 00837 00838 //Find out if the object is visible in this frame 00839 bool object_isVis = node_object.attributes().namedItem("vis").toAttr().value().toInt(); 00840 00841 //Grab the object's position 00842 QPointF objectPos; 00843 objectPos.setX(node_object.firstChildElement("x").firstChild().nodeValue().toDouble()); 00844 objectPos.setY(node_object.firstChildElement("y").firstChild().nodeValue().toDouble()); 00845 00846 //Read the object's vertices' states and store them in VertexFrames 00847 QDomNode node_vertex = node_object.firstChildElement("polygon").firstChildElement("pt"); 00848 std::map<int, ObjectAnimation::FrameState> VertexFrames; 00849 while(!node_vertex.isNull()) 00850 { 00851 ObjectAnimation::FrameState vertexState; 00852 vertexState.pos.setX(node_vertex.firstChildElement("x").firstChild().nodeValue().toDouble()); 00853 vertexState.pos.setY(node_vertex.firstChildElement("y").firstChild().nodeValue().toDouble()); 00854 vertexState.pos -= objectPos; 00855 vertexState.visible = node_vertex.attributes().namedItem("vis").toAttr().value().toInt(); 00856 vertexState.is_keyframe = node_vertex.attributes().namedItem("key").toAttr().value().toInt(); 00857 00858 int vertexId = node_vertex.firstChildElement("id").firstChild().nodeValue().toDouble(); 00859 VertexFrames[vertexId] = vertexState; 00860 00861 node_vertex = node_vertex.nextSibling(); 00862 } 00863 00864 //Check to see if the object already exists in the database 00865 AnnotationObject* obj = itsObjectManager->getObjectById(objectId); 00866 if(obj == NULL) 00867 { 00868 //If the object is not in the database, and we have found it's first keyframe then we should 00869 //create it and insert it into storage. 00870 00871 QString name = node_object.firstChildElement("name").firstChild().nodeValue(); 00872 int category = node_object.firstChildElement("description").firstChild().nodeValue().toInt(); 00873 00874 //Create a new object and insert it into the Object Manager 00875 obj = 00876 new AnnotationObject( 00877 fnum, 00878 videoFrameRange, 00879 objectPos, name, category 00880 ); 00881 obj->forceId(objectId); 00882 obj->clearAnimation(); 00883 obj->setVertices(VertexFrames); 00884 00885 itsObjectManager->addObject(obj); 00886 itsMainDisplay->addObject(obj); 00887 connect(itsTimeline, SIGNAL(frameChanged(int)), obj, SLOT(frameChanged(int))); 00888 } 00889 00890 //Check to see if we have a new keyframe for our object 00891 if(object_isKey) 00892 { 00893 obj->setKeyframe(fnum, objectPos, object_isVis); 00894 } 00895 00896 //Roll through all of the vertices to see if they have any keyframes at this frame 00897 std::map<int, ObjectAnimation::FrameState>::iterator vIt; 00898 for(vIt=VertexFrames.begin(); vIt!=VertexFrames.end(); vIt++) 00899 { 00900 ObjectAnimation::FrameState vertexState = vIt->second; 00901 if(vertexState.is_keyframe) 00902 { 00903 AnnotationObjectVertex* vertex = obj->getVertexById(vIt->first); 00904 vertex->setKeyframe(fnum, vertexState.pos, vertexState.visible); 00905 } 00906 } 00907 00908 } 00909 } 00910 00911 LINFO("Finished Import"); 00912 } 00913 */ 00914 00915 void MainWindow::saveAnnotationToDB() 00916 { 00917 itsDBManager.saveAnnotation(); 00918 } 00919 00920 void MainWindow::openAnnotationFromDB() 00921 { 00922 itsDBManager.openAnnotation(); 00923 } 00924 00925 void MainWindow::createDBEntry() 00926 { 00927 itsDBManager.createDBEntry(); 00928 } 00929 00930 void MainWindow::openPrefsDialog() 00931 { 00932 00933 if(itsSettings->contains("archiveLoc")) 00934 itsPrefsDialog.archiveLocEdit->setText(itsSettings->value("archiveLoc").toString()); 00935 if(itsSettings->contains("workingLoc")) 00936 itsPrefsDialog.workingLocEdit->setText(itsSettings->value("workingLoc").toString()); 00937 if(itsSettings->contains("incomingLoc")) 00938 itsPrefsDialog.incomingLocEdit->setText(itsSettings->value("incomingLoc").toString()); 00939 00940 if(itsPrefsDialog.exec()) 00941 { 00942 QString archiveLoc = itsPrefsDialog.archiveLocEdit->text(); 00943 QString workingLoc = itsPrefsDialog.workingLocEdit->text(); 00944 QString incomingLoc = itsPrefsDialog.incomingLocEdit->text(); 00945 00946 itsSettings->setValue("archiveLoc", archiveLoc); 00947 itsSettings->setValue("workingLoc", workingLoc); 00948 itsSettings->setValue("incomingLoc", incomingLoc); 00949 00950 } 00951 } 00952 #endif //MAINWINDOW_C 00953