Skip to content
Snippets Groups Projects
petrack.cpp 156 KiB
Newer Older
  • Learn to ignore specific revisions
  •         title = tr("PeTrack (v") + mPetrackVersion + tr("): ") + QFileInfo(mProFileName).fileName();
    
            if(mAnimation->isVideo() || mAnimation->isImageSequence())
    
                title += "; ";
    
    d.kilic's avatar
    d.kilic committed
        }
    
        if(mAnimation->isVideo())
    
            title += "sequence: " + mAnimation->getCurrentFileName() + tr(" (%1").arg(mAnimation->getNumFrames()) +
                     tr(" frames; %1x%2").arg(size.width()).arg(size.height()) + " pixel)";
    
        else if(mAnimation->isImageSequence())
    
            title += "sequence: " + mAnimation->getCurrentFileName() + tr(" ... (%1").arg(mAnimation->getNumFrames()) +
                     tr(" frames; %1x%2").arg(size.width()).arg(size.height()) + " pixel)";
    
    d.kilic's avatar
    d.kilic committed
        setWindowTitle(title);
    }
    
    void Petrack::saveVideo()
    {
        saveSequence(true, false);
    }
    void Petrack::saveVideoView()
    {
        saveSequence(true, true);
    }
    void Petrack::saveImageSequence()
    {
        saveSequence(false, false);
    }
    void Petrack::saveViewSequence()
    {
        saveSequence(false, true);
    }
    
    
    
    /**
     * @brief Saves current sequence as avi-file or image sequence
     *
     * Saves the loaded image sequence or video from current frame on till the end.
     * One can save the sequence as is or one can save the view shown in PeTrack.
     *
     * @param saveVideo true, if wanting to save a video. Ignored when dest isn't empty
     * @param saveView true, if sequence should be saved as shown in PeTrack (with trajectories etc.)
     * @param dest destination file; if empty, the user chooses via a dialog
     */
    
    d.kilic's avatar
    d.kilic committed
    void Petrack::saveSequence(bool saveVideo, bool saveView, QString dest) // default saveView= false, dest=""
    {
        static QString lastDir;
    
        //    bool autoSave = false;
    
    d.kilic's avatar
    d.kilic committed
    
        // if no destination file or folder is given
    
        if(dest.isEmpty())
    
    d.kilic's avatar
    d.kilic committed
        {
    
            if(lastDir.isEmpty() && !mSeqFileName.isEmpty())
    
    d.kilic's avatar
    d.kilic committed
                lastDir = QFileInfo(mSeqFileName).path();
    
    d.kilic's avatar
    d.kilic committed
    
    
    d.kilic's avatar
    d.kilic committed
            {
    
                dest = QFileDialog::getSaveFileName(
                    this,
                    tr("Select video file"),
                    lastDir,
                    tr("Video (*.avi);;All files (*.*)")); //? *.mpg *.mpeg
    
    d.kilic's avatar
    d.kilic committed
            }
            else
            {
    
                    dest = QFileDialog::getExistingDirectory(
                        this,
                        tr("Select directory to save view sequence"),
                        lastDir,
                        QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
    
    d.kilic's avatar
    d.kilic committed
                else
    
                    dest = QFileDialog::getExistingDirectory(
                        this,
                        tr("Select directory to save image sequence"),
                        lastDir,
                        QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
    
    d.kilic's avatar
    d.kilic committed
            }
        }
        else // uebergabe von saveVideo spielt keine roll mehr, sondern wird hier analysiert anhand von Dateiendung
        {
    
            //        autoSave = true; // command line option
            if(dest.right(4) == ".avi")
    
    d.kilic's avatar
    d.kilic committed
                saveVideo = true;
    
    d.kilic's avatar
    d.kilic committed
            else
    
    d.kilic's avatar
    d.kilic committed
                saveVideo = false;
    
    d.kilic's avatar
    d.kilic committed
        }
    
    
        if(!dest.isEmpty() && mImage)
    
    d.kilic's avatar
    d.kilic committed
        {
    
            int     rest      = mAnimation->getNumFrames() - 1;
            int     numLength = 1;
            int     memPos    = mPlayerWidget->getPos();
            QString fileName  = "";
    
    d.kilic's avatar
    d.kilic committed
    #ifdef AVI
            AviFile aviFile;
    #else
            AviFileWriter aviFile;
    #endif
    
            bool      formatIsSaveAble = false;
            bool      saveRet;
    
            QImage   *viewImage = nullptr;
    
            QPainter *painter   = nullptr;
            int       progEnd   = mAnimation->getSourceOutFrameNum() -
                          mPlayerWidget->getPos(); // nur wenn nicht an anfang gesprungen wird:-mPlayerWidget->getPos()
    
            cv::Mat iplImgFilteredBGR;
    
            bool    writeFrameRet   = false;
            bool    convert8To24bit = false;
            int     mult;
    
    d.kilic's avatar
    d.kilic committed
    
    
    
    d.kilic's avatar
    d.kilic committed
            {
    
    d.kilic's avatar
    d.kilic committed
                {
    
                    if(mCropZoomViewAct->isChecked())
    
                        viewImage =
                            new QImage(mView->viewport()->width(), mView->viewport()->height(), QImage::Format_RGB32);
    
    d.kilic's avatar
    d.kilic committed
                    else
    
    d.kilic's avatar
    d.kilic committed
                        viewImage = new QImage((int) mScene->width(), (int) mScene->height(), QImage::Format_RGB32);
    
    d.kilic's avatar
    d.kilic committed
                    painter = new QPainter();
                }
    
                if(convert8To24bit)
    
    d.kilic's avatar
    d.kilic committed
                    mult = 3;
    
    d.kilic's avatar
    d.kilic committed
                else
    
    d.kilic's avatar
    d.kilic committed
                    mult = 1;
    
    d.kilic's avatar
    d.kilic committed
    
                bool ok = false;
    
    
                    ok = aviFile.open(
                        dest.toStdString().c_str(),
                        viewImage->width(),
                        viewImage->height(),
                        viewImage->depth(),
                        mAnimation->getFPS());
    
    d.kilic's avatar
    d.kilic committed
                else
    
                    ok = aviFile.open(
                        dest.toStdString().c_str(), mImg.cols, mImg.rows, mult * 8 * mImg.channels(), mAnimation->getFPS());
    
    d.kilic's avatar
    d.kilic committed
                {
    
                    debout << "Error: opening AVI file: " << dest.toStdString().c_str() << std::endl;
    
    d.kilic's avatar
    d.kilic committed
                    return;
                }
            }
    
    
            if(!saveVideo)
    
    d.kilic's avatar
    d.kilic committed
            {
    
    d.kilic's avatar
    d.kilic committed
                {
    
                    if(mCropZoomViewAct->isChecked())
    
                        viewImage =
                            new QImage(mView->viewport()->width(), mView->viewport()->height(), QImage::Format_RGB32);
    
    d.kilic's avatar
    d.kilic committed
                    else
    
    d.kilic's avatar
    d.kilic committed
                        viewImage = new QImage((int) mScene->width(), (int) mScene->height(), QImage::Format_RGB32);
    
    d.kilic's avatar
    d.kilic committed
                    painter = new QPainter();
                }
    
                // test, if fileformat is supported
    
                if(mAnimation->isVideo())
    
    d.kilic's avatar
    d.kilic committed
                {
                    // calculate string length of sequence number
    
                    while((rest /= 10) > 0)
    
    d.kilic's avatar
    d.kilic committed
                        numLength++;
    
                    fileName = (dest + "/" + mAnimation->getFileBase() + "%1.png")
                                   .arg(mPlayerWidget->getPos(), numLength, 10, QChar('0'));
    
    d.kilic's avatar
    d.kilic committed
                }
                else
    
    d.kilic's avatar
    d.kilic committed
                    fileName = dest + "/" + mAnimation->getCurrentFileName();
    
    d.kilic's avatar
    d.kilic committed
    
    
    d.kilic's avatar
    d.kilic committed
                {
                    painter->begin(viewImage);
    
                    if(mCropZoomViewAct->isChecked())
    
    d.kilic's avatar
    d.kilic committed
                        mView->render(painter);
    
    d.kilic's avatar
    d.kilic committed
                    else
    
    d.kilic's avatar
    d.kilic committed
                        mScene->render(painter);
    
    d.kilic's avatar
    d.kilic committed
                    painter->end();
    
    
                    if(viewImage->save(fileName)) //, const char * format = 0 (format wird aus dateinamen geholt), int
                                                  // quality = -1 default normal (0..100)
    
    d.kilic's avatar
    d.kilic committed
                    {
                        formatIsSaveAble = true;
                        mPlayerWidget->frameForward();
                    }
                }
    
                else if(mImage->save(fileName)) //, const char * format = 0 (format wird aus dateinamen geholt), int quality
                                                //= -1 default normal (0..100)
    
    d.kilic's avatar
    d.kilic committed
                {
                    formatIsSaveAble = true;
                    mPlayerWidget->frameForward();
                }
            }
    
            else if((mImgFiltered.channels() == 1) /*&& convert8To24bit*/)
    
    d.kilic's avatar
    d.kilic committed
            {
    
                size.width  = mImgFiltered.cols;
    
    d.kilic's avatar
    d.kilic committed
                size.height = mImgFiltered.rows;
                iplImgFilteredBGR.create(size, CV_8UC3);
            }
    
            QProgressDialog progress("", "Abort save", 0, progEnd, this);
            progress.setWindowModality(Qt::WindowModal); // blocks main window
    
    
    d.kilic's avatar
    d.kilic committed
            {
    
    d.kilic's avatar
    d.kilic committed
                    progress.setLabelText("Save video view...");
    
    d.kilic's avatar
    d.kilic committed
                else
    
    d.kilic's avatar
    d.kilic committed
                    progress.setLabelText("Save video...");
    
    d.kilic's avatar
    d.kilic committed
            {
    
    d.kilic's avatar
    d.kilic committed
                    progress.setLabelText("Save view sequence...");
    
    d.kilic's avatar
    d.kilic committed
                else
    
    d.kilic's avatar
    d.kilic committed
                    progress.setLabelText("Save image sequence...");
    
                progress.setValue(
                    mPlayerWidget->getPos() - memPos); // -mempos nur, wenn nicht an den anfang gesprungen wird
    
    d.kilic's avatar
    d.kilic committed
                qApp->processEvents();
    
                if(progress.wasCanceled())
    
    d.kilic's avatar
    d.kilic committed
                    break;
    
    d.kilic's avatar
    d.kilic committed
    
    
    d.kilic's avatar
    d.kilic committed
                {
                    // video sequence
    
    d.kilic's avatar
    d.kilic committed
                    {
                        painter->begin(viewImage);
    
                        if(mCropZoomViewAct->isChecked())
    
    d.kilic's avatar
    d.kilic committed
                            mView->render(painter);
    
    d.kilic's avatar
    d.kilic committed
                        else
    
    d.kilic's avatar
    d.kilic committed
                            mScene->render(painter);
    
    d.kilic's avatar
    d.kilic committed
                        painter->end();
                    }
    
                    if((mImgFiltered.channels() == 1) /* && convert8To24bit*/)
    
    d.kilic's avatar
    d.kilic committed
                    {
    
                        cv::cvtColor(mImg, iplImgFilteredBGR, cv::COLOR_GRAY2BGR);
    
                            writeFrameRet = aviFile.appendFrame(
                                (const unsigned char *) viewImage->bits(),
                                true); // 2. param besagt, ob vertikal gespiegel werden soll
    
    d.kilic's avatar
    d.kilic committed
                        else
    
                            writeFrameRet = aviFile.appendFrame(
                                (const unsigned char *) iplImgFilteredBGR.data,
                                true); // 2. param besagt, ob vertikal gespiegel werden soll
    
    d.kilic's avatar
    d.kilic committed
                    }
                    else
                    {
    
                        if(saveView)
                        {
                            writeFrameRet = aviFile.appendFrame(
                                (const unsigned char *) viewImage->bits(),
                                true); // 2. param besagt, ob vertikal gespiegel werden soll
                        }
                        else
                        {
                            writeFrameRet = aviFile.appendFrame(
                                (const unsigned char *) mImg.data,
                                true); // 2. param besagt, ob vertikal gespiegel werden soll
    
    d.kilic's avatar
    d.kilic committed
                        }
                    }
    
    
                    if(!writeFrameRet)
    
    d.kilic's avatar
    d.kilic committed
                    {
                        progress.setValue(progEnd);
    
                        PCritical(
                            this,
                            tr("PeTrack"),
                            tr("Cannot save %1 maybe because of wrong file extension or unsupported codec.").arg(dest));
    
    d.kilic's avatar
    d.kilic committed
                        break;
                    }
                }
                else
                {
                    // single frame sequence
    
    d.kilic's avatar
    d.kilic committed
                    {
                        painter->begin(viewImage);
    
                        if(mCropZoomViewAct->isChecked())
    
    d.kilic's avatar
    d.kilic committed
                            mView->render(painter);
    
    d.kilic's avatar
    d.kilic committed
                        else
    
    d.kilic's avatar
    d.kilic committed
                            mScene->render(painter);
    
    d.kilic's avatar
    d.kilic committed
                        painter->end();
                    }
    
                    if(mAnimation->isVideo())
    
    d.kilic's avatar
    d.kilic committed
                    {
    
                        fileName = (dest + "/" + mAnimation->getFileBase() + "%1.png")
                                       .arg(mPlayerWidget->getPos(), numLength, 10, QChar('0'));
                        if(saveView)
    
    d.kilic's avatar
    d.kilic committed
                            saveRet = viewImage->save(fileName);
    
    d.kilic's avatar
    d.kilic committed
                        else
    
    d.kilic's avatar
    d.kilic committed
                            saveRet = mImage->save(fileName);
    
    d.kilic's avatar
    d.kilic committed
                    }
    
                    else if(formatIsSaveAble)
    
    d.kilic's avatar
    d.kilic committed
                    {
                        fileName = dest + "/" + mAnimation->getCurrentFileName();
    
    d.kilic's avatar
    d.kilic committed
                            saveRet = viewImage->save(fileName);
    
    d.kilic's avatar
    d.kilic committed
                        else
    
    d.kilic's avatar
    d.kilic committed
                            saveRet = mImage->save(fileName);
    
    d.kilic's avatar
    d.kilic committed
                    }
                    else
                    {
                        fileName = dest + "/" + QFileInfo(mAnimation->getCurrentFileName()).completeBaseName() + ".png";
    
    d.kilic's avatar
    d.kilic committed
                            saveRet = viewImage->save(fileName);
    
    d.kilic's avatar
    d.kilic committed
                        else
    
    d.kilic's avatar
    d.kilic committed
                            saveRet = mImage->save(fileName, "PNG"); //, int quality = -1 default normal (0..100)
    
    d.kilic's avatar
    d.kilic committed
                    }
    
    d.kilic's avatar
    d.kilic committed
                    {
                        progress.setValue(progEnd);
    
                        PCritical(this, tr("PeTrack"), tr("Cannot save %1.").arg(fileName));
    
    d.kilic's avatar
    d.kilic committed
                        break;
                    }
                }
    
            } while(mPlayerWidget->frameForward());
    
    d.kilic's avatar
    d.kilic committed
    
    
            if(!saveVideo && saveView)
    
    d.kilic's avatar
    d.kilic committed
            {
                delete viewImage;
                delete painter;
            }
    
            // bei abbruch koennen es auch mPlayerWidget->getPos() frames sein, die bisher geschrieben wurden
            //-memPos nur, wenn nicht an den anfang gesprungen wird
    
            debout << "wrote " << mPlayerWidget->getPos() + 1 - memPos << " of " << mAnimation->getNumFrames() << " frames."
                   << std::endl;
    
    d.kilic's avatar
    d.kilic committed
            progress.setValue(progEnd);
    
    
    d.kilic's avatar
    d.kilic committed
            {
                aviFile.close();
            }
    
            mPlayerWidget->skipToFrame(memPos);
            lastDir = dest;
        }
    }
    
    
    /**
     * @brief Saves the current View, including visualizations, in a file (.g. pdf)
     *
     * @param dest name of the saved file; if empty, a dialogue for the user opens
     */
    
    void Petrack::saveView(QString dest) // default = ""
    
    d.kilic's avatar
    d.kilic committed
    {
        static QString lastFile;
    
        if(mImage)
        {
            // if no destination file or folder is given
    
            if(dest.isEmpty())
    
    d.kilic's avatar
    d.kilic committed
            {
    
                if(lastFile.isEmpty() && !mSeqFileName.isEmpty())
    
    d.kilic's avatar
    d.kilic committed
                    lastFile = QFileInfo(mSeqFileName).path();
    
    d.kilic's avatar
    d.kilic committed
    
                // alle unetrstuetzen fileformate erhaelt man mit
                // QImageReader::supportedImageFormats() and QImageWriter::supportedImageFormats()
                // gif muss nicht dabei sein, dazu muss qt mit -qt-gif uebersetzt worden sein
    
                dest = QFileDialog::getSaveFileName(
                    this,
                    tr("Select image file"),
                    lastFile,
                    tr("PDF (*.pdf);;Postscript (*.ps *.eps);;Windows bitmaps (*.bmp);;JPEG (*.jpeg *.jpg);;Portable "
                       "network graphics (*.png);;Portable image format (*.pbm *.pgm *.ppm);;X11 Bitmap or Pixmap (*.xbm "
                       "*.xpm);;Pixel Images (*.bmp *.jpeg *.jpg *.png *.pbm *.pgm *.ppm *.xbm *.xpm);;All supported types "
                       "(*pdf *ps *.eps *.bmp *.jpeg *.jpg *.png *.pbm *.pgm *.ppm *.xbm *.xpm);;All files (*.*)"));
    
    d.kilic's avatar
    d.kilic committed
            }
    
    
            if(!dest.isEmpty())
    
    d.kilic's avatar
    d.kilic committed
            {
    
                if(dest.right(4) == ".pdf" || dest.right(3) == ".ps" || dest.right(4) == ".eps")
    
    d.kilic's avatar
    d.kilic committed
                {
    
                    QPdfWriter pdfWriter(dest);
                    pdfWriter.setPageMargins({0, 0, 0, 0});
                    QPageSize pageSize{mImage->size()};
                    pdfWriter.setPageSize(pageSize);
                    QPainter painter(&pdfWriter);
    
    
                    if(mCropZoomViewAct->isChecked())
    
    d.kilic's avatar
    d.kilic committed
                        mView->render(&painter);
    
    d.kilic's avatar
    d.kilic committed
                    else
    
    d.kilic's avatar
    d.kilic committed
                        mScene->render(&painter);
    
    d.kilic's avatar
    d.kilic committed
                }
                else
                {
                    // schwarzer rand links und unten?!
                    QImage *img;
    
                    if(mCropZoomViewAct->isChecked())
    
    d.kilic's avatar
    d.kilic committed
                        img = new QImage(mView->viewport()->width(), mView->viewport()->height(), QImage::Format_RGB32);
    
    d.kilic's avatar
    d.kilic committed
                    else
    
    d.kilic's avatar
    d.kilic committed
                        img = new QImage((int) mScene->width(), (int) mScene->height(), QImage::Format_RGB32);
    
    d.kilic's avatar
    d.kilic committed
                    QPainter painter(img);
    
                    if(mCropZoomViewAct->isChecked())
    
    d.kilic's avatar
    d.kilic committed
                        mView->render(&painter);
    
    d.kilic's avatar
    d.kilic committed
                    else
    
    d.kilic's avatar
    d.kilic committed
                        mScene->render(&painter);
    
    d.kilic's avatar
    d.kilic committed
                    painter.end();
    
                    if(!img->save(dest)) //, "PNG"
    
                        PCritical(
                            this, tr("PeTrack"), tr("Cannot save %1 maybe because of wrong file extension.").arg(dest));
    
    d.kilic's avatar
    d.kilic committed
                    delete img;
                }
                lastFile = dest;
            }
        }
    }
    
    
    void Petrack::saveImage(QString dest) // default = ""
    
    d.kilic's avatar
    d.kilic committed
    {
        static QString lastFile;
    
        if(mImage)
        {
            // if no destination file or folder is given
    
            if(dest.isEmpty())
    
    d.kilic's avatar
    d.kilic committed
            {
    
                if(lastFile.isEmpty() && !mSeqFileName.isEmpty())
    
    d.kilic's avatar
    d.kilic committed
                    lastFile = QFileInfo(mSeqFileName).path();
    
    d.kilic's avatar
    d.kilic committed
    
    
                // alle unetrstuetzen fileformate erhaelt man mit
                // QImageReader::supportedImageFormats() and QImageWriter::supportedImageFormats()
                // gif muss nict dabei sein, dazu muss qt mit -qt-gif uebesetz worden sein
    
                dest = QFileDialog::getSaveFileName(
                    this,
                    tr("Select image file"),
                    lastFile,
                    tr("PDF (*.pdf);;Postscript (*.ps *.eps);;Windows bitmaps (*.bmp);;JPEG (*.jpeg *.jpg);;Portable "
                       "network graphics (*.png);;Portable image format (*.pbm *.pgm *.ppm);;X11 Bitmap or Pixmap (*.xbm "
                       "*.xpm);;Pixel Images (*.bmp *.jpeg *.jpg *.png *.pbm *.pgm *.ppm *.xbm *.xpm);;All supported types "
                       "(*pdf *ps *.eps *.bmp *.jpeg *.jpg *.png *.pbm *.pgm *.ppm *.xbm *.xpm);;All files (*.*)"));
    
    d.kilic's avatar
    d.kilic committed
            }
    
    
            if(!dest.isEmpty())
    
    d.kilic's avatar
    d.kilic committed
            {
    
                if(dest.right(4) == ".pdf" || dest.right(3) == ".ps" || dest.right(4) == ".eps")
    
    d.kilic's avatar
    d.kilic committed
                {
    
                    QPdfWriter pdfWriter(dest);
                    pdfWriter.setPageMargins({0, 0, 0, 0});
                    QPageSize pageSize{mImage->size()};
                    pdfWriter.setPageSize(pageSize);
                    QPainter painter(&pdfWriter);
    
                    QRect rect = painter.viewport();
                    QSize size = mImage->size();
    
    d.kilic's avatar
    d.kilic committed
                    size.scale(rect.size(), Qt::KeepAspectRatio);
                    painter.setViewport(rect.x(), rect.y(), size.width(), size.height());
                    painter.setWindow(mImage->rect());
                    painter.drawImage(0, 0, *mImage);
                }
                else
                {
    
                    if(!mImage->save(dest)) //, "PNG"
    
                        PCritical(
                            this, tr("PeTrack"), tr("Cannot save %1 maybe because of wrong file extension.").arg(dest));
    
    d.kilic's avatar
    d.kilic committed
                }
                lastFile = dest;
            }
        }
    }
    
    void Petrack::print()
    {
        if(mImage)
        {
            // HighResolution font zu gross! und laengere laufzeit und eher overflow
            // aber so pixelig und keine schoenen linien
    
            QPrinter printer(QPrinter::ScreenResolution); // ScreenResolution, HighResolution// liefert zu hause:
                                                          // QWin32PrintEngine::initialize: GetPrinter failed ()
    
            printer.setPageSize(QPageSize{QPageSize::PageSizeId::A4});
    
    d.kilic's avatar
    d.kilic committed
            QPrintDialog dialog(&printer, this);
    
            if(dialog.exec())
            {
    
    d.kilic's avatar
    d.kilic committed
                QPainter painter(&printer);
                mView->render(&painter);
            }
        }
        else
    
            PCritical(this, tr("PeTrack"), tr("Nothing to print!"));
    
    d.kilic's avatar
    d.kilic committed
    }
    
    void Petrack::resetSettings()
    {
        mAnimation->reset();
    
        openXml(mDefaultSettings, false);
    
    d.kilic's avatar
    d.kilic committed
    }
    
    void Petrack::about()
    {
    
        auto about = new AboutDialog(
            this,
            mPetrackVersion,
            mGitCommitID,
            mGitCommitDate,
            mGitCommitBranch,
            mCompilerID,
            mCompilerVersion,
            mCompileDate,
            mAuthors);
        about->show();
    
    d.kilic's avatar
    d.kilic committed
    }
    
    
    void Petrack::commandLineOptions()
    {
    
        PMessageBox *mb = new PMessageBox{this, tr("Command line options"), commandLineOptionsString, QIcon()};
        mb->setModal(false);
    
    d.kilic's avatar
    d.kilic committed
        mb->setAttribute(Qt::WA_DeleteOnClose);
    
    d.kilic's avatar
    d.kilic committed
    }
    
    void Petrack::keyBindings()
    {
    
        const QString out = tr(
            "<p>Beside the space bar all bindings only affect inside the image.</p>"
            "<dl><dt><kbd>Space bar</kbd></dt><dd>toggles between pause and last play direction</dd>"
    
            "<dt><kbd>Mouse scroll wheel</kbd></dt><dd>zooms in and out to or from the pixel of the image at the position "
            "of the mouse pointer</dd>"
    
            "<dt><kbd>Shift + mouse scroll wheel</kbd></dt><dd>plays forwards or backwards frame by frame</dd>"
            "<dt><kbd>Holding left mouse button</kbd></dt><dd>moves image</dd>"
            "<dt><kbd>Arrows up/down</kbd></dt><dd>zoom in/out</dd>"
            "<dt><kbd>Arrows left/right</kbd></dt><dd>frame back/forward</dd>"
            "<dt><kbd>Double-click left mouse button</kbd></dt><dd>opens video or image sequence</dd>"
            "<dt><kbd>Ctrl + double-click left mouse button</kbd></dt><dd>inserts new or moves near trackpoint</dd>"
    
            "<dt><kbd>Ctrl + Shift + double-click left mouse button</kbd></dt><dd>splits near trackpoint before actual "
            "frame</dd>"
    
            "<dt><kbd>Ctrl + double-click right mouse button</kbd></dt><dd>deletes a trajectory of a near trackpoint</dd>"
    
            "<dt><kbd>Shift + double-click right mouse button</kbd></dt><dd>deletes the past part of a trajectory of a "
            "near trackpoint</dd>"
            "<dt><kbd>Alt + double-click right mouse button</kbd></dt><dd>deletes the future part of a trajectory of a "
            "near trackpoint</dd>"
    
            "<dt><kbd>Ctrl + double-click middle mouse button</kbd></dt><dd>deletes all trajectories</dd>"
            "<dt><kbd>Shift + double-click middle mouse button</kbd></dt><dd>deletes the past part of all trajectories</dd>"
            "<dt><kbd>Alt + double-click middle mouse button</kbd></dt><dd>deletes the future part of all trajectories</dd>"
            "<dt><kbd>Shift + t</kbd></dt><dd>toggles tracking online calculation</dd>"
    
            "<dt><kbd>Shift + double-click left mouse button</kbd></dt><dd>inserts new or moves near trackpoint and "
    
            "enables showing only the modified trajectory</dd>"
            "<dt><kbd>Alt + double-click left mouse button</kbd></dt><dd>jumps to frame of trackpoint under cursor</dd>"
    
            "<dt><kbd>Alt + holding left mouse button</kbd></dt><dd>moves trackpoint under cursor</dd>"
    
            "<dt><kbd>Ctrl + E</kbd></dt><dd>export trajectories</dd>"
            "<dt><kbd>Ctrl + mouse scroll wheel</kbd></dt><dd>change the displayed person (if show only people "
            "enabled)</dd></dl>"
    
            "<p>Further key bindings you will find next to the entries of the menus.</p>");
    
    
        PMessageBox *mb = new PMessageBox(this, tr("Key Bindings"), out, QIcon());
    
    d.kilic's avatar
    d.kilic committed
        mb->setAttribute(Qt::WA_DeleteOnClose);
    
    d.kilic's avatar
    d.kilic committed
    
        mb->show();
    }
    
    void Petrack::onlineHelp()
    {
    
        static QUrl url("https://jugit.fz-juelich.de/ped-dyn-emp/petrack/-/wikis/home");
    
        if(!(QDesktopServices::openUrl(url)))
    
            PCritical(this, tr("PeTrack"), tr("Cannot open external browser<br>with url ") + url.toString() + "!");
    
    d.kilic's avatar
    d.kilic committed
    }
    
    void Petrack::antialias()
    {
        mView->setRenderHint(QPainter::Antialiasing, mAntialiasAct->isChecked());
    }
    void Petrack::opengl()
    {
        mView->setViewport(mOpenGLAct->isChecked() ? new QGLWidget(QGLFormat(QGL::SampleBuffers)) : new QWidget);
    
        // alten freigeben wegen new????
    }
    void Petrack::reset()
    {
        mViewWidget->resetView();
    }
    void Petrack::fitInView()
    {
        mViewWidget->fitInView();
    }
    void Petrack::fitInROI()
    {
        mViewWidget->fitInROI(getRecoRoiItem()->rect());
        // what about trackingRoi???
    }
    void Petrack::setGlobalFont()
    {
    
    d.kilic's avatar
    d.kilic committed
        QFont font = QFontDialog::getFont(&ok, this->font(), this);
    
    d.kilic's avatar
    d.kilic committed
            this->setFont(font); // font is set to the font the user selected
    
    d.kilic's avatar
    d.kilic committed
            // the user canceled the dialog; font is set to the initial
            // value, in this case Times, 12.
        }
    }
    
    void Petrack::showHideControlWidget()
    {
        // show | hide Control
        mViewWidget->hideControls(mHideControlsAct->isChecked());
    }
    
    void Petrack::setCamera()
    {
    
    #ifndef STEREO_DISABLED
    
        if(mAnimation)
    
    d.kilic's avatar
    d.kilic committed
        {
    
            if(mCameraLeftViewAct->isChecked())
    
    d.kilic's avatar
    d.kilic committed
            {
    
                if((mAnimation->getCamera()) != cameraLeft)
    
    d.kilic's avatar
    d.kilic committed
                    mAnimation->setCamera(cameraLeft); // war: hier wird direkt bei Umstellung neu gelesen
                else
                    return;
            }
    
            else if(mCameraRightViewAct->isChecked())
    
    d.kilic's avatar
    d.kilic committed
            {
    
                if((mAnimation->getCamera()) != cameraRight)
    
    d.kilic's avatar
    d.kilic committed
                    mAnimation->setCamera(cameraRight); // war: hier wird direkt bei Umstellung neu gelesen
                else
                    return;
            }
            else // kann eigentlich nicht vorkommen
            {
                mAnimation->setCamera(cameraUnset);
                return;
            }
    
            updateImage(mAnimation->getFrameAtIndex(
                mAnimation->getCurrentFrameNum())); // wird nur aufgerufen, wenn left / right sich geaendert hat
            // mPlayerWidget->updateImage();
            // mPlayerWidget->skipToFrame(mPlayerWidget->getPos()); // machtpasue!!
            // updateImage(true); // nur dies aufrufen, wenn nicht links rechts gleichzeitig gehalten wird
    
    d.kilic's avatar
    d.kilic committed
        }
    
    d.kilic's avatar
    d.kilic committed
    }
    
    
    /**
     * @brief Helper function to create Actions for the menu bar
     * @see Petrack::createMenus()
     */
    
    d.kilic's avatar
    d.kilic committed
    void Petrack::createActions()
    {
        mOpenSeqAct = new QAction(tr("&Open Sequence"), this);
        mOpenSeqAct->setShortcut(tr("Ctrl+Shift+O"));
        connect(mOpenSeqAct, SIGNAL(triggered()), this, SLOT(openSequence()));
    
        mOpenCameraAct = new QAction(tr("Open Camera Stream"), this);
    
        // mOpenCameraAct->setShortcut(tr("Ctrl+C")); // because of some reason it is sometimes fired with
        // Ctrl+LeftMouseButton ==> so disabled (it's also not really needed)
    
    d.kilic's avatar
    d.kilic committed
        connect(mOpenCameraAct, SIGNAL(triggered()), this, SLOT(openCameraLiveStream()));
    
    
        mOpenMoCapAct = new QAction(tr("Manage MoCap Files"), this);
    
        connect(mOpenMoCapAct, &QAction::triggered, this, &Petrack::openMoCapFile);
    
    
    d.kilic's avatar
    d.kilic committed
        mSaveSeqVidAct = new QAction(tr("Save Video"), this);
        mSaveSeqVidAct->setEnabled(false);
        connect(mSaveSeqVidAct, SIGNAL(triggered()), this, SLOT(saveVideo()));
    
        mSaveSeqVidViewAct = new QAction(tr("Save Video View"), this);
        mSaveSeqVidViewAct->setEnabled(false);
        connect(mSaveSeqVidViewAct, SIGNAL(triggered()), this, SLOT(saveVideoView()));
    
        mSaveSeqImgAct = new QAction(tr("Save Image S&equence"), this);
        mSaveSeqImgAct->setShortcut(tr("Ctrl+F"));
        mSaveSeqImgAct->setEnabled(false);
        connect(mSaveSeqImgAct, SIGNAL(triggered()), this, SLOT(saveImageSequence()));
    
        mSaveSeqViewAct = new QAction(tr("Save View S&equence"), this);
        //    mSaveSeqViewAct->setShortcut(tr("Ctrl+F"));
        mSaveSeqViewAct->setEnabled(false);
        connect(mSaveSeqViewAct, SIGNAL(triggered()), this, SLOT(saveViewSequence()));
    
        mOpenPrAct = new QAction(tr("&Open Project"), this);
        mOpenPrAct->setShortcut(tr("Ctrl+O"));
        connect(mOpenPrAct, SIGNAL(triggered()), this, SLOT(openProject()));
    
        mSavePrAct = new QAction(tr("&Save Project As"), this);
        mSavePrAct->setShortcut(tr("Ctrl+Shift+S"));
    
    d.kilic's avatar
    d.kilic committed
        connect(mSavePrAct, &QAction::triggered, this, &Petrack::saveProjectAs);
    
    d.kilic's avatar
    d.kilic committed
    
        mSaveAct = new QAction(tr("&Save Project"), this);
        mSaveAct->setShortcut(tr("Ctrl+S"));
        connect(mSaveAct, SIGNAL(triggered()), this, SLOT(saveSameProject()));
    
        mSaveImageAct = new QAction(tr("&Save Image"), this);
        mSaveImageAct->setShortcut(tr("Ctrl+I"));
        mSaveImageAct->setEnabled(false);
        connect(mSaveImageAct, SIGNAL(triggered()), this, SLOT(saveImage()));
    
        mSaveViewAct = new QAction(tr("&Save View"), this);
        mSaveViewAct->setShortcut(tr("Ctrl+V"));
        mSaveViewAct->setEnabled(false);
        connect(mSaveViewAct, SIGNAL(triggered()), this, SLOT(saveView()));
    
        mPrintAct = new QAction(tr("&Print"), this);
        mPrintAct->setShortcut(tr("Ctrl+P"));
        mPrintAct->setEnabled(false);
        connect(mPrintAct, SIGNAL(triggered()), this, SLOT(print()));
    
        mResetSettingsAct = new QAction(tr("&Reset Settings"), this);
    
        //    mResetSettingsAct->setShortcut(tr("Ctrl+R"));
    
    d.kilic's avatar
    d.kilic committed
        mResetSettingsAct->setEnabled(false); // da es noch nicht fehlerfrei funktioniert
        connect(mResetSettingsAct, SIGNAL(triggered()), this, SLOT(resetSettings()));
    
        mExitAct = new QAction(tr("E&xit"), this);
        mExitAct->setShortcut(tr("Ctrl+Q"));
        connect(mExitAct, SIGNAL(triggered()), this, SLOT(close()));
    
        mAntialiasAct = new QAction(tr("&Antialias"), this);
        mAntialiasAct->setShortcut(tr("Ctrl+A"));
        mAntialiasAct->setCheckable(true);
        connect(mAntialiasAct, SIGNAL(triggered()), this, SLOT(antialias()));
    
        mFontAct = new QAction(tr("&Font"), this);
        connect(mFontAct, SIGNAL(triggered()), this, SLOT(setGlobalFont()));
    
        mHideControlsAct = new QAction(tr("&Hide controls"), this);
        mHideControlsAct->setShortcut(tr("Ctrl+H"));
        mHideControlsAct->setCheckable(true);
        connect(mHideControlsAct, SIGNAL(triggered()), this, SLOT(showHideControlWidget()));
    
        connect(mHideControlsAct, SIGNAL(changed()), this, SLOT(showHideControlWidget()));
    
    d.kilic's avatar
    d.kilic committed
    
    
        mCropZoomViewAct = new QAction(tr("&Transform while saving"), this); // Crop and zoom while saving
    
    d.kilic's avatar
    d.kilic committed
        mCropZoomViewAct->setCheckable(true);
    
        mOpenGLAct = new QAction(tr("Open&GL"), this);
        mOpenGLAct->setShortcut(tr("Ctrl+G"));
        mOpenGLAct->setCheckable(true);
        connect(mOpenGLAct, SIGNAL(triggered()), this, SLOT(opengl()));
    
        mResetAct = new QAction(tr("&Reset"), this);
        mResetAct->setShortcut(tr("Ctrl+R"));
        connect(mResetAct, SIGNAL(triggered()), this, SLOT(reset()));
    
    
        mFitViewAct =
            new QAction(tr("Fit in window"), this); // Resize to window; fit in view; show all; in fenster einpassen
    
    d.kilic's avatar
    d.kilic committed
        mFitViewAct->setShortcut(tr("Ctrl+0"));
        connect(mFitViewAct, SIGNAL(triggered()), this, SLOT(fitInView()));
    
        mFitROIAct = new QAction(tr("Fit in region of interest"), this); // Resize ROI to window; fit in view;
        mFitROIAct->setShortcut(tr("Ctrl+1"));
        connect(mFitROIAct, SIGNAL(triggered()), this, SLOT(fitInROI()));
    
        mCameraGroupView = new QActionGroup(this);
    
        // mCameraGroupView->addAction(mCameraLeftViewAct);
        // mCameraGroupView->addAction(mCameraRightViewAct);
    
    d.kilic's avatar
    d.kilic committed
        mCameraLeftViewAct = new QAction(tr("&Left"), mCameraGroupView);
        mCameraLeftViewAct->setShortcut(tr("Ctrl++Shift+L"));
        mCameraLeftViewAct->setCheckable(true);
        connect(mCameraLeftViewAct, SIGNAL(triggered()), this, SLOT(setCamera()));
        mCameraRightViewAct = new QAction(tr("&Right"), mCameraGroupView);
        mCameraRightViewAct->setShortcut(tr("Ctrl++Shift+R"));
        mCameraRightViewAct->setCheckable(true);
        connect(mCameraRightViewAct, SIGNAL(triggered()), this, SLOT(setCamera()));
    
        mCameraRightViewAct->setChecked(true); // right wird als default genommen, da reference image in triclops auch right
                                               // ist // erste trj wurden mit left gerechnet
    
    d.kilic's avatar
    d.kilic committed
    
    
        mLimitPlaybackSpeed = new QAction(tr("&Limit playback speed"));
    
        // Not checkable like Fix since this is also controlled through clicking on FPS and syncing currently would be
        // bothersome
        connect(
            mLimitPlaybackSpeed,
            &QAction::triggered,
            mPlayerWidget,
            [&]() { mPlayerWidget->setPlayerSpeedLimited(!mPlayerWidget->getPlayerSpeedLimited()); });
    
        mFixPlaybackSpeed = new QAction(tr("&Fix playback speed"));
        mFixPlaybackSpeed->setCheckable(true);
        connect(mFixPlaybackSpeed, &QAction::toggled, mPlayerWidget, &Player::setPlayerSpeedFixed);
        mSetToRealtime = new QAction(tr("&Realtime"));
    
        connect(
            mSetToRealtime, &QAction::triggered, mPlayerWidget, [&]() { mPlayerWidget->setSpeedRelativeToRealtime(1.0); });
    
        mSetTo2p00 = new QAction(tr("&x2"));
    
        connect(mSetTo2p00, &QAction::triggered, mPlayerWidget, [&]() { mPlayerWidget->setSpeedRelativeToRealtime(2.0); });
    
        mSetTo1p75 = new QAction(tr("&x1.75"));
    
        connect(mSetTo1p75, &QAction::triggered, mPlayerWidget, [&]() { mPlayerWidget->setSpeedRelativeToRealtime(1.75); });
    
        mSetTo1p50 = new QAction(tr("&x1.5"));
    
        connect(mSetTo1p50, &QAction::triggered, mPlayerWidget, [&]() { mPlayerWidget->setSpeedRelativeToRealtime(1.5); });
    
        mSetTo1p25 = new QAction(tr("&x1.25"));
    
        connect(mSetTo1p25, &QAction::triggered, mPlayerWidget, [&]() { mPlayerWidget->setSpeedRelativeToRealtime(1.25); });
    
        mSetTo0p75 = new QAction(tr("&x0.75"));
    
        connect(mSetTo0p75, &QAction::triggered, mPlayerWidget, [&]() { mPlayerWidget->setSpeedRelativeToRealtime(0.75); });
    
        mSetTo0p50 = new QAction(tr("&x0.5"));
    
        connect(mSetTo0p50, &QAction::triggered, mPlayerWidget, [&]() { mPlayerWidget->setSpeedRelativeToRealtime(0.5); });
    
        mSetTo0p25 = new QAction(tr("&x0.25"));
    
        connect(mSetTo0p25, &QAction::triggered, mPlayerWidget, [&]() { mPlayerWidget->setSpeedRelativeToRealtime(0.25); });
    
    d.kilic's avatar
    d.kilic committed
    
        mPlayerLooping = new QAction(tr("&Loop"));
        mPlayerLooping->setCheckable(true);
        connect(mPlayerLooping, &QAction::triggered, mPlayerWidget, &Player::setLooping);
    
    d.kilic's avatar
    d.kilic committed
        // -------------------------------------------------------------------------------------------------------
    
    
        mDelPastAct = new QAction(tr("&Past part of all trj."), this);
    
        connect(
            mDelPastAct,
            &QAction::triggered,
            this,
            [this]() { this->deleteTrackPointAll(PersonStorage::Direction::Previous); });
    
    d.kilic's avatar
    d.kilic committed
    
        mDelFutureAct = new QAction(tr("&Future part of all trj."), this);
    
        connect(
            mDelFutureAct,
            &QAction::triggered,
            this,
            [this]() { this->deleteTrackPointAll(PersonStorage::Direction::Following); });
    
    d.kilic's avatar
    d.kilic committed
    
        mDelAllRoiAct = new QAction(tr("&Trj. moving through ROI"), this);
    
        connect(mDelAllRoiAct, &QAction::triggered, this, &Petrack::deleteTrackPointROI);
    
    d.kilic's avatar
    d.kilic committed
        mDelPartRoiAct = new QAction(tr("Part of Trj. inside &ROI"), this);
    
        connect(mDelPartRoiAct, &QAction::triggered, this, &Petrack::deleteTrackPointInsideROI);
    
    d.kilic's avatar
    d.kilic committed
    
        // -------------------------------------------------------------------------------------------------------
    
        mCommandAct = new QAction(tr("&Command line options"), this);
        connect(mCommandAct, SIGNAL(triggered()), this, SLOT(commandLineOptions()));
    
        mKeyAct = new QAction(tr("&Key bindings"), this);
        connect(mKeyAct, SIGNAL(triggered()), this, SLOT(keyBindings()));
    
        mAboutAct = new QAction(tr("&About"), this);
        connect(mAboutAct, SIGNAL(triggered()), this, SLOT(about()));
    
        mOnlineHelpAct = new QAction(tr("Online &Help"), this);
        mOnlineHelpAct->setShortcut(tr("Ctrl+H"));
        connect(mOnlineHelpAct, SIGNAL(triggered()), this, SLOT(onlineHelp()));
    }
    
    
    /**
     * @brief Helper function building menues out of QActions
     * @see Petrack::createActions()
     */
    
    d.kilic's avatar
    d.kilic committed
    void Petrack::createMenus()
    {
        mFileMenu = new QMenu(tr("&File"), this);
        mFileMenu->addAction(mOpenPrAct);
        mFileMenu->addAction(mSaveAct);
        mFileMenu->addAction(mSavePrAct);
        mFileMenu->addSeparator();
        mFileMenu->addAction(mOpenSeqAct);
        mFileMenu->addAction(mOpenCameraAct);
    
        mFileMenu->addAction(mOpenMoCapAct);
    
    d.kilic's avatar
    d.kilic committed
        mFileMenu->addAction(mSaveSeqVidAct);
        mFileMenu->addAction(mSaveSeqVidViewAct);
        mFileMenu->addAction(mSaveImageAct);
        mFileMenu->addAction(mSaveSeqImgAct);
        mFileMenu->addAction(mSaveViewAct);
        mFileMenu->addAction(mSaveSeqViewAct);
        mFileMenu->addAction(mPrintAct);
        mFileMenu->addSeparator();
        mFileMenu->addAction(mResetSettingsAct);
        mFileMenu->addSeparator();
        mFileMenu->addAction(mExitAct);
    
        mViewMenu = new QMenu(tr("&View"), this);
        mViewMenu->addAction(mAntialiasAct);
        mViewMenu->addAction(mOpenGLAct);
        mViewMenu->addAction(mCropZoomViewAct);
        mCameraMenu = mViewMenu->addMenu(tr("&Camera"));
        mCameraMenu->addAction(mCameraLeftViewAct);
        mCameraMenu->addAction(mCameraRightViewAct);
    
        mViewMenu->addAction(mFixPlaybackSpeed);
        mViewMenu->addAction(mLimitPlaybackSpeed);
        mPlaybackSpeedMenu = mViewMenu->addMenu(tr("&Playback speed"));
        mPlaybackSpeedMenu->addAction(mSetToRealtime);
        mPlaybackSpeedMenu->addAction(mSetTo2p00);
        mPlaybackSpeedMenu->addAction(mSetTo1p75);
        mPlaybackSpeedMenu->addAction(mSetTo1p50);
        mPlaybackSpeedMenu->addAction(mSetTo1p25);
        mPlaybackSpeedMenu->addAction(mSetTo0p75);
        mPlaybackSpeedMenu->addAction(mSetTo0p50);
        mPlaybackSpeedMenu->addAction(mSetTo0p25);
    
    d.kilic's avatar
    d.kilic committed
        mViewMenu->addAction(mPlayerLooping);
    
    d.kilic's avatar
    d.kilic committed
        mViewMenu->addSeparator();
        mViewMenu->addAction(mFitViewAct);
        mViewMenu->addAction(mFitROIAct);
        mViewMenu->addAction(mResetAct);
        mViewMenu->addSeparator();
        mViewMenu->addAction(mFontAct);
        mViewMenu->addSeparator();
        mViewMenu->addAction(mHideControlsAct);
    
        mDeleteMenu = new QMenu(tr("&Delete"), this);
        mDeleteMenu->addAction(mDelPastAct);
        mDeleteMenu->addAction(mDelFutureAct);
        mDeleteMenu->addAction(mDelAllRoiAct);
        mDeleteMenu->addAction(mDelPartRoiAct);
    
        mHelpMenu = new QMenu(tr("&Help"), this);
        mHelpMenu->addAction(mCommandAct);
        mHelpMenu->addAction(mKeyAct);
        mHelpMenu->addAction(mAboutAct);
        mHelpMenu->addAction(mOnlineHelpAct);
    
        menuBar()->addMenu(mFileMenu);
        menuBar()->addMenu(mViewMenu);
        menuBar()->addMenu(mDeleteMenu);