/*========================================================================= medInria Copyright (c) INRIA 2013 - 2020. All rights reserved. See LICENSE.txt for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. =========================================================================*/ #include #include // floor #include #include #include #include #include #include #include class medTimeLineParameterLPrivate { public: QWidget* widget; medDoubleParameterL* timeParameter; medIntParameterL* speedFactorParameter; medBoolParameterL* playParameter; medTriggerParameterL* nextFrameParameter; medTriggerParameterL* previousFrameParameter; medBoolParameterL *loopParameter; // Time shift display medBoolParameterL *displayTimeParameter; medDoubleParameterL *timeShiftParameter; medComboBox *extensionShiftParameter; QString currentDisplayedTime; QTimeLine *timeLine; QPointer frameLabel; QPointer numberOfFramesLabel; unsigned int numberOfFrames; int currentFrame; double duration; double timeBetweenFrames; unsigned int stepFrame; bool timeLineLocked; QList parametersCandidateToPool; ~medTimeLineParameterLPrivate() { if(widget) delete widget; if(frameLabel) delete frameLabel; if(numberOfFramesLabel) delete numberOfFramesLabel; } }; medTimeLineParameterL::medTimeLineParameterL(QString name, QObject *parent): medAbstractGroupParameterL(name, parent), d(new medTimeLineParameterLPrivate) { d->widget = nullptr; d->frameLabel = nullptr; d->numberOfFramesLabel = nullptr; d->speedFactorParameter = new medIntParameterL("Speed", this); d->speedFactorParameter->setToolTip("Speed factor of data display"); d->speedFactorParameter->setRange(1,5000); d->speedFactorParameter->setValue(100); d->playParameter = new medBoolParameterL("Play", this); d->playParameter->setToolTip("Play"); d->parametersCandidateToPool << d->playParameter; d->timeParameter = new medDoubleParameterL("Time", this); d->timeParameter->setValue(0); d->parametersCandidateToPool << d->timeParameter; d->nextFrameParameter = new medTriggerParameterL("NextFrame", this); d->nextFrameParameter->setToolTip("Move to the next frame"); d->previousFrameParameter = new medTriggerParameterL("PreviousFrame", this); d->previousFrameParameter->setToolTip("Go back to the previous frame"); d->timeLine = new QTimeLine(7000, this); d->timeLine->setCurveShape (QTimeLine::LinearCurve); // Choose if the time display is going to loop d->loopParameter = new medBoolParameterL("Loop", this); d->parametersCandidateToPool << d->loopParameter; d->loopParameter->setText("Loop"); d->loopParameter->setToolTip("Choose if display loops or not"); // Time shift display d->displayTimeParameter = new medBoolParameterL("Display Time", this); d->displayTimeParameter->setText(""); d->displayTimeParameter->setToolTip("Choose if time is displayed in the view"); d->displayTimeParameter->setValue(true); d->timeShiftParameter = new medDoubleParameterL("Display time with shift:", this); d->timeShiftParameter->setToolTip("Add a duration in displayed time on view"); d->timeShiftParameter->setRange(0, 500000); d->timeShiftParameter->setValue(0.0); d->extensionShiftParameter = new medComboBox; d->extensionShiftParameter->setToolTip("Duration extension"); d->extensionShiftParameter->addItem(QString("ยต")+"s", 0); d->extensionShiftParameter->addItem("ms", 1); d->extensionShiftParameter->addItem("s", 2); d->extensionShiftParameter->addItem("min", 3); d->extensionShiftParameter->addItem("h", 4); d->extensionShiftParameter->addItem("d", 5); d->extensionShiftParameter->setCurrentIndex(2); connect(d->displayTimeParameter, SIGNAL(valueChanged(bool)), this, SLOT(computeDisplayedTime())); connect(d->timeShiftParameter, SIGNAL(valueChanged(double)), this, SLOT(computeDisplayedTime())); connect(d->extensionShiftParameter, SIGNAL(currentIndexChanged(int)), this, SLOT(computeDisplayedTime())); // Default values d->duration = 1; d->currentFrame = 0; d->timeBetweenFrames = 0; d->numberOfFrames = 0; d->timeLineLocked = false; this->clear(); connect(d->timeLine, SIGNAL(frameChanged(int)), this, SLOT(setFrame(int))); connect(d->timeLine, SIGNAL(finished()), this, SLOT(reset())); connect(d->playParameter, SIGNAL(valueChanged(bool)), this, SLOT(play(bool))); connect(d->previousFrameParameter, SIGNAL(triggered()), this, SLOT(previousFrame())); connect(d->nextFrameParameter, SIGNAL(triggered()), this, SLOT(nextFrame())); connect(d->speedFactorParameter, SIGNAL(valueChanged(int)), this, SLOT(setSpeedFactor(int))); connect(d->timeParameter, SIGNAL(valueChanged(double)), this, SLOT(updateTime(double))); connect(d->loopParameter, SIGNAL(valueChanged(bool)), this, SLOT(setLoop(bool))); connect(d->timeParameter, SIGNAL(valueChanged(double)), this, SLOT(updateFrameLabel())); d->loopParameter->setValue(true); computeDisplayedTime(); } medTimeLineParameterL::~medTimeLineParameterL() { emit aboutToBeDestroyed(); delete d; } void medTimeLineParameterL::clear() { this->setSpeedFactor(1); this->setDuration(1); this->setNumberOfFrame(1); this->setFrame(0); this->setStepFrame(1); this->play(false); d->timeBetweenFrames = 0.0; } int medTimeLineParameterL::speedFactor() const { return d->speedFactorParameter->value(); } bool medTimeLineParameterL::isPlaying() const { return d->playParameter->value(); } int medTimeLineParameterL::numberOfFrame() const { return d->numberOfFrames; } int medTimeLineParameterL::frame() const { return d->currentFrame; } double& medTimeLineParameterL::duration() const { return d->duration; } int medTimeLineParameterL::stepFrame() const { return d->stepFrame; } unsigned int medTimeLineParameterL::mapTimeToFrame(const double &time) { return floor( (double)(time / d->timeBetweenFrames) + 0.5 ); } double medTimeLineParameterL::mapFrameToTime (int frame) { return frame * d->timeBetweenFrames; } void medTimeLineParameterL::setSpeedFactor(int speedFactor) { d->timeLine->setDuration((d->duration)*(1000/(speedFactor/100.0))); } void medTimeLineParameterL::unlockTimeLine() { d->timeLineLocked = false; } void medTimeLineParameterL::lockTimeLine() { d->timeLineLocked = true; } void medTimeLineParameterL::play(bool play) { d->playParameter->setValue(play); if((d->timeLine->state() == QTimeLine::Paused || d->timeLine->state() == QTimeLine::NotRunning) && play) { d->timeLine->setCurrentTime(mapFrameToTime(d->currentFrame) * 1000); d->timeLine->resume(); d->playParameter->setIcon(QIcon::fromTheme("time_pause")); emit playing(play); } else if(d->timeLine->state() == QTimeLine::Running && !play) { d->timeLine->setPaused(true); d->playParameter->setIcon(QIcon::fromTheme("time_play")); } this->lockTimeLine(); } void medTimeLineParameterL::reset() { d->playParameter->setValue(false); d->playParameter->setIcon(QIcon::fromTheme("time_play")); } void medTimeLineParameterL::setNumberOfFrame(int numberOfFrame) { d->numberOfFrames = numberOfFrame; if((d->duration != 0)&&(d->numberOfFrames > 1)) { d->timeBetweenFrames = d->duration / (d->numberOfFrames - 1); d->timeParameter->setSingleStep(d->timeBetweenFrames); } else d->timeBetweenFrames = 0; d->timeLine->setFrameRange(0, d->numberOfFrames); updateNumberOfFrameLabel(); } void medTimeLineParameterL::setStepFrame(int stepFrame) { d->stepFrame = stepFrame; } void medTimeLineParameterL::setDuration(const double& timeDuration) { d->duration = timeDuration; d->timeParameter->setRange(0, d->duration); if((d->duration != 0)&&(d->numberOfFrames > 1)) { d->timeBetweenFrames = d->duration / (d->numberOfFrames - 1); d->timeParameter->setSingleStep(d->timeBetweenFrames); } else d->timeBetweenFrames = 0; d->timeLine->setDuration(timeDuration*1000); } void medTimeLineParameterL::setFrame(int frame) { double time = this->mapFrameToTime(frame); if(!d->timeLineLocked) d->timeParameter->setValue(time); } void medTimeLineParameterL::setTime(const double &time) { d->timeParameter->setValue(time); } void medTimeLineParameterL::updateTime(double time) { unsigned int frame = this->mapTimeToFrame(time); if(frame > d->numberOfFrames) d->currentFrame = d->numberOfFrames; else d->currentFrame = frame; computeDisplayedTime(); emit timeChanged(time); } double medTimeLineParameterL::time() const { return d->timeParameter->value(); } void medTimeLineParameterL::previousFrame() { this->unlockTimeLine(); int tempFrame = this->frame(); this->setFrame( tempFrame - d->stepFrame ); } void medTimeLineParameterL::nextFrame() { this->unlockTimeLine(); int tempFrame = this->frame(); this->setFrame( tempFrame + d->stepFrame ); } void medTimeLineParameterL::setLoop(bool loop) { int loopCount = (loop == true)?0:1; d->timeLine->setLoopCount(loopCount); } void medTimeLineParameterL::computeDisplayedTime() { if (d->displayTimeParameter->getCheckBox()->isChecked()) { double displayedTimeValue = time() + d->timeShiftParameter->getSpinBox()->value(); // Ex. "Time = 66.6 ms" d->currentDisplayedTime = QString("Time = ") + QString::number(displayedTimeValue) + " " + d->extensionShiftParameter->currentText(); } else { d->currentDisplayedTime = QString(""); } } QString medTimeLineParameterL::getDisplayedTime() { return d->currentDisplayedTime; } QList medTimeLineParameterL::parametersCandidateToPool() const { return d->parametersCandidateToPool; } QWidget* medTimeLineParameterL::getWidget() { if(!d->widget) { d->widget = new QWidget; QVBoxLayout *widgetLayout = new QVBoxLayout(d->widget); QHBoxLayout *buttonsLayout = new QHBoxLayout; QHBoxLayout *indicatorLayout = new QHBoxLayout; d->playParameter->setIcon(QIcon::fromTheme("time_play")); connect(d->playParameter->getPushButton(), SIGNAL(clicked()), this, SLOT(unlockTimeLine())); d->previousFrameParameter->getPushButton()->setIcon(QIcon::fromTheme("time_backward_step")); d->nextFrameParameter->getPushButton()->setIcon(QIcon::fromTheme("time_forward_step")); d->speedFactorParameter->getSpinBox()->setSingleStep(10); d->timeParameter->getSlider()->setOrientation(Qt::Horizontal); buttonsLayout->addWidget(d->previousFrameParameter->getPushButton()); buttonsLayout->addWidget(d->playParameter->getPushButton()); buttonsLayout->addWidget(d->nextFrameParameter->getPushButton()); buttonsLayout->addWidget(d->speedFactorParameter->getLabel()); buttonsLayout->addWidget(d->speedFactorParameter->getSpinBox()); buttonsLayout->addWidget(d->loopParameter->getCheckBox()); if(d->frameLabel.isNull()) { d->frameLabel = new QLabel; this->updateFrameLabel(); connect(d->timeParameter, SIGNAL(valueChanged(double)), this, SLOT(updateFrameLabel())); } if(d->numberOfFramesLabel.isNull()) { d->numberOfFramesLabel = new QLabel; this->updateNumberOfFrameLabel(); } indicatorLayout->addWidget(d->timeParameter->getValueLabel(), 0, Qt::AlignLeft); indicatorLayout->addWidget(new QLabel("/ " + QString::number(d->duration,'g',2) + " s"), 0, Qt::AlignLeft); indicatorLayout->addStretch(1); indicatorLayout->addWidget(d->frameLabel, 0, Qt::AlignRight); indicatorLayout->addWidget(d->numberOfFramesLabel, 0, Qt::AlignRight); // Time Shift QHBoxLayout *shiftLayout = new QHBoxLayout; shiftLayout->setAlignment(Qt::AlignLeft); shiftLayout->addWidget(d->displayTimeParameter->getCheckBox()); shiftLayout->addWidget(d->timeShiftParameter->getLabel()); shiftLayout->addWidget(d->timeShiftParameter->getSpinBox()); shiftLayout->addWidget(d->extensionShiftParameter); widgetLayout->addLayout(buttonsLayout); widgetLayout->addWidget(d->timeParameter->getSlider()); widgetLayout->addLayout(indicatorLayout); widgetLayout->addLayout(shiftLayout); this->addToInternWidgets(d->widget); connect(d->widget, SIGNAL(destroyed()), this, SLOT(removeInternWidget())); } return d->widget; } void medTimeLineParameterL::updateFrameLabel() { if(d->frameLabel.isNull()) return; d->frameLabel->setText(QString::number(d->currentFrame + 1)); } void medTimeLineParameterL::updateNumberOfFrameLabel() { if(d->numberOfFramesLabel.isNull()) return; d->numberOfFramesLabel->setText("/ " + QString::number(d->numberOfFrames) + " frames"); } void medTimeLineParameterL::removeInternWidget() { this->removeFromInternWidgets(d->widget); d->widget = nullptr; } void medTimeLineParameterL::trigger() { d->timeParameter->trigger(); }