Skip to content
Snippets Groups Projects
petrack.cpp 145 KiB
Newer Older
d.kilic's avatar
d.kilic committed
        updateSequence();
        updateWindowTitle();
        mPlayerWidget->setFPS(mAnimation->getFPS());
        if(mOpenGLAct->isChecked())
d.kilic's avatar
d.kilic committed
            mLogoItem->fadeOut(1);
d.kilic's avatar
d.kilic committed
        else
d.kilic's avatar
d.kilic committed
            mLogoItem->fadeOut(50);
d.kilic's avatar
d.kilic committed
        updateCoord();
        mMissingFrames.reset();
d.kilic's avatar
d.kilic committed
    }
}

void Petrack::openMoCapFile()
{
    OpenMoCapDialog dialog(this, mMoCapController);
    dialog.exec();
}

d.kilic's avatar
d.kilic committed
void Petrack::updateWindowTitle()
{
    QString title;
    QSize   size = mAnimation->getSize();
d.kilic's avatar
d.kilic committed

    if(QFileInfo(mProFileName).isDir())
        title = tr("PeTrack (v") + mPetrackVersion + tr("): ");
d.kilic's avatar
d.kilic committed
    else
    {
        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;

    // 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 (*.mp4 *.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
        }
    }
    auto extension = dest.right(4);

    int fourcc = -1;
    if(extension == ".mp4")
d.kilic's avatar
d.kilic committed
    {
        fourcc    = cv::VideoWriter::fourcc('m', 'p', '4', 'v');
        saveVideo = true;
    }
    else if(extension == ".avi")
    {
        fourcc    = cv::VideoWriter::fourcc('M', 'J', 'P', 'G');
        saveVideo = true;
    }
    else
    {
        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         = "";
        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;
        cv::VideoWriter outputVideo;
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();
            }

                outputVideo = cv::VideoWriter(
                    dest.toStdString(),
                    fourcc,
                    mAnimation->getFPS(),
                    cv::Size(viewImage->width(), viewImage->height()));
d.kilic's avatar
d.kilic committed
            else
                bool colored = (mImg.channels() > 1);
                outputVideo  = cv::VideoWriter(
                    dest.toStdString(), fourcc, mAnimation->getFPS(), cv::Size(mImg.cols, mImg.rows), colored);
d.kilic's avatar
d.kilic committed
            }
        }

        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(saveView)
d.kilic's avatar
d.kilic committed
                {
                    cv::Mat frame(
                        viewImage->height(),
                        viewImage->width(),
                        CV_8UC4,
                        (unsigned char *) viewImage->bits(),
                        viewImage->bytesPerLine());
                    cv::cvtColor(frame, frame, cv::COLOR_RGBA2RGB); // need for right image interpretation
                    outputVideo.write(frame);
                    writeFrameRet = true;
d.kilic's avatar
d.kilic committed
                }
                else
                {
                    cv::Mat frame = mImg.clone();
                    outputVideo.write(frame);
                    writeFrameRet = true;
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
        SPDLOG_INFO("wrote {} of {} frames.", mPlayerWidget->getPos() + 1 - memPos, mAnimation->getNumFrames());
d.kilic's avatar
d.kilic committed
        progress.setValue(progEnd);

d.kilic's avatar
d.kilic committed
        {
            outputVideo.release();
d.kilic's avatar
d.kilic committed
        }

        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()
{
    QString ctrlSign   = (mCompileOS != "Darwin") ? "⌃ Ctrl" : "⌘ Cmd";
    QString shiftSign  = "⇧ Shift";
    QString altSign    = (mCompileOS != "Darwin") ? "⎇ Alt" : "⌥ Option";
    QString arrowUp    = "Arrow up ↑";
    QString arrowDown  = "Arrow down ↓";
    QString arrowLeft  = "Arrow left ←";
    QString arrowRight = "Arrow right →";

    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>%2 + 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>%4/%5</kbd></dt><dd>zoom in/out</dd>"
           "<dt><kbd>%6/%7</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>%1 + double-click left mouse button</kbd></dt><dd>inserts new or moves near trackpoint</dd>"
           "<dt><kbd>%1 + %2 + double-click left mouse button</kbd></dt><dd>splits near trackpoint before current "
           "frame</dd>"
           "<dt><kbd>%1 + double-click right mouse button</kbd></dt><dd>deletes a trajectory of a near trackpoint</dd>"
           "<dt><kbd>%2 + double-click right mouse button</kbd></dt><dd>deletes the past part of a trajectory of a "
           "near trackpoint</dd>"
           "<dt><kbd>%3 + double-click right mouse button</kbd></dt><dd>deletes the future part of a trajectory of a "
           "near trackpoint</dd>"
           "<dt><kbd>%1 + double-click middle mouse button</kbd></dt><dd>deletes all trajectories</dd>"
           "<dt><kbd>%2 + double-click middle mouse button</kbd></dt><dd>deletes the past part of all trajectories</dd>"
           "<dt><kbd>%3 + double-click middle mouse button</kbd></dt><dd>deletes the future part of all "
           "trajectories</dd>"
           "<dt><kbd>%2 + t</kbd></dt><dd>toggles tracking online calculation</dd>"
           "<dt><kbd>%2 + double-click left mouse button</kbd></dt><dd>inserts new or moves near trackpoint and "
           "enables showing only the modified trajectory</dd>"
           "<dt><kbd>%1 + %3 + double-click left mouse button</kbd></dt><dd>jumps to frame of trackpoint under "
           "cursor</dd>"
           "<dt><kbd>%3 + holding left mouse button</kbd></dt><dd>moves trackpoint under cursor</dd>"
           "<dt><kbd>%1 + e</kbd></dt><dd>export trajectories</dd>"
           "<dt><kbd>%1 + 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>")
            .arg(ctrlSign)
            .arg(shiftSign)
            .arg(altSign)
            .arg(arrowUp)
            .arg(arrowDown)
            .arg(arrowLeft)
            .arg(arrowRight);

    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();