void ViewerNodePrivate::getAllViewerNodes(bool inGroupOnly, std::list<ViewerNodePtr>& viewerNodes) const { NodeCollectionPtr collection; if (inGroupOnly) { collection = _publicInterface->getNode()->getGroup(); } else { collection = _publicInterface->getApp()->getProject(); } NodesList nodes; if (inGroupOnly) { nodes = collection->getNodes(); } else { collection->getNodes_recursive(nodes, false); } for (NodesList::iterator it = nodes.begin(); it != nodes.end(); ++it) { if (!(*it)->isActivated()) { continue; } ViewerNodePtr isViewer = (*it)->isEffectViewerNode(); if (isViewer) { viewerNodes.push_back(isViewer); } } }
static void restoreLinksRecursive(const NodeCollectionPtr& group, const SERIALIZATION_NAMESPACE::NodeSerializationList& nodes, const std::list<std::pair<NodePtr, SERIALIZATION_NAMESPACE::NodeSerializationPtr > >* allCreatedNodes) { for (SERIALIZATION_NAMESPACE::NodeSerializationList::const_iterator it = nodes.begin(); it != nodes.end(); ++it) { // The nodes created from the serialization may have changed name if another node with the same script-name already existed. // By chance since we created all nodes within the same Group at the same time, we have a list of the old node serialization // and the corresponding created node (with its new script-name). // If we find a match, make sure we use the new node script-name to restore the input. NodePtr foundNode; if (allCreatedNodes) { foundNode = Project::findNodeWithScriptName((*it)->_nodeScriptName, *allCreatedNodes); } if (!foundNode) { // We did not find the node in the serialized nodes list, the last resort is to look into already created nodes // and find an exact match, hoping the script-name of the node did not change. foundNode = group->getNodeByName((*it)->_nodeScriptName); } if (!foundNode) { continue; } // The allCreatedNodes list is useful if the nodes that we created had their script-name changed from what was inside the node serialization object. // It may have changed if a node would already exist in the group with the same script-name. // This kind of conflict may only occur in the top-level graph that we are restoring: sub-graphs are created entirely so script-names should remain // the same between the serilization object and the created node. foundNode->restoreKnobsLinks(**it, allCreatedNodes ? *allCreatedNodes : std::list<std::pair<NodePtr, SERIALIZATION_NAMESPACE::NodeSerializationPtr > >()); NodeGroupPtr isGroup = toNodeGroup(foundNode->getEffectInstance()); if (isGroup) { // For sub-groupe, we don't have the list of created nodes, and their serialization list, but we should not need it: // It's only the top-level group that we create that may have conflicts with script-names, sub-groups are conflict free since // we just created them. restoreLinksRecursive(isGroup, (*it)->_children, 0); } } }
void GuiAppInstance::createNodeGui(const NodePtr &node, const NodePtr& parentMultiInstance, const CreateNodeArgs& args) { NodeCollectionPtr group = node->getGroup(); NodeGraph* graph; if (group) { NodeGraphI* graph_i = group->getNodeGraph(); assert(graph_i); graph = dynamic_cast<NodeGraph*>(graph_i); assert(graph); } else { graph = _imp->_gui->getNodeGraph(); } if (!graph) { throw std::logic_error(""); } NodesGuiList selectedNodes = graph->getSelectedNodes(); NodeGuiPtr nodegui = _imp->_gui->createNodeGUI(node, args); assert(nodegui); if (parentMultiInstance && nodegui) { nodegui->hideGui(); NodeGuiIPtr parentNodeGui_i = parentMultiInstance->getNodeGui(); assert(parentNodeGui_i); nodegui->setParentMultiInstance( boost::dynamic_pointer_cast<NodeGui>(parentNodeGui_i) ); } bool isViewer = node->isEffectViewerInstance() != 0; if (isViewer) { _imp->_gui->createViewerGui(node); } // Must be done after the viewer gui has been created _imp->_gui->createNodeViewerInterface(nodegui); NodeGroupPtr isGroup = node->isEffectNodeGroup(); if ( isGroup && isGroup->isSubGraphUserVisible() ) { _imp->_gui->createGroupGui(node, args); } ///Don't initialize inputs if it is a multi-instance child since it is not part of the graph if (!parentMultiInstance) { nodegui->initializeInputs(); } NodeSerializationPtr serialization = args.getProperty<NodeSerializationPtr >(kCreateNodeArgsPropNodeSerialization); if ( !serialization && !isViewer ) { ///we make sure we can have a clean preview. node->computePreviewImage( getTimeLine()->currentFrame() ); triggerAutoSave(); } ///only move main instances if ( node->getParentMultiInstanceName().empty() && !serialization) { bool autoConnect = args.getProperty<bool>(kCreateNodeArgsPropAutoConnect); if ( selectedNodes.empty() || serialization) { autoConnect = false; } double xPosHint = serialization ? INT_MIN : args.getProperty<double>(kCreateNodeArgsPropNodeInitialPosition, 0); double yPosHint = serialization ? INT_MIN : args.getProperty<double>(kCreateNodeArgsPropNodeInitialPosition, 1); if ( (xPosHint != INT_MIN) && (yPosHint != INT_MIN) && (!autoConnect) ) { QPointF pos = nodegui->mapToParent( nodegui->mapFromScene( QPointF(xPosHint, yPosHint) ) ); nodegui->refreshPosition( pos.x(), pos.y(), true ); } else { BackdropGuiPtr isBd = toBackdropGui(nodegui); if (!isBd) { NodeGuiPtr selectedNode; if ( !serialization && (selectedNodes.size() == 1) ) { selectedNode = selectedNodes.front(); BackdropGuiPtr isBdGui = toBackdropGui(selectedNode); if (isBdGui) { selectedNode.reset(); } } nodegui->getDagGui()->moveNodesForIdealPosition(nodegui, selectedNode, autoConnect); } } } } // createNodeGui
bool Project::restoreGroupFromSerialization(const SERIALIZATION_NAMESPACE::NodeSerializationList & serializedNodes, const NodeCollectionPtr& group, std::list<std::pair<NodePtr, SERIALIZATION_NAMESPACE::NodeSerializationPtr > >* createdNodesOut) { bool mustShowErrorsLog = false; NodeGroupPtr isGrp = toNodeGroup(group); QString groupName; if (isGrp) { groupName = QString::fromUtf8( isGrp->getNode()->getLabel().c_str() ); } else { groupName = tr("top-level"); } group->getApplication()->updateProjectLoadStatus( tr("Creating nodes in group: %1").arg(groupName) ); std::list<std::pair<NodePtr, SERIALIZATION_NAMESPACE::NodeSerializationPtr > > createdNodes; // Loop over all node serialization and create them first for (SERIALIZATION_NAMESPACE::NodeSerializationList::const_iterator it = serializedNodes.begin(); it != serializedNodes.end(); ++it) { NodePtr node = appPTR->createNodeForProjectLoading(*it, group); if (!node) { QString text( tr("ERROR: The node %1 version %2.%3" " was found in the script but does not" " exist in the loaded plug-ins.") .arg( QString::fromUtf8( (*it)->_pluginID.c_str() ) ) .arg((*it)->_pluginMajorVersion).arg((*it)->_pluginMinorVersion) ); appPTR->writeToErrorLog_mt_safe(tr("Project"), QDateTime::currentDateTime(), text); mustShowErrorsLog = true; continue; } else { if (node->getPluginID() == PLUGINID_NATRON_STUB) { // If the node could not be created and we made a stub instead, warn the user QString text( tr("WARNING: The node %1 (%2 version %3.%4) " "was found in the script but the plug-in could not be found. It has been replaced by a pass-through node instead.") .arg( QString::fromUtf8( (*it)->_nodeScriptName.c_str() ) ) .arg( QString::fromUtf8( (*it)->_pluginID.c_str() ) ) .arg((*it)->_pluginMajorVersion) .arg((*it)->_pluginMinorVersion)); appPTR->writeToErrorLog_mt_safe(tr("Project"), QDateTime::currentDateTime(), text); mustShowErrorsLog = true; } else if ( (*it)->_pluginMajorVersion != -1 && (node->getMajorVersion() != (int)(*it)->_pluginMajorVersion) ) { // If the node has a IOContainer don't do this check: when loading older projects that had a // ReadOIIO node for example in version 2, we would now create a new Read meta-node with version 1 instead QString text( tr("WARNING: The node %1 (%2 version %3.%4) " "was found in the script but was loaded " "with version %5.%6 instead.") .arg( QString::fromUtf8( (*it)->_nodeScriptName.c_str() ) ) .arg( QString::fromUtf8( (*it)->_pluginID.c_str() ) ) .arg((*it)->_pluginMajorVersion) .arg((*it)->_pluginMinorVersion) .arg( node->getPlugin()->getPropertyUnsafe<unsigned int>(kNatronPluginPropVersion, 0) ) .arg( node->getPlugin()->getPropertyUnsafe<unsigned int>(kNatronPluginPropVersion, 1) ) ); appPTR->writeToErrorLog_mt_safe(tr("Project"), QDateTime::currentDateTime(), text); } } assert(node); createdNodes.push_back(std::make_pair(node, *it)); } // for all node serialization group->getApplication()->updateProjectLoadStatus( tr("Restoring graph links in group: %1").arg(groupName) ); // Connect the nodes together for (std::list<std::pair<NodePtr, SERIALIZATION_NAMESPACE::NodeSerializationPtr > >::const_iterator it = createdNodes.begin(); it != createdNodes.end(); ++it) { // Loop over the inputs map // This is a map <input label, input node name> // // When loading projects before Natron 2.2, the inputs contain both masks and inputs. // restoreInputs(it->first, it->second->_inputs, createdNodes, false /*isMasks*/); // After Natron 2.2, masks are saved separatly restoreInputs(it->first, it->second->_masks, createdNodes, true /*isMasks*/); } // for (std::list< NodeSerializationPtr >::const_iterator it = serializedNodes.begin(); it != serializedNodes.end(); ++it) { if (createdNodesOut) { *createdNodesOut = createdNodes; } // If we created all sub-groups recursively, then we are in the top-level group. We may now restore all links if (!group->getApplication()->isCreatingNode()) { // Now that we created all nodes. There may be cross-graph link(s) and they can only be truely restored now. restoreLinksRecursive(group, serializedNodes, createdNodesOut); } return !mustShowErrorsLog; } // ProjectPrivate::restoreGroupFromSerialization
PickKnobDialog::PickKnobDialog(DockablePanel* panel, QWidget* parent) : QDialog(parent) , _imp( new PickKnobDialogPrivate(panel) ) { NodeSettingsPanel* nodePanel = dynamic_cast<NodeSettingsPanel*>(panel); assert(nodePanel); if (!nodePanel) { throw std::logic_error("PickKnobDialog::PickKnobDialog()"); } NodeGuiPtr nodeGui = nodePanel->getNodeGui(); NodePtr node = nodeGui->getNode(); NodeGroupPtr isGroup = node->isEffectNodeGroup(); NodeCollectionPtr collec = node->getGroup(); NodeGroupPtr isCollecGroup = toNodeGroup(collec); NodesList collectNodes = collec->getNodes(); for (NodesList::iterator it = collectNodes.begin(); it != collectNodes.end(); ++it) { if ((*it)->isActivated() && (*it)->getNodeGui() && ( (*it)->getKnobs().size() > 0 ) ) { _imp->allNodes.push_back(*it); } } if (isCollecGroup) { _imp->allNodes.push_back( isCollecGroup->getNode() ); } if (isGroup) { NodesList groupnodes = isGroup->getNodes(); for (NodesList::iterator it = groupnodes.begin(); it != groupnodes.end(); ++it) { if ( (*it)->getNodeGui() && (*it)->isActivated() && ( (*it)->getKnobs().size() > 0 ) ) { _imp->allNodes.push_back(*it); } } } QStringList nodeNames; for (NodesList::iterator it = _imp->allNodes.begin(); it != _imp->allNodes.end(); ++it) { QString name = QString::fromUtf8( (*it)->getLabel().c_str() ); nodeNames.push_back(name); } nodeNames.sort(); _imp->mainLayout = new QGridLayout(this); _imp->selectNodeLabel = new Label( tr("Node:") ); _imp->nodeSelectionCombo = new CompleterLineEdit(nodeNames, nodeNames, false, this); _imp->nodeSelectionCombo->setToolTip( NATRON_NAMESPACE::convertFromPlainText(tr("Input the name of a node in the current project."), NATRON_NAMESPACE::WhiteSpaceNormal) ); _imp->nodeSelectionCombo->setFocus(Qt::PopupFocusReason); _imp->knobSelectionCombo = new ComboBox(this); QObject::connect( _imp->knobSelectionCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(onKnobComboIndexChanged(int)) ); QString useAliasTt = NATRON_NAMESPACE::convertFromPlainText(tr("If checked, an alias of the selected parameter will be created, coyping entirely its state. " "Only the script-name, label and tooltip will be editable.\n" "For choice parameters this will also " "dynamically refresh the menu entries when the original parameter's menu is changed.\n" "When unchecked, a simple expression will be set linking the two parameters, but things such as dynamic menus " "will be disabled."), NATRON_NAMESPACE::WhiteSpaceNormal); _imp->useAliasLabel = new Label(tr("Make Alias:"), this); _imp->useAliasLabel->setToolTip(useAliasTt); _imp->useAliasCheckBox = new QCheckBox(this); _imp->useAliasCheckBox->setToolTip(useAliasTt); _imp->useAliasCheckBox->setChecked(true); QObject::connect( _imp->nodeSelectionCombo, SIGNAL(itemCompletionChosen()), this, SLOT(onNodeComboEditingFinished()) ); _imp->destPageLabel = new Label(tr("Page:"), this); QString pagett = NATRON_NAMESPACE::convertFromPlainText(tr("Select the page into which the parameter will be created."), NATRON_NAMESPACE::WhiteSpaceNormal); _imp->destPageLabel->setToolTip(pagett); _imp->destPageCombo = new ComboBox(this); _imp->destPageCombo->setToolTip(pagett); QObject::connect( _imp->destPageCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(onPageComboIndexChanged(int)) ); const KnobsVec& knobs = node->getKnobs(); for (std::size_t i = 0; i < knobs.size(); ++i) { if ( knobs[i]->isUserKnob() ) { KnobPagePtr isPage = toKnobPage(knobs[i]); if (isPage) { _imp->pages.push_back(isPage); _imp->destPageCombo->addItem( QString::fromUtf8( isPage->getName().c_str() ) ); } else { KnobGroupPtr isGrp = toKnobGroup(knobs[i]); if (isGrp) { _imp->groups.push_back(isGrp); } } } } if (_imp->destPageCombo->count() == 0) { _imp->destPageLabel->hide(); _imp->destPageCombo->hide(); } _imp->groupLabel = new Label(tr("Group:"), this); QString grouptt = NATRON_NAMESPACE::convertFromPlainText(tr("Select the group into which the parameter will be created."), NATRON_NAMESPACE::WhiteSpaceNormal); _imp->groupCombo = new ComboBox(this); _imp->groupLabel->setToolTip(grouptt); _imp->groupCombo->setToolTip(grouptt); onPageComboIndexChanged(0); _imp->buttons = new DialogButtonBox(QDialogButtonBox::StandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel), Qt::Horizontal, this); QObject::connect( _imp->buttons, SIGNAL(accepted()), this, SLOT(accept()) ); QObject::connect( _imp->buttons, SIGNAL(rejected()), this, SLOT(reject()) ); _imp->mainLayout->addWidget(_imp->selectNodeLabel, 0, 0, 1, 1); _imp->mainLayout->addWidget(_imp->nodeSelectionCombo, 0, 1, 1, 1); _imp->mainLayout->addWidget(_imp->knobSelectionCombo, 0, 2, 1, 1); _imp->mainLayout->addWidget(_imp->useAliasLabel, 1, 0, 1, 1); _imp->mainLayout->addWidget(_imp->useAliasCheckBox, 1, 1, 1, 1); _imp->mainLayout->addWidget(_imp->destPageLabel, 2, 0, 1, 1); _imp->mainLayout->addWidget(_imp->destPageCombo, 2, 1, 1, 1); _imp->mainLayout->addWidget(_imp->groupLabel, 2, 2, 1, 1); _imp->mainLayout->addWidget(_imp->groupCombo, 2, 3, 1, 1); _imp->mainLayout->addWidget(_imp->buttons, 3, 0, 1, 3); QTimer::singleShot( 25, _imp->nodeSelectionCombo, SLOT(showCompleter()) ); }
void Gui::removeViewerTab(ViewerTab* tab, bool initiatedFromNode, bool deleteData) { assert(tab); unregisterTab(tab); if (tab == _imp->_activeViewer) { _imp->_activeViewer = 0; } NodeGraph* graph = 0; NodeGroupPtr isGrp; NodeCollectionPtr collection; if ( tab->getInternalNode() && tab->getInternalNode()->getNode() ) { NodeCollectionPtr collection = tab->getInternalNode()->getNode()->getGroup(); isGrp = toNodeGroup(collection); } if (isGrp) { NodeGraphI* graph_i = isGrp->getNodeGraph(); assert(graph_i); graph = dynamic_cast<NodeGraph*>(graph_i); } else { graph = getNodeGraph(); } assert(graph); if (!graph) { throw std::logic_error(""); } ViewerTab* lastSelectedViewer = graph->getLastSelectedViewer(); if (lastSelectedViewer == tab) { bool foundOne = false; NodesList nodes; if (collection) { nodes = collection->getNodes(); } for (NodesList::iterator it = nodes.begin(); it != nodes.end(); ++it) { ViewerNodePtr isViewer = (*it)->isEffectViewerNode(); if ( !isViewer || ( isViewer == tab->getInternalNode() ) || !(*it)->isActivated() ) { continue; } OpenGLViewerI* viewerI = isViewer->getUiContext(); assert(viewerI); ViewerGL* glViewer = dynamic_cast<ViewerGL*>(viewerI); assert(glViewer); if (glViewer) { graph->setLastSelectedViewer( glViewer->getViewerTab() ); } foundOne = true; break; } if (!foundOne) { graph->setLastSelectedViewer(0); } } ViewerNodePtr viewerNode = tab->getInternalNode(); ViewerInstancePtr internalViewer; if (viewerNode) { internalViewer = viewerNode->getInternalViewerNode(); } if (internalViewer) { internalViewer->abortAnyEvaluation(); if (getApp()->getLastViewerUsingTimeline() == internalViewer) { getApp()->discardLastViewerUsingTimeline(); } } if (!initiatedFromNode) { assert(_imp->_nodeGraphArea); ///call the deleteNode which will call this function again when the node will be deactivated. NodePtr internalNode = tab->getInternalNode()->getNode(); NodeGuiIPtr guiI = internalNode->getNodeGui(); NodeGuiPtr gui = boost::dynamic_pointer_cast<NodeGui>(guiI); assert(gui); NodeGraphI* graph_i = internalNode->getGroup()->getNodeGraph(); assert(graph_i); NodeGraph* graph = dynamic_cast<NodeGraph*>(graph_i); assert(graph); if (graph) { graph->removeNode(gui); } } else { tab->hide(); TabWidget* container = dynamic_cast<TabWidget*>( tab->parentWidget() ); if (container) { container->removeTab(tab, false); } if (deleteData) { QMutexLocker l(&_imp->_viewerTabsMutex); std::list<ViewerTab*>::iterator it = std::find(_imp->_viewerTabs.begin(), _imp->_viewerTabs.end(), tab); if ( it != _imp->_viewerTabs.end() ) { _imp->_viewerTabs.erase(it); } tab->notifyGuiClosingPublic(); tab->deleteLater(); } } Q_EMIT viewersChanged(); } // Gui::removeViewerTab
ViewerTab* Gui::addNewViewerTab(const NodeGuiPtr& node, TabWidget* where) { if (!node) { return 0; } ViewerNodePtr viewer = node->getNode()->isEffectViewerNode(); NodesGuiList activeNodeViewerUi, nodeViewerUi; //Don't create tracker & roto interface for file dialog preview viewer NodeCollectionPtr group = viewer->getNode()->getGroup(); if (group) { if ( !_imp->_viewerTabs.empty() ) { ( *_imp->_viewerTabs.begin() )->getNodesViewerInterface(&nodeViewerUi, &activeNodeViewerUi); } else { NodeGraph* graph = dynamic_cast<NodeGraph*>( group->getNodeGraph() ); if (!graph) { graph = _imp->_nodeGraphArea; } if (graph) { const NodesGuiList & allNodes = graph->getAllActiveNodes(); std::set<std::string> activeNodesPluginID; for (NodesGuiList::const_iterator it = allNodes.begin(); it != allNodes.end(); ++it) { nodeViewerUi.push_back( *it ); std::string pluginID = (*it)->getNode()->getPluginID(); std::set<std::string>::iterator found = activeNodesPluginID.find(pluginID); if ( found == activeNodesPluginID.end() ) { activeNodesPluginID.insert(pluginID); activeNodeViewerUi.push_back(*it); } } } } } std::string nodeName = node->getNode()->getFullyQualifiedName(); for (std::size_t i = 0; i < nodeName.size(); ++i) { if (nodeName[i] == '.') { nodeName[i] = '_'; } } std::string label; NodeGraph::makeFullyQualifiedLabel(node->getNode(), &label); ViewerTab* tab = new ViewerTab(nodeName, nodeViewerUi, activeNodeViewerUi, this, node, where); tab->setLabel(label); QObject::connect( tab->getViewer(), SIGNAL(imageChanged(int,bool)), this, SLOT(onViewerImageChanged(int,bool)) ); { QMutexLocker l(&_imp->_viewerTabsMutex); _imp->_viewerTabs.push_back(tab); if (!_imp->_activeViewer) { _imp->_activeViewer = tab; } } where->appendTab(tab, tab); Q_EMIT viewersChanged(); return tab; } // Gui::addNewViewerTab