bool KnobGui::isSecretRecursive() const { // If the Knob is within a group, only show it if the group is unfolded! // To test it: // try TuttlePinning: fold all groups, then switch from perspective to affine to perspective. // VISIBILITY is different from SECRETNESS. The code considers that both things are equivalent, which is wrong. // Of course, this check has to be *recursive* (in case the group is within a folded group) KnobPtr knob = getKnob(); bool isViewerKnob = _imp->container->isInViewerUIKnob(); bool showit = isViewerKnob ? !knob->getInViewerContextSecret() : !knob->getIsSecret(); KnobPtr parentKnob = knob->getParentKnob(); KnobGroup* parentIsGroup = dynamic_cast<KnobGroup*>( parentKnob.get() ); while (showit && parentKnob && parentIsGroup) { KnobGuiGroup* parentGui = dynamic_cast<KnobGuiGroup*>( _imp->container->getKnobGui(parentKnob).get() ); // check for secretness and visibility of the group bool parentSecret = isViewerKnob ? parentKnob->getInViewerContextSecret() : parentKnob->getIsSecret(); if ( parentSecret || ( parentGui && !parentGui->isChecked() ) ) { showit = false; // one of the including groups is folder, so this item is hidden } // prepare for next loop iteration parentKnob = parentKnob->getParentKnob(); parentIsGroup = dynamic_cast<KnobGroup*>( parentKnob.get() ); } return !showit; }
void KnobGui::onSetKeyActionTriggered() { QAction* action = qobject_cast<QAction*>( sender() ); assert(action); int dim = action->data().toInt(); KnobPtr knob = getKnob(); assert( knob->getHolder()->getApp() ); //get the current time on the global timeline SequenceTime time = knob->getHolder()->getApp()->getTimeLine()->currentFrame(); AddKeysCommand::KeysToAddList toAdd; KnobGuiPtr thisShared = shared_from_this(); for (int i = 0; i < knob->getDimension(); ++i) { if ( (dim == -1) || (i == dim) ) { std::list<boost::shared_ptr<CurveGui> > curves = getGui()->getCurveEditor()->findCurve(thisShared, i); for (std::list<boost::shared_ptr<CurveGui> >::iterator it = curves.begin(); it != curves.end(); ++it) { AddKeysCommand::KeyToAdd keyToAdd; KeyFrame kf; kf.setTime(time); Knob<int>* isInt = dynamic_cast<Knob<int>*>( knob.get() ); Knob<bool>* isBool = dynamic_cast<Knob<bool>*>( knob.get() ); AnimatingKnobStringHelper* isString = dynamic_cast<AnimatingKnobStringHelper*>( knob.get() ); Knob<double>* isDouble = dynamic_cast<Knob<double>*>( knob.get() ); if (isInt) { kf.setValue( isInt->getValue(i) ); } else if (isBool) { kf.setValue( isBool->getValue(i) ); } else if (isDouble) { kf.setValue( isDouble->getValue(i) ); } else if (isString) { std::string v = isString->getValue(i); double dv; isString->stringToKeyFrameValue(time, ViewIdx(0), v, &dv); kf.setValue(dv); } keyToAdd.keyframes.push_back(kf); keyToAdd.curveUI = *it; keyToAdd.knobUI = thisShared; keyToAdd.dimension = i; toAdd.push_back(keyToAdd); } } } pushUndoCommand( new AddKeysCommand(getGui()->getCurveEditor()->getCurveWidget(), toAdd) ); }
void KnobGui::resetDefault(int dimension) { KnobPtr knob = getKnob(); KnobButton* isBtn = dynamic_cast<KnobButton*>( knob.get() ); KnobPage* isPage = dynamic_cast<KnobPage*>( knob.get() ); KnobGroup* isGroup = dynamic_cast<KnobGroup*>( knob.get() ); KnobSeparator* isSeparator = dynamic_cast<KnobSeparator*>( knob.get() ); if (!isBtn && !isPage && !isGroup && !isSeparator) { std::list<KnobPtr > knobs; knobs.push_back(knob); pushUndoCommand( new RestoreDefaultsCommand(false, knobs, dimension) ); } }
void KnobGui::pasteClipBoard(int targetDimension) { KnobPtr knob = getKnob(); if (!knob) { return; } //the dimension from which it was copied from int cbDim; KnobClipBoardType type; KnobPtr fromKnob; appPTR->getKnobClipBoard(&type, &fromKnob, &cbDim); if (!fromKnob) { return; } if ( (targetDimension == 0) && !getAllDimensionsVisible() ) { targetDimension = -1; } if ( !knob->isAnimationEnabled() && (type == eKnobClipBoardTypeCopyAnim) ) { Dialogs::errorDialog( tr("Paste").toStdString(), tr("This parameter does not support animation").toStdString() ); return; } if ( !KnobI::areTypesCompatibleForSlave( fromKnob.get(), knob.get() ) ) { Dialogs::errorDialog( tr("Paste").toStdString(), tr("You can only copy/paste between parameters of the same type. To overcome this, use an expression instead.").toStdString() ); return; } if ( (cbDim != -1) && (targetDimension == -1) ) { Dialogs::errorDialog( tr("Paste").toStdString(), tr("When copy/pasting on all dimensions, original and target parameters must have the same dimension.").toStdString() ); return; } if ( ( (targetDimension == -1) || (cbDim == -1) ) && ( fromKnob->getDimension() != knob->getDimension() ) ) { Dialogs::errorDialog( tr("Paste").toStdString(), tr("When copy/pasting on all dimensions, original and target parameters must have the same dimension.").toStdString() ); return; } pushUndoCommand( new PasteUndoCommand(shared_from_this(), type, cbDim, targetDimension, fromKnob) ); } // pasteClipBoard
void KnobGui::onLabelChanged() { if (_imp->descriptionLabel) { KnobPtr knob = getKnob(); if (!knob) { return; } const std::string& iconLabel = knob->getIconLabel(); if (!iconLabel.empty()) { return; } std::string descriptionLabel; KnobString* isStringKnob = dynamic_cast<KnobString*>(knob.get()); bool isLabelKnob = isStringKnob && isStringKnob->isLabel(); if (isLabelKnob) { descriptionLabel = isStringKnob->getValue(); } else { descriptionLabel = knob->getLabel(); } _imp->descriptionLabel->setText_overload(QString::fromUtf8(descriptionLabel.c_str())); onLabelChangedInternal(); } }
void KnobSerialization::restoreTracks(const KnobPtr & knob, const NodesList & allNodes) { KnobDouble* isDouble = dynamic_cast<KnobDouble*>( knob.get() ); if ( isDouble && (isDouble->getName() == "center") && (isDouble->getDimension() == 2) ) { isDouble->restoreTracks(slavedTracks,allNodes); } }
void KnobGui::onSetValueUsingUndoStack(const Variant & v, ViewSpec /*view*/, int dim) { KnobPtr knob = getKnob(); Knob<int>* isInt = dynamic_cast<Knob<int>*>( knob.get() ); Knob<bool>* isBool = dynamic_cast<Knob<bool>*>( knob.get() ); Knob<double>* isDouble = dynamic_cast<Knob<double>*>( knob.get() ); Knob<std::string>* isString = dynamic_cast<Knob<std::string>*>( knob.get() ); if (isInt) { pushUndoCommand( new KnobUndoCommand<int>(shared_from_this(), isInt->getValue(dim), v.toInt(), dim) ); } else if (isBool) { pushUndoCommand( new KnobUndoCommand<bool>(shared_from_this(), isBool->getValue(dim), v.toBool(), dim) ); } else if (isDouble) { pushUndoCommand( new KnobUndoCommand<double>(shared_from_this(), isDouble->getValue(dim), v.toDouble(), dim) ); } else if (isString) { pushUndoCommand( new KnobUndoCommand<std::string>(shared_from_this(), isString->getValue(dim), v.toString().toStdString(), dim) ); } }
void PythonPanelSerialization::initialize(NATRON_PYTHON_NAMESPACE::PyPanel* tab, const std::string& func) { name = tab->getLabel(); pythonFunction = func; std::list<NATRON_PYTHON_NAMESPACE::Param*> parameters = tab->getParams(); for (std::list<NATRON_PYTHON_NAMESPACE::Param*>::iterator it = parameters.begin(); it != parameters.end(); ++it) { KnobPtr knob = (*it)->getInternalKnob(); KnobGroup* isGroup = dynamic_cast<KnobGroup*>( knob.get() ); KnobPage* isPage = dynamic_cast<KnobPage*>( knob.get() ); KnobButton* isButton = dynamic_cast<KnobButton*>( knob.get() ); //KnobChoice* isChoice = dynamic_cast<KnobChoice*>( knob.get() ); if (!isGroup && !isPage && !isButton) { boost::shared_ptr<KnobSerialization> k( new KnobSerialization(knob) ); knobs.push_back(k); } delete *it; } userData = tab->save_serialization_thread().toStdString(); }
void RestoreDefaultsCommand::undo() { assert( _clones.size() == _knobs.size() ); std::list<SequenceTime> times; KnobPtr first = _knobs.front().lock(); AppInstance* app = first->getHolder()->getApp(); assert(app); std::list<KnobWPtr >::const_iterator itClone = _clones.begin(); for (std::list<KnobWPtr >::const_iterator it = _knobs.begin(); it != _knobs.end(); ++it, ++itClone) { KnobPtr itKnob = it->lock(); if (!itKnob) { continue; } KnobPtr itCloneKnob = itClone->lock(); if (!itCloneKnob) { continue; } itKnob->cloneAndUpdateGui( itCloneKnob.get() ); if ( itKnob->getHolder()->getApp() ) { int dim = itKnob->getDimension(); for (int i = 0; i < dim; ++i) { if ( (i == _targetDim) || (_targetDim == -1) ) { boost::shared_ptr<Curve> c = itKnob->getCurve(ViewIdx(0), i); if (c) { KeyFrameSet kfs = c->getKeyFrames_mt_safe(); for (KeyFrameSet::iterator it = kfs.begin(); it != kfs.end(); ++it) { times.push_back( std::floor(it->getTime() + 0.5) ); } } } } } } app->addMultipleKeyframeIndicatorsAdded(times, true); first->getHolder()->incrHashAndEvaluate(true, true); if ( first->getHolder()->getApp() ) { first->getHolder()->getApp()->redrawAllViewers(); } setText( tr("Restore default value(s)") ); }
///High level test: render 1 frame of dot generator TEST_F(BaseTest, GenerateDot) { ///create the generator NodePtr generator = createNode(_generatorPluginID); ///create the writer and set its output filename NodePtr writer = createNode(_writeOIIOPluginID); ASSERT_TRUE(generator && writer); KnobPtr frameRange = generator->getApp()->getProject()->getKnobByName("frameRange"); ASSERT_TRUE(frameRange); KnobInt* knob = dynamic_cast<KnobInt*>( frameRange.get() ); ASSERT_TRUE(knob); knob->setValue(1, ViewSpec::all(), 0); knob->setValue(1, ViewSpec::all(), 1); Format f(0, 0, 200, 200, "toto", 1.); generator->getApp()->getProject()->setOrAddProjectFormat(f); const QString& binPath = appPTR->getApplicationBinaryPath(); QString filePath = binPath + QString::fromUtf8("/test_dot_generator.jpg"); writer->setOutputFilesForWriter( filePath.toStdString() ); ///attempt to connect the 2 nodes together connectNodes(generator, writer, 0, true); ///and start rendering. This call is blocking. std::list<AppInstance::RenderWork> works; AppInstance::RenderWork w; w.writer = dynamic_cast<OutputEffectInstance*>( writer->getEffectInstance().get() ); assert(w.writer); w.firstFrame = INT_MIN; w.lastFrame = INT_MAX; w.frameStep = INT_MIN; w.useRenderStats = false; works.push_back(w); getApp()->startWritersRendering(false, works); EXPECT_TRUE( QFile::exists(filePath) ); QFile::remove(filePath); }
void KnobGui::onFrozenChanged(bool frozen) { KnobPtr knob = getKnob(); KnobButton* isBtn = dynamic_cast<KnobButton*>(knob.get()); if (isBtn && !isBtn->isRenderButton()) { return; } int dims = knob->getDimension(); for (int i = 0; i < dims; ++i) { ///Do not unset read only if the knob is slaved in this dimension because we are still using it. if ( !frozen && knob->isSlave(i) ) { continue; } if (knob->isEnabled(i)) { setReadOnly_(frozen, i); } } }
void OutputEffectInstance::createWriterPath() { ///Make sure that the file path exists KnobPtr fileParam = getKnobByName(kOfxImageEffectFileParamName); if (fileParam) { Knob<std::string>* isString = dynamic_cast<Knob<std::string>*>( fileParam.get() ); if (isString) { std::string pattern = isString->getValue(); std::string path = SequenceParsing::removePath(pattern); std::map<std::string, std::string> env; getApp()->getProject()->getEnvironmentVariables(env); Project::expandVariable(env, path); if ( !path.empty() ) { QDir().mkpath( QString::fromUtf8( path.c_str() ) ); } } } }
TEST_F(BaseTest, SetValues) { NodePtr generator = createNode(_generatorPluginID); assert(generator); KnobPtr knob = generator->getKnobByName("noiseZSlope"); KnobDouble* radius = dynamic_cast<KnobDouble*>( knob.get() ); EXPECT_TRUE(radius != 0); if (!radius) { return; } radius->setValue(0.5); EXPECT_TRUE(radius->getValue() == 0.5); //Check that linear interpolation is working as intended KeyFrame kf; radius->setInterpolationAtTime(eCurveChangeReasonInternal, ViewSpec::all(), 0, 0, eKeyframeTypeLinear, &kf); radius->setValueAtTime(0, 0., ViewSpec::all(), 0); radius->setValueAtTime(100, 1., ViewSpec::all(), 0); for (int i = 0; i <= 100; ++i) { double v = radius->getValueAtTime(i); EXPECT_TRUE(std::abs(v - i / 100.) < 1e-6); } }
void DockablePanelPrivate::initializeKnobVector(const KnobsVec& knobs, QWidget* lastRowWidget) { std::list<boost::shared_ptr<KnobPage> > pages; KnobsVec regularKnobs; //Extract pages first for (U32 i = 0; i < knobs.size(); ++i) { KnobPage *isPage = dynamic_cast<KnobPage*>( knobs[i].get() ); if (isPage) { pages.push_back( boost::dynamic_pointer_cast<KnobPage>(knobs[i]) ); continue; } else { regularKnobs.push_back(knobs[i]); } } for (std::list<boost::shared_ptr<KnobPage> >::iterator it = pages.begin(); it != pages.end(); ++it) { //create page (void)findKnobGuiOrCreate( *it, true, 0, KnobsVec() ); KnobsVec children = (*it)->getChildren(); KnobsVec::iterator prev = children.end(); for (KnobsVec::iterator it2 = children.begin(); it2 != children.end(); ++it2) { bool makeNewLine = true; KnobGroup *isGroup = dynamic_cast<KnobGroup*>( it2->get() ); ////The knob will have a vector of all other knobs on the same line. KnobsVec knobsOnSameLine; //If the knob is dynamic (i:e created after the initial creation of knobs) //it can be added as part of a group defined earlier hence we have to insert it at the proper index. KnobPtr parentKnob = (*it2)->getParentKnob(); KnobGroup* isParentGroup = dynamic_cast<KnobGroup*>( parentKnob.get() ); if (!isGroup) { if ( ( prev != children.end() ) && !(*prev)->isNewLineActivated() ) { makeNewLine = false; } if (isParentGroup) { KnobsVec groupsiblings = isParentGroup->getChildren(); findKnobsOnSameLine(groupsiblings, *it2, knobsOnSameLine); } else { findKnobsOnSameLine(children, *it2, knobsOnSameLine); } } KnobGuiPtr newGui = findKnobGuiOrCreate(*it2, makeNewLine, lastRowWidget, knobsOnSameLine); ///childrens cannot be on the same row than their parent if (!isGroup && newGui) { lastRowWidget = newGui->getFieldContainer(); } std::vector<KnobPtr>::iterator foundRegular = std::find(regularKnobs.begin(), regularKnobs.end(), *it2); if ( foundRegular != regularKnobs.end() ) { regularKnobs.erase(foundRegular); } if ( prev == children.end() ) { prev = children.begin(); } else { ++prev; } } } //For knobs left, create them KnobsVec::iterator prev = regularKnobs.end(); for (KnobsVec::iterator it = regularKnobs.begin(); it != regularKnobs.end(); ++it) { bool makeNewLine = true; KnobGroup *isGroup = dynamic_cast<KnobGroup*>( it->get() ); ////The knob will have a vector of all other knobs on the same line. KnobsVec knobsOnSameLine; //If the knob is dynamic (i:e created after the initial creation of knobs) //it can be added as part of a group defined earlier hence we have to insert it at the proper index. KnobPtr parentKnob = (*it)->getParentKnob(); KnobGroup* isParentGroup = dynamic_cast<KnobGroup*>( parentKnob.get() ); if (!isGroup) { if ( ( prev != regularKnobs.end() ) && !(*prev)->isNewLineActivated() ) { makeNewLine = false; } KnobPage* isParentPage = dynamic_cast<KnobPage*>( parentKnob.get() ); if (isParentPage) { KnobsVec children = isParentPage->getChildren(); findKnobsOnSameLine(children, (*it), knobsOnSameLine); } else if (isParentGroup) { KnobsVec children = isParentGroup->getChildren(); findKnobsOnSameLine(children, (*it), knobsOnSameLine); } else { findKnobsOnSameLine(regularKnobs, (*it), knobsOnSameLine); } } KnobGuiPtr newGui = findKnobGuiOrCreate(*it, makeNewLine, lastRowWidget, knobsOnSameLine); ///childrens cannot be on the same row than their parent if (!isGroup && newGui) { lastRowWidget = newGui->getFieldContainer(); } if ( prev == regularKnobs.end() ) { prev = regularKnobs.begin(); } else { ++prev; } } _publicInterface->refreshTabWidgetMaxHeight(); } // DockablePanelPrivate::initializeKnobVector
KnobGuiPtr DockablePanelPrivate::findKnobGuiOrCreate(const KnobPtr & knob, bool makeNewLine, QWidget* lastRowWidget, const std::vector< boost::shared_ptr< KnobI > > & knobsOnSameLine) { assert(knob); boost::shared_ptr<KnobGroup> isGroup = boost::dynamic_pointer_cast<KnobGroup>(knob); boost::shared_ptr<KnobPage> isPage = boost::dynamic_pointer_cast<KnobPage>(knob); for (KnobsGuiMapping::const_iterator it = _knobs.begin(); it != _knobs.end(); ++it) { if ( (it->first.lock() == knob) && it->second ) { if (isPage) { return it->second; } else if ( isGroup && ( ( !isGroup->isTab() && it->second->hasWidgetBeenCreated() ) || isGroup->isTab() ) ) { return it->second; } else if ( it->second->hasWidgetBeenCreated() ) { return it->second; } else { break; } } } if (isPage) { if ( isPage->getChildren().empty() ) { return KnobGuiPtr(); } getOrCreatePage(isPage); KnobsVec children = isPage->getChildren(); initializeKnobVector(children, lastRowWidget); return KnobGuiPtr(); } KnobGuiPtr ret = createKnobGui(knob); if (!ret) { return KnobGuiPtr(); } KnobPtr parentKnob = knob->getParentKnob(); boost::shared_ptr<KnobGroup> parentIsGroup = boost::dynamic_pointer_cast<KnobGroup>(parentKnob); KnobGuiGroup* parentGui = 0; /// if this knob is within a group, make sure the group is created so far if (parentIsGroup) { parentGui = dynamic_cast<KnobGuiGroup*>( findKnobGuiOrCreate( parentKnob, true, ret->getFieldContainer() ).get() ); } ///So far the knob could have no parent, in which case we force it to be in the default page. if (!parentKnob) { boost::shared_ptr<KnobPage> defPage = ensureDefaultPageKnobCreated(); defPage->addKnob(knob); parentKnob = defPage; } ///if widgets for the KnobGui have already been created, don't do the following ///For group only create the gui if it is not a tab. if ( isGroup && isGroup->isTab() ) { boost::shared_ptr<KnobPage> parentIsPage = boost::dynamic_pointer_cast<KnobPage>(parentKnob); if (!parentKnob || parentIsPage) { PageMap::iterator page = _pages.end(); if (!parentKnob) { page = getDefaultPage(knob); } else { page = getOrCreatePage(parentIsPage); } bool existed = true; if (!page->second.groupAsTab) { existed = false; page->second.groupAsTab = new TabGroup(_publicInterface); } page->second.groupAsTab->addTab( isGroup, QString::fromUtf8( isGroup->getLabel().c_str() ) ); ///retrieve the form layout QGridLayout* layout; if (_useScrollAreasForTabs) { layout = dynamic_cast<QGridLayout*>( dynamic_cast<QScrollArea*>(page->second.tab)->widget()->layout() ); } else { layout = dynamic_cast<QGridLayout*>( page->second.tab->layout() ); } assert(layout); if (!existed) { layout->addWidget(page->second.groupAsTab, page->second.currentRow, 0, 1, 2); } page->second.groupAsTab->refreshTabSecretNess( isGroup.get() ); } else { assert(parentIsGroup); assert(parentGui); TabGroup* groupAsTab = parentGui->getOrCreateTabWidget(); groupAsTab->addTab( isGroup, QString::fromUtf8( isGroup->getLabel().c_str() ) ); if ( parentIsGroup && parentIsGroup->isTab() ) { ///insert the tab in the layout of the parent ///Find the page in the parentParent group KnobPtr parentParent = parentKnob->getParentKnob(); assert(parentParent); boost::shared_ptr<KnobGroup> parentParentIsGroup = boost::dynamic_pointer_cast<KnobGroup>(parentParent); boost::shared_ptr<KnobPage> parentParentIsPage = boost::dynamic_pointer_cast<KnobPage>(parentParent); assert(parentParentIsGroup || parentParentIsPage); TabGroup* parentTabGroup = 0; if (parentParentIsPage) { PageMap::iterator page = getOrCreatePage(parentParentIsPage); assert( page != _pages.end() ); parentTabGroup = page->second.groupAsTab; } else { KnobsGuiMapping::iterator it = findKnobGui(parentParent); assert( it != _knobs.end() ); KnobGuiGroup* parentParentGroupGui = dynamic_cast<KnobGuiGroup*>( it->second.get() ); assert(parentParentGroupGui); parentTabGroup = parentParentGroupGui->getOrCreateTabWidget(); } QGridLayout* layout = parentTabGroup->addTab( parentIsGroup, QString::fromUtf8( parentIsGroup->getLabel().c_str() ) ); assert(layout); layout->addWidget(groupAsTab, 0, 0, 1, 2); } else { boost::shared_ptr<KnobPage> topLevelPage = knob->getTopLevelPage(); PageMap::iterator page = getOrCreatePage(topLevelPage); assert( page != _pages.end() ); ///retrieve the form layout QGridLayout* layout; if (_useScrollAreasForTabs) { layout = dynamic_cast<QGridLayout*>( dynamic_cast<QScrollArea*>(page->second.tab)->widget()->layout() ); } else { layout = dynamic_cast<QGridLayout*>( page->second.tab->layout() ); } assert(layout); layout->addWidget(groupAsTab, page->second.currentRow, 0, 1, 2); } groupAsTab->refreshTabSecretNess( isGroup.get() ); } } else if ( !ret->hasWidgetBeenCreated() ) { KnobPtr parentKnobTmp = parentKnob; while (parentKnobTmp) { KnobPtr parent = parentKnobTmp->getParentKnob(); if (!parent) { break; } else { parentKnobTmp = parent; } } ////find in which page the knob should be boost::shared_ptr<KnobPage> isTopLevelParentAPage = boost::dynamic_pointer_cast<KnobPage>(parentKnobTmp); assert(isTopLevelParentAPage); PageMap::iterator page = getOrCreatePage(isTopLevelParentAPage); assert( page != _pages.end() ); ///retrieve the form layout QGridLayout* layout; if (_useScrollAreasForTabs) { layout = dynamic_cast<QGridLayout*>( dynamic_cast<QScrollArea*>(page->second.tab)->widget()->layout() ); } else { layout = dynamic_cast<QGridLayout*>( page->second.tab->layout() ); } assert(layout); ///if the knob has specified that it didn't want to trigger a new line, decrement the current row /// index of the tab if (!makeNewLine) { --page->second.currentRow; } QWidget* fieldContainer = 0; QHBoxLayout* fieldLayout = 0; if (makeNewLine) { ///if new line is not turned off, create a new line fieldContainer = new QWidget(page->second.tab); fieldLayout = new QHBoxLayout(fieldContainer); fieldLayout->setContentsMargins( TO_DPIX(3), 0, 0, TO_DPIY(NATRON_SETTINGS_VERTICAL_SPACING_PIXELS) ); fieldLayout->setSpacing( TO_DPIY(2) ); fieldLayout->setAlignment(Qt::AlignLeft); } else { ///otherwise re-use the last row's widget and layout assert(lastRowWidget); fieldContainer = lastRowWidget; fieldLayout = dynamic_cast<QHBoxLayout*>( fieldContainer->layout() ); } assert(fieldContainer); assert(fieldLayout); ///Create the label if needed KnobClickableLabel* label = 0; Label* warningLabel = 0; std::string descriptionLabel; KnobString* isStringKnob = dynamic_cast<KnobString*>( knob.get() ); bool isLabelKnob = isStringKnob && isStringKnob->isLabel(); if (isLabelKnob) { descriptionLabel = isStringKnob->getValue(); } else { descriptionLabel = knob->getLabel(); } const std::string& labelIconFilePath = knob->getIconLabel(); QWidget *labelContainer = 0; QHBoxLayout *labelLayout = 0; const bool hasLabel = ret->isLabelVisible() || isLabelKnob; if (hasLabel) { if (makeNewLine) { labelContainer = new QWidget(page->second.tab); labelLayout = new QHBoxLayout(labelContainer); labelLayout->setContentsMargins( TO_DPIX(3), 0, 0, TO_DPIY(NATRON_SETTINGS_VERTICAL_SPACING_PIXELS) ); labelLayout->setSpacing( TO_DPIY(2) ); } label = new KnobClickableLabel(QString(), ret, page->second.tab); warningLabel = new Label(page->second.tab); warningLabel->setVisible(false); QFontMetrics fm(label->font(), 0); int pixSize = fm.height(); QPixmap stdErrorPix; stdErrorPix = getStandardIcon(QMessageBox::Critical, pixSize, label); warningLabel->setPixmap(stdErrorPix); bool pixmapSet = false; if ( !labelIconFilePath.empty() ) { QPixmap pix; if (labelIconFilePath == "dialog-warning") { pix = getStandardIcon(QMessageBox::Warning, pixSize, label); } else if (labelIconFilePath == "dialog-question") { pix = getStandardIcon(QMessageBox::Question, pixSize, label); } else if (labelIconFilePath == "dialog-error") { pix = stdErrorPix; } else if (labelIconFilePath == "dialog-information") { pix = getStandardIcon(QMessageBox::Information, pixSize, label); } else { pix.load( QString::fromUtf8( labelIconFilePath.c_str() ) ); if (pix.width() != pixSize) { pix = pix.scaled(pixSize, pixSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); } } if ( !pix.isNull() ) { pixmapSet = true; label->setPixmap(pix); } } if (!pixmapSet) { QString labelStr( QString::fromUtf8( descriptionLabel.c_str() ) ); /*labelStr += ":";*/ if ( ret->isLabelBold() ) { label->setBold(true); } label->setText_overload(labelStr ); } QObject::connect( label, SIGNAL(clicked(bool)), ret.get(), SIGNAL(labelClicked(bool)) ); if (makeNewLine) { labelLayout->addWidget(warningLabel); labelLayout->addWidget(label); } } /* * Find out in which layout the knob should be: either in the layout of the page or in the layout of * the nearest parent group tab in the hierarchy */ boost::shared_ptr<KnobGroup> closestParentGroupTab; KnobPtr parentTmp = parentKnob; assert(parentKnobTmp); while (!closestParentGroupTab) { boost::shared_ptr<KnobGroup> parentGroup = boost::dynamic_pointer_cast<KnobGroup>(parentTmp); if ( parentGroup && parentGroup->isTab() ) { closestParentGroupTab = parentGroup; } parentTmp = parentTmp->getParentKnob(); if (!parentTmp) { break; } } if (closestParentGroupTab) { /* * At this point we know that the parent group (which is a tab in the TabWidget) will have at least 1 knob * so ensure it is added to the TabWidget. * There are 2 possibilities, either the parent of the group tab is another group, in which case we have to * make sure the TabWidget is visible in the parent TabWidget of the group, otherwise we just add the TabWidget * to the on of the page. */ KnobPtr parentParent = closestParentGroupTab->getParentKnob(); KnobGroup* parentParentIsGroup = dynamic_cast<KnobGroup*>( parentParent.get() ); boost::shared_ptr<KnobPage> parentParentIsPage = boost::dynamic_pointer_cast<KnobPage>(parentParent); assert(parentParentIsGroup || parentParentIsPage); if (parentParentIsGroup) { KnobGuiGroup* parentParentGroupGui = dynamic_cast<KnobGuiGroup*>( findKnobGuiOrCreate( parentParent, true, ret->getFieldContainer() ).get() ); assert(parentParentGroupGui); if (parentParentGroupGui) { TabGroup* groupAsTab = parentParentGroupGui->getOrCreateTabWidget(); assert(groupAsTab); layout = groupAsTab->addTab( closestParentGroupTab, QString::fromUtf8( closestParentGroupTab->getLabel().c_str() ) ); } } else if (parentParentIsPage) { PageMap::iterator page = getOrCreatePage(parentParentIsPage); assert( page != _pages.end() ); assert(page->second.groupAsTab); layout = page->second.groupAsTab->addTab( closestParentGroupTab, QString::fromUtf8( closestParentGroupTab->getLabel().c_str() ) ); } assert(layout); } ///fill the fieldLayout with the widgets ret->createGUI(layout, fieldContainer, labelContainer, label, warningLabel, fieldLayout, makeNewLine, knobsOnSameLine); ret->setEnabledSlot(); ///Must add the row to the layout before calling setSecret() if (makeNewLine) { int rowIndex; if (closestParentGroupTab) { rowIndex = layout->rowCount(); } else if ( parentGui && knob->isDynamicallyCreated() ) { const std::list<KnobGuiWPtr>& children = parentGui->getChildren(); if ( children.empty() ) { rowIndex = parentGui->getActualIndexInLayout(); } else { rowIndex = children.back().lock()->getActualIndexInLayout(); } ++rowIndex; } else { rowIndex = page->second.currentRow; } const bool labelOnSameColumn = ret->isLabelOnSameColumn(); if (!hasLabel) { layout->addWidget(fieldContainer, rowIndex, 0, 1, 2); } else { if (label) { if (labelOnSameColumn) { labelLayout->addWidget(fieldContainer); layout->addWidget(labelContainer, rowIndex, 0, 1, 2); } else { layout->addWidget(labelContainer, rowIndex, 0, 1, 1, Qt::AlignRight); layout->addWidget(fieldContainer, rowIndex, 1, 1, 1); } } } //if (closestParentGroupTab) { ///See http://stackoverflow.com/questions/14033902/qt-qgridlayout-automatically-centers-moves-items-to-the-middle for ///a bug of QGridLayout: basically all items are centered, but we would like to add stretch in the bottom of the layout. ///To do this we add an empty widget with an expanding vertical size policy. /*QWidget* foundSpacer = 0; for (int i = 0; i < layout->rowCount(); ++i) { QLayoutItem* item = layout->itemAtPosition(i, 0); if (!item) { continue; } QWidget* w = item->widget(); if (!w) { continue; } if (w->objectName() == QString::fromUtf8("emptyWidget")) { foundSpacer = w; break; } } if (foundSpacer) { layout->removeWidget(foundSpacer); } else { foundSpacer = new QWidget(layout->parentWidget()); foundSpacer->setObjectName(QString::fromUtf8("emptyWidget")); foundSpacer->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding); } ///And add our stretch layout->addWidget(foundSpacer,layout->rowCount(), 0, 1, 2);*/ // } } // makeNewLine ret->setSecret(); if ( knob->isNewLineActivated() && ret->shouldAddStretch() ) { fieldLayout->addStretch(); } ///increment the row count ++page->second.currentRow; if (parentIsGroup && parentGui) { parentGui->addKnob(ret); } } // if ( !ret->hasWidgetBeenCreated() && ( !isGroup || !isGroup->isTab() ) ) {
void RestoreDefaultsCommand::redo() { std::list<SequenceTime> times; KnobPtr first = _knobs.front().lock(); AppInstance* app = 0; KnobHolder* holder = first->getHolder(); EffectInstance* isEffect = dynamic_cast<EffectInstance*>(holder); if (holder) { app = holder->getApp(); holder->beginChanges(); } /* First reset all knobs values, this will not call instanceChanged action */ for (std::list<KnobWPtr >::iterator it = _knobs.begin(); it != _knobs.end(); ++it) { KnobPtr itKnob = it->lock(); if (!itKnob) { continue; } if ( itKnob->getHolder() && itKnob->getHolder()->getApp() ) { int dim = itKnob->getDimension(); for (int i = 0; i < dim; ++i) { if ( (i == _targetDim) || (_targetDim == -1) ) { boost::shared_ptr<Curve> c = itKnob->getCurve(ViewIdx(0), i); if (c) { KeyFrameSet kfs = c->getKeyFrames_mt_safe(); for (KeyFrameSet::iterator it = kfs.begin(); it != kfs.end(); ++it) { times.push_back( std::floor(it->getTime() + 0.5) ); } } } } } if ( itKnob->getHolder() ) { itKnob->getHolder()->beginChanges(); } itKnob->blockValueChanges(); for (int d = 0; d < itKnob->getDimension(); ++d) { itKnob->resetToDefaultValue(d); } itKnob->unblockValueChanges(); if ( itKnob->getHolder() ) { itKnob->getHolder()->endChanges(true); } } /* Block value changes and call instanceChange on all knobs afterwards to put back the plug-in in a correct state */ double time = 0; if (app) { time = app->getTimeLine()->currentFrame(); } for (std::list<KnobWPtr >::iterator it = _knobs.begin(); it != _knobs.end(); ++it) { KnobPtr itKnob = it->lock(); if (!itKnob) { continue; } if ( itKnob->getHolder() ) { itKnob->getHolder()->onKnobValueChanged_public(itKnob.get(), eValueChangedReasonRestoreDefault, time, ViewIdx(0), true); } } if (app) { app->removeMultipleKeyframeIndicator(times, true); } if ( holder && holder->getApp() ) { holder->endChanges(); } if (_isNodeReset && isEffect) { isEffect->purgeCaches(); } if ( first->getHolder() ) { first->getHolder()->incrHashAndEvaluate(true, true); if ( first->getHolder()->getApp() ) { first->getHolder()->getApp()->redrawAllViewers(); } } setText( tr("Restore default value(s)") ); } // RestoreDefaultsCommand::redo
void MultipleKnobEditsUndoCommand::redo() { assert( !knobs.empty() ); KnobHolder* holder = knobs.begin()->first.lock()->getKnob()->getHolder(); if (holder) { holder->beginChanges(); } ///this is the first redo command, set values for (ParamsMap::iterator it = knobs.begin(); it != knobs.end(); ++it) { KnobGuiPtr knobUI = it->first.lock(); if (!knobUI) { continue; } KnobPtr knob = knobUI->getKnob(); if (!knob) { continue; } knob->beginChanges(); Knob<int>* isInt = dynamic_cast<Knob<int>*>( knob.get() ); Knob<bool>* isBool = dynamic_cast<Knob<bool>*>( knob.get() ); Knob<double>* isDouble = dynamic_cast<Knob<double>*>( knob.get() ); Knob<std::string>* isString = dynamic_cast<Knob<std::string>*>( knob.get() ); for (std::list<ValueToSet>::iterator it2 = it->second.begin(); it2 != it->second.end(); ++it2) { KeyFrame k; if (!firstRedoCalled) { if (isInt) { it2->oldValue.setValue( isInt->getValueAtTime(it2->time, it2->dimension) ); } else if (isBool) { it2->oldValue.setValue( isBool->getValueAtTime(it2->time, it2->dimension) ); } else if (isDouble) { it2->oldValue.setValue( isDouble->getValueAtTime(it2->time, it2->dimension) ); } else if (isString) { it2->oldValue.setValue( isString->getValueAtTime(it2->time, it2->dimension) ); } } if (it2->setKeyFrame) { bool keyAdded = false; bool refreshGui = it2->time == knob->getHolder()->getApp()->getTimeLine()->currentFrame(); if (isInt) { keyAdded = knobUI->setValueAtTime<int>(it2->dimension, it2->newValue.toInt(), it2->time, ViewIdx(0), &k, refreshGui, _reason); } else if (isBool) { keyAdded = knobUI->setValueAtTime<bool>(it2->dimension, it2->newValue.toBool(), it2->time, ViewIdx(0), &k, refreshGui, _reason); } else if (isDouble) { keyAdded = knobUI->setValueAtTime<double>(it2->dimension, it2->newValue.toDouble(), it2->time, ViewIdx(0), &k, refreshGui, _reason); } else if (isString) { keyAdded = knobUI->setValueAtTime<std::string>(it2->dimension, it2->newValue.toString().toStdString(), it2->time, ViewIdx(0), &k, refreshGui, _reason); } else { assert(false); } it2->setValueRetCode = keyAdded ? KnobHelper::eValueChangedReturnCodeKeyframeAdded : KnobHelper::eValueChangedReturnCodeKeyframeModified; } else { if (isInt) { it2->setValueRetCode = knobUI->setValue<int>(it2->dimension, it2->newValue.toInt(), &k, true, _reason); } else if (isBool) { it2->setValueRetCode = knobUI->setValue<bool>(it2->dimension, it2->newValue.toBool(), &k, true, _reason); } else if (isDouble) { it2->setValueRetCode = knobUI->setValue<double>(it2->dimension, it2->newValue.toDouble(), &k, true, _reason); } else if (isString) { it2->setValueRetCode = knobUI->setValue<std::string>(it2->dimension, it2->newValue.toString().toStdString(), &k, true, _reason); } else { assert(false); } if (!firstRedoCalled && !it2->setKeyFrame) { it2->time = knob->getCurrentTime(); } } } knob->endChanges(); } if (holder) { holder->endChanges(); } firstRedoCalled = true; } // redo
void MultipleKnobEditsUndoCommand::undo() { assert( !knobs.empty() ); KnobHolder* holder = knobs.begin()->first.lock()->getKnob()->getHolder(); if (holder) { holder->beginChanges(); } for (ParamsMap::iterator it = knobs.begin(); it != knobs.end(); ++it) { KnobGuiPtr knobUI = it->first.lock(); if (!knobUI) { continue; } KnobPtr knob = knobUI->getKnob(); if (!knob) { continue; } knob->beginChanges(); Knob<int>* isInt = dynamic_cast<Knob<int>*>( knob.get() ); Knob<bool>* isBool = dynamic_cast<Knob<bool>*>( knob.get() ); Knob<double>* isDouble = dynamic_cast<Knob<double>*>( knob.get() ); Knob<std::string>* isString = dynamic_cast<Knob<std::string>*>( knob.get() ); KeyFrame k; for (std::list<ValueToSet>::iterator it2 = it->second.begin(); it2 != it->second.end(); ++it2) { KnobHelper::ValueChangedReturnCodeEnum retCode = (KnobHelper::ValueChangedReturnCodeEnum)it2->setValueRetCode; if (retCode == KnobHelper::eValueChangedReturnCodeKeyframeAdded) { knobUI->removeKeyFrame( it2->time, it2->dimension, ViewIdx(0) ); } else { if (it2->setKeyFrame) { bool refreshGui = it2->time == knob->getHolder()->getApp()->getTimeLine()->currentFrame(); if (isInt) { knobUI->setValueAtTime<int>(it2->dimension, it2->oldValue.toInt(), it2->time, ViewIdx(0), &k, refreshGui, _reason); } else if (isBool) { knobUI->setValueAtTime<bool>(it2->dimension, it2->oldValue.toBool(), it2->time, ViewIdx(0), &k, refreshGui, _reason); } else if (isDouble) { knobUI->setValueAtTime<double>(it2->dimension, it2->oldValue.toDouble(), it2->time, ViewIdx(0), &k, refreshGui, _reason); } else if (isString) { knobUI->setValueAtTime<std::string>(it2->dimension, it2->oldValue.toString().toStdString(), it2->time, ViewIdx(0), &k, refreshGui, _reason); } else { assert(false); } } else { if (isInt) { knobUI->setValue<int>(it2->dimension, it2->oldValue.toInt(), &k, true, _reason); } else if (isBool) { knobUI->setValue<bool>(it2->dimension, it2->oldValue.toBool(), &k, true, _reason); } else if (isDouble) { knobUI->setValue<double>(it2->dimension, it2->oldValue.toDouble(), &k, true, _reason); } else if (isString) { knobUI->setValue<std::string>(it2->dimension, it2->oldValue.toString().toStdString(), &k, true, _reason); } else { assert(false); } } } } knob->endChanges(); } if (holder) { holder->endChanges(); } } // MultipleKnobEditsUndoCommand::undo
void PasteUndoCommand::copyFrom(const KnobPtr& serializedKnob, bool isRedo) { KnobPtr internalKnob = _imp->knob.lock()->getKnob(); switch (_imp->type) { case eKnobClipBoardTypeCopyAnim: { internalKnob->beginChanges(); for (int i = 0; i < internalKnob->getDimension(); ++i) { if ( ( _imp->targetDimension == -1) || ( i == _imp->targetDimension) ) { boost::shared_ptr<Curve> fromCurve; if ( ( i == _imp->targetDimension) && ( _imp->fromDimension != -1) ) { fromCurve = serializedKnob->getCurve(ViewIdx(0), _imp->fromDimension); } else { fromCurve = serializedKnob->getCurve(ViewIdx(0), i); } if (!fromCurve) { continue; } internalKnob->cloneCurve(ViewIdx(0), i, *fromCurve); } } internalKnob->endChanges(); break; } case eKnobClipBoardTypeCopyValue: { Knob<int>* isInt = dynamic_cast<Knob<int>*>( internalKnob.get() ); Knob<bool>* isBool = dynamic_cast<Knob<bool>*>( internalKnob.get() ); Knob<double>* isDouble = dynamic_cast<Knob<double>*>( internalKnob.get() ); Knob<std::string>* isString = dynamic_cast<Knob<std::string>*>( internalKnob.get() ); Knob<int>* isFromInt = dynamic_cast<Knob<int>*>( serializedKnob.get() ); Knob<bool>* isFromBool = dynamic_cast<Knob<bool>*>( serializedKnob.get() ); Knob<double>* isFromDouble = dynamic_cast<Knob<double>*>( serializedKnob.get() ); Knob<std::string>* isFromString = dynamic_cast<Knob<std::string>*>( serializedKnob.get() ); internalKnob->beginChanges(); for (int i = 0; i < internalKnob->getDimension(); ++i) { if ( ( _imp->targetDimension == -1) || ( i == _imp->targetDimension) ) { if (isInt && isFromInt) { int f = (i == _imp->targetDimension && _imp->fromDimension != -1) ? isFromInt->getValue(_imp->fromDimension) : isFromInt->getValue(i); isInt->setValue(f, ViewIdx(0), i, eValueChangedReasonNatronInternalEdited, 0); } else if (isBool && isFromBool) { bool f = (i == _imp->targetDimension && _imp->fromDimension != -1) ? isFromBool->getValue(_imp->fromDimension) : isFromBool->getValue(i); isBool->setValue(f, ViewIdx(0), i, eValueChangedReasonNatronInternalEdited, 0); } else if (isDouble && isFromDouble) { double f = (i == _imp->targetDimension && _imp->fromDimension != -1) ? isFromDouble->getValue(_imp->fromDimension) : isFromDouble->getValue(i); isDouble->setValue(f, ViewIdx(0), i, eValueChangedReasonNatronInternalEdited, 0); } else if (isString && isFromString) { std::string f = (i == _imp->targetDimension && _imp->fromDimension != -1) ? isFromString->getValue(_imp->fromDimension) : isFromString->getValue(i); isString->setValue(f, ViewIdx(0), i, eValueChangedReasonNatronInternalEdited, 0); } } } internalKnob->endChanges(); break; } case eKnobClipBoardTypeCopyLink: { //bool useExpression = !KnobI::areTypesCompatibleForSlave(internalKnob.get(), serializedKnob.get()); internalKnob->beginChanges(); for (int i = 0; i < internalKnob->getDimension(); ++i) { if ( ( _imp->targetDimension == -1) || ( i == _imp->targetDimension) ) { if (isRedo) { if (_imp->fromDimension != -1) { internalKnob->slaveTo(i, serializedKnob, _imp->fromDimension); } else { internalKnob->slaveTo(i, serializedKnob, i); } } else { internalKnob->unSlave(i, false); } } } internalKnob->endChanges(); break; } } // switch } // redo
QString KnobGui::toolTip() const { KnobPtr knob = getKnob(); KnobChoice* isChoice = dynamic_cast<KnobChoice*>( knob.get() ); bool isMarkdown = knob.get()->isHintInMarkdown(); QString tt; QString realTt; if (isMarkdown) { tt = QString::fromUtf8( knob.get()->getName().c_str() ); tt.append( QString::fromUtf8("\n==========\n\n") ); } else { tt = getScriptNameHtml(); } if (!isChoice) { realTt.append( QString::fromUtf8( knob->getHintToolTip().c_str() ) ); } else { realTt.append( QString::fromUtf8( isChoice->getHintToolTipFull().c_str() ) ); } std::vector<std::string> expressions; bool exprAllSame = true; for (int i = 0; i < knob->getDimension(); ++i) { expressions.push_back( knob->getExpression(i) ); if ( (i > 0) && (expressions[i] != expressions[0]) ) { exprAllSame = false; } } QString exprTt; if (exprAllSame) { if ( !expressions[0].empty() ) { if (isMarkdown) { exprTt = QString::fromUtf8("ret = **%1**\n\n").arg( QString::fromUtf8( expressions[0].c_str() ) ); } else { exprTt = QString::fromUtf8("ret = <b>%1</b><br />").arg( QString::fromUtf8( expressions[0].c_str() ) ); } } } else { for (int i = 0; i < knob->getDimension(); ++i) { std::string dimName = knob->getDimensionName(i); QString toAppend; if (isMarkdown) { toAppend = QString::fromUtf8("%1 = **%2**\n\n").arg( QString::fromUtf8( dimName.c_str() ) ).arg( QString::fromUtf8( expressions[i].c_str() ) ); } else { toAppend = QString::fromUtf8("%1 = <b>%2</b><br />").arg( QString::fromUtf8( dimName.c_str() ) ).arg( QString::fromUtf8( expressions[i].c_str() ) ); } exprTt.append(toAppend); } } if ( !exprTt.isEmpty() ) { tt.append(exprTt); if (!isMarkdown) { tt += QLatin1String("<br/>"); } } if ( !realTt.isEmpty() ) { if (!isMarkdown) { realTt = GuiUtils::convertFromPlainText(realTt.trimmed(), Qt::WhiteSpaceNormal); } tt.append(realTt); } if (isMarkdown) { tt = Markdown::convert2html(tt); // Shrink H1/H2 (Can't do it in qt stylesheet) tt.replace( QString::fromUtf8("<h1>"), QString::fromUtf8("<h1 style=\"font-size:large;\">") ); tt.replace( QString::fromUtf8("<h2>"), QString::fromUtf8("<h2 style=\"font-size:large;\">") ); } return tt; } // KnobGui::toolTip
void OutputEffectInstance::renderFullSequence(bool isBlocking, bool enableRenderStats, BlockingBackgroundRender* renderController, int first, int last, int frameStep) { int viewsCount = getApp()->getProject()->getProjectViewsCount(); const ViewIdx mainView(0); std::vector<ViewIdx> viewsToRender(viewsCount); for (int i = 0; i < viewsCount; ++i) { viewsToRender[i] = ViewIdx(i); } ///The effect is sequential (e.g: WriteFFMPEG), and thus cannot render multiple views, we have to choose one ///We pick the user defined main view in the project settings SequentialPreferenceEnum sequentiallity = getSequentialPreference(); bool canOnlyHandleOneView = sequentiallity == eSequentialPreferenceOnlySequential || sequentiallity == eSequentialPreferencePreferSequential; if (canOnlyHandleOneView) { viewsToRender.clear(); viewsToRender.push_back(mainView); } if ( isViewAware() ) { //If the Writer is view aware, check if it wants to render all views at once or not KnobPtr outputFileNameKnob = getKnobByName(kOfxImageEffectFileParamName); if (outputFileNameKnob) { KnobOutputFile* outputFileName = dynamic_cast<KnobOutputFile*>( outputFileNameKnob.get() ); assert(outputFileName); if (outputFileName) { std::string pattern = outputFileName->getValue(); std::size_t foundViewPattern = pattern.find_first_of("%v"); if (foundViewPattern == std::string::npos) { foundViewPattern = pattern.find_first_of("%V"); } if (foundViewPattern == std::string::npos) { ///No view pattern ///all views will be overwritten to the same file ///If this is WriteOIIO, check the parameter "viewsSelector" to determine if the user wants to encode all ///views to a single file or not KnobPtr viewsKnob = getKnobByName(kWriteOIIOParamViewsSelector); bool hasViewChoice = false; if ( viewsKnob && !viewsKnob->getIsSecret() ) { KnobChoice* viewsChoice = dynamic_cast<KnobChoice*>( viewsKnob.get() ); if (viewsChoice) { hasViewChoice = true; int viewChoice_i = viewsChoice->getValue(); if (viewChoice_i == 0) { // the "All" choice viewsToRender.clear(); // note: if the plugin renders all views to a single file, then rendering view 0 will do the job. viewsToRender.push_back( ViewIdx(0) ); } else { //The user has specified a view viewsToRender.clear(); assert(viewChoice_i >= 1); viewsToRender.push_back( ViewIdx(viewChoice_i - 1) ); } } } if (!hasViewChoice) { if (viewsToRender.size() > 1) { std::string mainViewName; const std::vector<std::string>& viewNames = getApp()->getProject()->getProjectViewNames(); if ( mainView < (int)viewNames.size() ) { mainViewName = viewNames[mainView]; } QString message = tr("%1 does not support multi-view, only the view %2 will be rendered.") .arg( QString::fromUtf8( getNode()->getLabel_mt_safe().c_str() ) ) .arg( QString::fromUtf8( mainViewName.c_str() ) ); if (!renderController) { message.append( QChar::fromLatin1('\n') ); message.append( QString::fromUtf8("You can use the %v or %V indicator in the filename to render to separate files.\n" "Would you like to continue?") ); StandardButtonEnum rep = Dialogs::questionDialog(tr("Multi-view support").toStdString(), message.toStdString(), false, StandardButtons(eStandardButtonOk | eStandardButtonCancel), eStandardButtonOk); if (rep != eStandardButtonOk) { return; } } else { Dialogs::warningDialog( tr("Multi-view support").toStdString(), message.toStdString() ); } } //Render the main-view only... viewsToRender.clear(); viewsToRender.push_back(mainView); } } else { ///The user wants to write each view into a separate file ///This will disregard the content of kWriteOIIOParamViewsSelector and the Writer ///should write one view per-file. } } } } else { // !isViewAware if (viewsToRender.size() > 1) { std::string mainViewName; const std::vector<std::string>& viewNames = getApp()->getProject()->getProjectViewNames(); if ( mainView < (int)viewNames.size() ) { mainViewName = viewNames[mainView]; } QString message = tr("%1 does not support multi-view, only the view %2 will be rendered.") .arg( QString::fromUtf8( getNode()->getLabel_mt_safe().c_str() ) ) .arg( QString::fromUtf8( mainViewName.c_str() ) ); if (!renderController) { message.append( QChar::fromLatin1('\n') ); message.append( QString::fromUtf8("You can use the %v or %V indicator in the filename to render to separate files.\n" "Would you like to continue?") ); StandardButtonEnum rep = Dialogs::questionDialog(tr("Multi-view support").toStdString(), message.toStdString(), false, StandardButtons(eStandardButtonOk | eStandardButtonCancel), eStandardButtonOk); if (rep != eStandardButtonOk) { return; } } else { Dialogs::warningDialog( tr("Multi-view support").toStdString(), message.toStdString() ); } } } RenderSequenceArgs args; { QMutexLocker k(&_outputEffectDataLock); args.firstFrame = first; args.lastFrame = last; args.frameStep = frameStep; args.renderController = renderController; args.useStats = enableRenderStats; args.blocking = isBlocking; args.viewsToRender = viewsToRender; _renderSequenceRequests.push_back(args); if (_renderSequenceRequests.size() > 1) { //The node is already rendering a sequence, queue it and dequeue it in notifyRenderFinished() return; } } launchRenderSequence(args); } // OutputEffectInstance::renderFullSequence
void OutputEffectInstance::reportStats(int time, ViewIdx view, double wallTime, const std::map<NodePtr, NodeRenderStats > & stats) { std::string filename; KnobPtr fileKnob = getKnobByName(kOfxImageEffectFileParamName); if (fileKnob) { KnobOutputFile* strKnob = dynamic_cast<KnobOutputFile*>( fileKnob.get() ); if (strKnob) { QString qfileName = QString::fromUtf8( SequenceParsing::generateFileNameFromPattern(strKnob->getValue( 0, ViewIdx(view) ), getApp()->getProject()->getProjectViewNames(), time, view).c_str() ); QtCompat::removeFileExtension(qfileName); qfileName.append( QString::fromUtf8("-stats.txt") ); filename = qfileName.toStdString(); } } //If there's no filename knob, do not write anything if ( filename.empty() ) { std::cout << tr("Cannot write render statistics file: " "%1 does not seem to have a parameter named \"filename\" " "to determine the location where to write the stats file.") .arg( QString::fromUtf8( getScriptName_mt_safe().c_str() ) ).toStdString(); return; } FStreamsSupport::ofstream ofile; FStreamsSupport::open(&ofile, filename); if (!ofile) { std::cout << tr("Failure to write render statistics file.").toStdString() << std::endl; return; } ofile << "Time spent to render frame (wall clock time): " << Timer::printAsTime(wallTime, false).toStdString() << std::endl; for (std::map<NodePtr, NodeRenderStats >::const_iterator it = stats.begin(); it != stats.end(); ++it) { ofile << "------------------------------- " << it->first->getScriptName_mt_safe() << "------------------------------- " << std::endl; ofile << "Time spent rendering: " << Timer::printAsTime(it->second.getTotalTimeSpentRendering(), false).toStdString() << std::endl; const RectD & rod = it->second.getRoD(); ofile << "Region of definition: x1 = " << rod.x1 << " y1 = " << rod.y1 << " x2 = " << rod.x2 << " y2 = " << rod.y2 << std::endl; ofile << "Is Identity to Effect? "; NodePtr identity = it->second.getInputImageIdentity(); if (identity) { ofile << "Yes, to " << identity->getScriptName_mt_safe() << std::endl; } else { ofile << "No" << std::endl; } ofile << "Has render-scale support? "; if ( it->second.isRenderScaleSupportEnabled() ) { ofile << "Yes"; } else { ofile << "No"; } ofile << std::endl; ofile << "Has tiles support? "; if ( it->second.isTilesSupportEnabled() ) { ofile << "Yes"; } else { ofile << "No"; } ofile << std::endl; ofile << "Channels processed: "; std::bitset<4> processChannels = it->second.getChannelsRendered(); if (processChannels[0]) { ofile << "red "; } if (processChannels[1]) { ofile << "green "; } if (processChannels[2]) { ofile << "blue "; } if (processChannels[3]) { ofile << "alpha"; } ofile << std::endl; ofile << "Output alpha premultiplication: "; switch ( it->second.getOutputPremult() ) { case eImagePremultiplicationOpaque: ofile << "opaque"; break; case eImagePremultiplicationPremultiplied: ofile << "premultiplied"; break; case eImagePremultiplicationUnPremultiplied: ofile << "unpremultiplied"; break; } ofile << std::endl; ofile << "Mipmap level(s) rendered: "; for (std::set<unsigned int>::const_iterator it2 = it->second.getMipMapLevelsRendered().begin(); it2 != it->second.getMipMapLevelsRendered().end(); ++it2) { ofile << *it2 << ' '; } ofile << std::endl; int nbCacheMiss, nbCacheHit, nbCacheHitButDownscaled; it->second.getCacheAccessInfos(&nbCacheMiss, &nbCacheHit, &nbCacheHitButDownscaled); ofile << "Nb cache hit: " << nbCacheMiss << std::endl; ofile << "Nb cache miss: " << nbCacheMiss << std::endl; ofile << "Nb cache hit requiring mipmap downscaling: " << nbCacheHitButDownscaled << std::endl; const std::set<std::string> & planes = it->second.getPlanesRendered(); ofile << "Plane(s) rendered: "; for (std::set<std::string>::const_iterator it2 = planes.begin(); it2 != planes.end(); ++it2) { ofile << *it2 << ' '; } ofile << std::endl; std::list<std::pair<RectI, NodePtr > > identityRectangles = it->second.getIdentityRectangles(); const std::list<RectI> & renderedRectangles = it->second.getRenderedRectangles(); ofile << "Identity rectangles: " << identityRectangles.size() << std::endl; for (std::list<std::pair<RectI, NodePtr > > ::iterator it2 = identityRectangles.begin(); it2 != identityRectangles.end(); ++it2) { ofile << "Origin: " << it2->second->getScriptName_mt_safe() << ", rect: x1 = " << it2->first.x1 << " y1 = " << it2->first.y1 << " x2 = " << it2->first.x2 << " y2 = " << it2->first.y2 << std::endl; } ofile << "Rectangles rendered: " << renderedRectangles.size() << std::endl; for (std::list<RectI>::const_iterator it2 = renderedRectangles.begin(); it2 != renderedRectangles.end(); ++it2) { ofile << "x1 = " << it2->x1 << " y1 = " << it2->y1 << " x2 = " << it2->x2 << " y2 = " << it2->y2 << std::endl; } } } // OutputEffectInstance::reportStats