diff --git a/doc/distrib/NodeHelpFiles/DSCore.List.IndexOf_img.jpg b/doc/distrib/NodeHelpFiles/DSCore.List.IndexOf_img.jpg
index 335f8615fe4..149d623b394 100644
Binary files a/doc/distrib/NodeHelpFiles/DSCore.List.IndexOf_img.jpg and b/doc/distrib/NodeHelpFiles/DSCore.List.IndexOf_img.jpg differ
diff --git a/src/DocumentationBrowserViewExtension/NodeDocumentationHtmlGenerator.cs b/src/DocumentationBrowserViewExtension/NodeDocumentationHtmlGenerator.cs
index b60426ba4f0..b1eaf43bac9 100644
--- a/src/DocumentationBrowserViewExtension/NodeDocumentationHtmlGenerator.cs
+++ b/src/DocumentationBrowserViewExtension/NodeDocumentationHtmlGenerator.cs
@@ -315,7 +315,8 @@ private static string GetDefaultValueFromDescription(string element)
{
if (line.ToLowerInvariant().Contains(Resources.InputDefaultValue))
{
- return line.Remove(0, 16);
+ var index = line.IndexOf(":");
+ return line.Remove(0, index + 1);
}
}
return string.Empty;
diff --git a/src/DynamoCore/Logging/DynamoAnalyticsClient.cs b/src/DynamoCore/Logging/DynamoAnalyticsClient.cs
index 37ecab06c98..98138164e73 100644
--- a/src/DynamoCore/Logging/DynamoAnalyticsClient.cs
+++ b/src/DynamoCore/Logging/DynamoAnalyticsClient.cs
@@ -124,7 +124,7 @@ public DynamoAnalyticsClient(HostAnalyticsInfo hostAnalyticsInfo)
Session.Start();
var hostName = string.IsNullOrEmpty(hostAnalyticsInfo.HostName) ? Configurations.DynamoAsString : hostAnalyticsInfo.HostName;
- var appversion = hostAnalyticsInfo.HostVersion != null ? hostAnalyticsInfo.HostVersion.ToString() : string.Empty;
+ var appversion = hostAnalyticsInfo.HostVersion?.ToString();
hostInfo = new HostContextInfo() { ParentId = hostAnalyticsInfo.ParentId, SessionId = hostAnalyticsInfo.SessionId };
diff --git a/src/DynamoCore/Models/DynamoModel.cs b/src/DynamoCore/Models/DynamoModel.cs
index e233387a79f..9ac8028a741 100644
--- a/src/DynamoCore/Models/DynamoModel.cs
+++ b/src/DynamoCore/Models/DynamoModel.cs
@@ -98,10 +98,14 @@ public static DynamoPreferencesData Default()
///
public struct HostAnalyticsInfo
{
- // Dynamo variation identified by host.
+ // Dynamo variation identified by host, e.g. Dynamo Revit
public string HostName;
// Dynamo variation version specific to host
public Version HostVersion;
+ // Dynamo host application name, e.g. Revit
+ public string HostProductName;
+ // Dynamo host application version, e.g. 2025.2.0
+ public Version HostProductVersion;
// Dynamo host parent id for analytics purpose.
public string ParentId;
// Dynamo host session id for analytics purpose.
@@ -2946,6 +2950,26 @@ public bool OpenCustomNodeWorkspace(Guid guid)
return false;
}
+ ///
+ /// Opens an existing custom node workspace.
+ ///
+ /// Identifier of the workspace to open
+ /// True if workspace was found and open
+ internal bool OpenCustomNodeWorkspaceSilent(Guid guid)
+ {
+ CustomNodeWorkspaceModel customNodeWorkspace;
+ if (CustomNodeManager.TryGetFunctionWorkspace(guid, IsTestMode, out customNodeWorkspace))
+ {
+ if (!Workspaces.OfType().Contains(customNodeWorkspace))
+ {
+ AddWorkspace(customNodeWorkspace);
+ }
+
+ return true;
+ }
+
+ return false;
+ }
///
/// Adds a node to the current workspace.
diff --git a/src/DynamoCore/PublicAPI.Unshipped.txt b/src/DynamoCore/PublicAPI.Unshipped.txt
index bb9716fd6e0..f7ecb28e9be 100644
--- a/src/DynamoCore/PublicAPI.Unshipped.txt
+++ b/src/DynamoCore/PublicAPI.Unshipped.txt
@@ -2041,6 +2041,8 @@ Dynamo.Models.EvaluationCompletedEventArgs.EvaluationSucceeded.get -> bool
Dynamo.Models.EvaluationCompletedEventArgs.EvaluationTookPlace.get -> bool
Dynamo.Models.HostAnalyticsInfo
Dynamo.Models.HostAnalyticsInfo.HostAnalyticsInfo() -> void
+Dynamo.Models.HostAnalyticsInfo.HostProductName -> string
+Dynamo.Models.HostAnalyticsInfo.HostProductVersion -> System.Version
Dynamo.Models.HostAnalyticsInfo.HostName -> string
Dynamo.Models.HostAnalyticsInfo.HostVersion -> System.Version
Dynamo.Models.HostAnalyticsInfo.ParentId -> string
diff --git a/src/DynamoCoreWpf/Commands/WorkspaceCommands.cs b/src/DynamoCoreWpf/Commands/WorkspaceCommands.cs
index cb402539e11..b15c1194291 100644
--- a/src/DynamoCoreWpf/Commands/WorkspaceCommands.cs
+++ b/src/DynamoCoreWpf/Commands/WorkspaceCommands.cs
@@ -328,6 +328,17 @@ public bool HasSelection
get { return DynamoSelection.Instance.Selection.Count > 0; }
}
+ [JsonIgnore]
+ public bool CanUpdatePythonEngine
+ {
+ get { return DynamoViewModel.CanUpdatePythonNodeEngine(null); }
+ }
+ [JsonIgnore]
+ public bool CanUpdateAllPythonEngine
+ {
+ get { return DynamoViewModel.CanUpdateAllPythonEngine(null); }
+ }
+
[JsonIgnore]
public bool IsGeometryOperationEnabled
{
diff --git a/src/DynamoCoreWpf/Properties/Resources.Designer.cs b/src/DynamoCoreWpf/Properties/Resources.Designer.cs
index 37a97fb1fb5..141cc6e0fd4 100644
--- a/src/DynamoCoreWpf/Properties/Resources.Designer.cs
+++ b/src/DynamoCoreWpf/Properties/Resources.Designer.cs
@@ -1,4 +1,4 @@
-//------------------------------------------------------------------------------
+//------------------------------------------------------------------------------
//
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
@@ -10557,5 +10557,38 @@ public static string ZoomLevel {
return ResourceManager.GetString("ZoomLevel", resourceCulture);
}
}
+
+ ///
+ /// Looks up a localized string similar to Update all {0} python nodes in the current workspace to use {1} engine?.
+ ///
+ public static string UpdateAllPythonEngineWarning
+ {
+ get
+ {
+ return ResourceManager.GetString("UpdateAllPythonEngineWarning", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Update All Python Nodes.
+ ///
+ public static string UpdateAllPythonEngineWarningTitle
+ {
+ get
+ {
+ return ResourceManager.GetString("UpdateAllPythonEngineWarningTitle", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Set Python Engine.
+ ///
+ public static string UpdateAllPythonEngineMainMenuHeader
+ {
+ get
+ {
+ return ResourceManager.GetString("UpdateAllPythonEngineMainMenuHeader", resourceCulture);
+ }
+ }
}
}
diff --git a/src/DynamoCoreWpf/Properties/Resources.en-US.resx b/src/DynamoCoreWpf/Properties/Resources.en-US.resx
index 962c0041ca9..0afa100be71 100644
--- a/src/DynamoCoreWpf/Properties/Resources.en-US.resx
+++ b/src/DynamoCoreWpf/Properties/Resources.en-US.resx
@@ -4025,4 +4025,13 @@ To make this file into a new template, save it to a different folder, then move
Node Help Data is dumped to \"{0}\".
+
+ Update all {0} python nodes in the current workspace to use {1} engine?
+
+
+ Update All Python Nodes
+
+
+ Set Python Engine
+
diff --git a/src/DynamoCoreWpf/Properties/Resources.resx b/src/DynamoCoreWpf/Properties/Resources.resx
index b6262ac5efd..ff5426a0527 100644
--- a/src/DynamoCoreWpf/Properties/Resources.resx
+++ b/src/DynamoCoreWpf/Properties/Resources.resx
@@ -4012,4 +4012,13 @@ To make this file into a new template, save it to a different folder, then move
Node Help Data is dumped to \"{0}\".
+
+ Update all {0} python nodes in the current workspace to use {1} engine?
+
+
+ Update All Python Nodes
+
+
+ Set Python Engine
+
diff --git a/src/DynamoCoreWpf/PublicAPI.Unshipped.txt b/src/DynamoCoreWpf/PublicAPI.Unshipped.txt
index ef105d2fb6a..22f10109f6f 100644
--- a/src/DynamoCoreWpf/PublicAPI.Unshipped.txt
+++ b/src/DynamoCoreWpf/PublicAPI.Unshipped.txt
@@ -2144,6 +2144,8 @@ Dynamo.ViewModels.DynamoViewModel.UngroupAnnotationCommand.get -> Dynamo.UI.Comm
Dynamo.ViewModels.DynamoViewModel.UngroupAnnotationCommand.set -> void
Dynamo.ViewModels.DynamoViewModel.UngroupModelCommand.get -> Dynamo.UI.Commands.DelegateCommand
Dynamo.ViewModels.DynamoViewModel.UngroupModelCommand.set -> void
+Dynamo.ViewModels.DynamoViewModel.UpdateAllPythonEngineCommand.get -> Dynamo.UI.Commands.DelegateCommand
+Dynamo.ViewModels.DynamoViewModel.UpdateAllPythonEngineCommand.set -> void
Dynamo.ViewModels.DynamoViewModel.UpdateGraphicHelpersScale(object parameter) -> void
Dynamo.ViewModels.DynamoViewModel.UpdateGraphicHelpersScaleCommand.get -> Dynamo.UI.Commands.DelegateCommand
Dynamo.ViewModels.DynamoViewModel.UpdateGraphicHelpersScaleCommand.set -> void
@@ -2992,6 +2994,8 @@ Dynamo.ViewModels.WorkspaceViewModel.CanFindNodesFromElements.set -> void
Dynamo.ViewModels.WorkspaceViewModel.CanPaste.get -> bool
Dynamo.ViewModels.WorkspaceViewModel.CanRunNodeToCode.get -> bool
Dynamo.ViewModels.WorkspaceViewModel.CanShowInfoBubble.get -> bool
+Dynamo.ViewModels.WorkspaceViewModel.CanUpdateAllPythonEngine.get -> bool
+Dynamo.ViewModels.WorkspaceViewModel.CanUpdatePythonEngine.get -> bool
Dynamo.ViewModels.WorkspaceViewModel.CanZoomIn.get -> bool
Dynamo.ViewModels.WorkspaceViewModel.CanZoomOut.get -> bool
Dynamo.ViewModels.WorkspaceViewModel.Checksum.get -> string
@@ -5492,6 +5496,9 @@ static Dynamo.Wpf.Properties.Resources.UnknowDateFormat.get -> string
static Dynamo.Wpf.Properties.Resources.UnloadFailureMessageBoxTitle.get -> string
static Dynamo.Wpf.Properties.Resources.UnpinNodeTooltip.get -> string
static Dynamo.Wpf.Properties.Resources.UnsavedChangesMessageBoxTitle.get -> string
+static Dynamo.Wpf.Properties.Resources.UpdateAllPythonEngineMainMenuHeader.get -> string
+static Dynamo.Wpf.Properties.Resources.UpdateAllPythonEngineWarning.get -> string
+static Dynamo.Wpf.Properties.Resources.UpdateAllPythonEngineWarningTitle.get -> string
static Dynamo.Wpf.Properties.Resources.UpdateMessage.get -> string
static Dynamo.Wpf.Properties.Resources.UpdateNodeIconsDebugMenu.get -> string
static Dynamo.Wpf.Properties.Resources.UsageReportPromptDialogTitle.get -> string
diff --git a/src/DynamoCoreWpf/ViewModels/Core/DynamoViewModel.cs b/src/DynamoCoreWpf/ViewModels/Core/DynamoViewModel.cs
index fc339f9b0f9..4a401ee6e3a 100644
--- a/src/DynamoCoreWpf/ViewModels/Core/DynamoViewModel.cs
+++ b/src/DynamoCoreWpf/ViewModels/Core/DynamoViewModel.cs
@@ -15,6 +15,7 @@
using System.Windows.Media;
using System.Windows.Threading;
using Dynamo.Configuration;
+using Dynamo.Controls;
using Dynamo.Core;
using Dynamo.Engine;
using Dynamo.Exceptions;
@@ -22,6 +23,7 @@
using Dynamo.Graph.Annotations;
using Dynamo.Graph.Connectors;
using Dynamo.Graph.Nodes;
+using Dynamo.Graph.Nodes.CustomNodes;
using Dynamo.Graph.Workspaces;
using Dynamo.Interfaces;
using Dynamo.Logging;
@@ -1394,6 +1396,148 @@ private void Paste(object parameter)
RaiseCanExecuteUndoRedo();
}
+ internal bool CanUpdatePythonNodeEngine(object parameter)
+ {
+ if (DynamoSelection.Instance.Selection.Count > 0 && SelectionHasPythonNodes())
+ {
+ return true;
+ }
+ return false;
+ }
+ private bool SelectionHasPythonNodes()
+ {
+ if (GetSelectedPythonNodes().Any())
+ {
+ return true;
+ }
+ return false;
+ }
+ ///
+ /// Updates the engine for the Python nodes,
+ /// if the nodes belong to another workspace (like custom nodes), they will be opened silently.
+ ///
+ ///
+ ///
+ internal void UpdatePythonNodeEngine(PythonNodeBase pythonNode, string engine)
+ {
+ try
+ {
+ var workspaceGUID = Guid.Empty;
+ var cnWorkspace = GetCustomNodeWorkspace(pythonNode);
+ if (cnWorkspace != null)
+ {
+ workspaceGUID = cnWorkspace.Guid;
+ FocusCustomNodeWorkspace(cnWorkspace.CustomNodeId, true);
+ }
+ this.ExecuteCommand(
+ new DynamoModel.UpdateModelValueCommand(
+ workspaceGUID, pythonNode.GUID, nameof(pythonNode.EngineName), engine));
+ pythonNode.OnNodeModified();
+ }
+ catch(Exception ex)
+ {
+ Model.Logger.Log("Failed to update Python node engine: " + ex.Message, LogLevel.Console);
+ }
+
+ }
+ internal void UpdateAllPythonEngine(object param)
+ {
+ var pNodes = GetSelectedPythonNodes(Model.CurrentWorkspace.Nodes);
+ if (pNodes.Count == 0) return;
+ var result = MessageBoxService.Show(
+ Owner,
+ string.Format(Resources.UpdateAllPythonEngineWarning, pNodes.Count, param.ToString()),
+ Resources.UpdateAllPythonEngineWarningTitle,
+ MessageBoxButton.YesNo,
+ MessageBoxImage.Exclamation);
+ if (result == MessageBoxResult.Yes)
+ {
+ pNodes.ForEach(x => UpdatePythonNodeEngine(x, param.ToString()));
+ }
+ }
+ internal bool CanUpdateAllPythonEngine(object param)
+ {
+ return true;
+ }
+
+ ///
+ /// Adds the python engine to the menu items and subscribes to their click event for updating the engine.
+ ///
+ /// List of python nodes
+ /// context menu item to which the engines will be added to
+ /// Update event handler, to trigger engine update for the node
+ /// Python engine to be added
+ /// Should be set to true, if you require to bind the passed
+ /// NodeModel engine value with the menu item, works only when a single node is passed in the list.
+ internal void AddPythonEngineToMenuItems(List pythonNodeModel,
+ MenuItem pythonEngineVersionMenu,
+ RoutedEventHandler updateEngineDelegate,
+ string engineName, bool isBinding = false)
+ {
+ //if all nodes in the selection are set to a specific engine, then that engine will be checked in the list.
+ bool hasCommonEngine = pythonNodeModel.All(x => x.EngineName == engineName);
+ var currentItem = pythonEngineVersionMenu.Items.Cast
+
+ private void AddPythonEngineToMainMenu()
+ {
+ PythonEngineMenu.Items.Clear();
+ var availablePythonEngines = PythonEngineManager.Instance.AvailableEngines.Select(x => x.Name).ToList();
+ availablePythonEngines.Select(pythonEngine => new MenuItem
+ {
+ Header = pythonEngine,
+ Command = dynamoViewModel.UpdateAllPythonEngineCommand,
+ CommandParameter = pythonEngine
+ }).ToList().ForEach(x => PythonEngineMenu.Items.Add(x));
+ }
+
private void OnWorkspaceHidden(WorkspaceModel workspace)
{
CalculateWindowMinWidth();
diff --git a/src/DynamoCoreWpf/Views/Core/WorkspaceView.xaml b/src/DynamoCoreWpf/Views/Core/WorkspaceView.xaml
index 8d8eee1d1f8..7d3f61daa72 100644
--- a/src/DynamoCoreWpf/Views/Core/WorkspaceView.xaml
+++ b/src/DynamoCoreWpf/Views/Core/WorkspaceView.xaml
@@ -381,6 +381,7 @@
@@ -764,6 +765,11 @@
+
+
+
+
+