Skip to content

Commit

Permalink
DYN-2763 Add options to change Python Engine in multiple python nodes (
Browse files Browse the repository at this point in the history
  • Loading branch information
zeusongit committed Sep 11, 2024
1 parent 7530abc commit 7827b5e
Show file tree
Hide file tree
Showing 15 changed files with 342 additions and 48 deletions.
20 changes: 20 additions & 0 deletions src/DynamoCore/Models/DynamoModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2946,6 +2946,26 @@ public bool OpenCustomNodeWorkspace(Guid guid)

return false;
}
/// <summary>
/// Opens an existing custom node workspace.
/// </summary>
/// <param name="guid">Identifier of the workspace to open</param>
/// <returns>True if workspace was found and open</returns>
internal bool OpenCustomNodeWorkspaceSilent(Guid guid)
{
CustomNodeWorkspaceModel customNodeWorkspace;
if (CustomNodeManager.TryGetFunctionWorkspace(guid, IsTestMode, out customNodeWorkspace))
{
if (!Workspaces.OfType<CustomNodeWorkspaceModel>().Contains(customNodeWorkspace))
{
AddWorkspace(customNodeWorkspace);
}

return true;
}

return false;
}

/// <summary>
/// Adds a node to the current workspace.
Expand Down
11 changes: 11 additions & 0 deletions src/DynamoCoreWpf/Commands/WorkspaceCommands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down
35 changes: 34 additions & 1 deletion src/DynamoCoreWpf/Properties/Resources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 21 additions & 1 deletion src/DynamoCoreWpf/Properties/Resources.en-US.resx
Original file line number Diff line number Diff line change
Expand Up @@ -4010,4 +4010,24 @@ To make this file into a new template, save it to a different folder, then move
<data name="PartialRenderFailureTitle" xml:space="preserve">
<value>Background Preview had an issue</value>
</data>
</root>
<data name="DynamoViewDebugMenuDumpData" xml:space="preserve">
<value>_Dump Data</value>
<comment>Debug menu | Dump data options</comment>
</data>
<data name="DynamoViewDebugMenuDumpNodeHelpData" xml:space="preserve">
<value>_Dump Node Help Data</value>
<comment>Debug menu | Dump all node help data like hash mapping</comment>
</data>
<data name="NodeHelpIsDumped" xml:space="preserve">
<value>Node Help Data is dumped to \"{0}\".</value>
</data>
<data name="UpdateAllPythonEngineWarning" xml:space="preserve">
<value>Update all {0} python nodes in the current workspace to use {1} engine?</value>
</data>
<data name="UpdateAllPythonEngineWarningTitle" xml:space="preserve">
<value>Update All Python Nodes</value>
</data>
<data name="UpdateAllPythonEngineMainMenuHeader" xml:space="preserve">
<value>Set Python Engine</value>
</data>
</root>
22 changes: 21 additions & 1 deletion src/DynamoCoreWpf/Properties/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -3997,4 +3997,24 @@ To make this file into a new template, save it to a different folder, then move
<data name="PartialRenderFailureTitle" xml:space="preserve">
<value>Background Preview had an issue</value>
</data>
</root>
<data name="DynamoViewDebugMenuDumpData" xml:space="preserve">
<value>_Dump Data</value>
<comment>Debug menu | Dump data options</comment>
</data>
<data name="DynamoViewDebugMenuDumpNodeHelpData" xml:space="preserve">
<value>_Dump Node Help Data</value>
<comment>Debug menu | Dump all node help data like hash mapping</comment>
</data>
<data name="NodeHelpIsDumped" xml:space="preserve">
<value>Node Help Data is dumped to \"{0}\".</value>
</data>
<data name="UpdateAllPythonEngineWarning" xml:space="preserve">
<value>Update all {0} python nodes in the current workspace to use {1} engine?</value>
</data>
<data name="UpdateAllPythonEngineWarningTitle" xml:space="preserve">
<value>Update All Python Nodes</value>
</data>
<data name="UpdateAllPythonEngineMainMenuHeader" xml:space="preserve">
<value>Set Python Engine</value>
</data>
</root>
7 changes: 7 additions & 0 deletions src/DynamoCoreWpf/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2137,6 +2137,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
Expand Down Expand Up @@ -2985,6 +2987,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
Expand Down Expand Up @@ -5481,6 +5485,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
Expand Down
153 changes: 149 additions & 4 deletions src/DynamoCoreWpf/ViewModels/Core/DynamoViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@
using System.Windows.Media;
using System.Windows.Threading;
using Dynamo.Configuration;
using Dynamo.Controls;
using Dynamo.Core;
using Dynamo.Engine;
using Dynamo.Exceptions;
using Dynamo.Graph;
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;
Expand Down Expand Up @@ -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;
}
/// <summary>
/// Updates the engine for the Python nodes,
/// if the nodes belong to another workspace (like custom nodes), they will be opened silently.
/// </summary>
/// <param name="pythonNode"></param>
/// <param name="engine"></param>
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;
}

/// <summary>
/// Adds the python engine to the menu items and subscribes to their click event for updating the engine.
/// </summary>
/// <param name="pythonNodeModel">List of python nodes</param>
/// <param name="pythonEngineVersionMenu">context menu item to which the engines will be added to</param>
/// <param name="updateEngineDelegate">Update event handler, to trigger engine update for the node</param>
/// <param name="engineName">Python engine to be added</param>
/// <param name="isBinding">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.</param>
internal void AddPythonEngineToMenuItems(List<PythonNodeBase> 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<MenuItem>().FirstOrDefault(x => x.Header as string == engineName);
if (currentItem != null)
{
if (pythonNodeModel.Count == 1) return;
currentItem.IsChecked = hasCommonEngine;
return;
}
MenuItem pythonEngineItem = null;
//if single node, then checked property is bound to the engine value, as python node context menu is not recreated
if (pythonNodeModel.Count == 1 && isBinding)
{
var pythonNode = pythonNodeModel.FirstOrDefault(); ;
pythonEngineItem = new MenuItem { Header = engineName, IsCheckable = false };
pythonEngineItem.SetBinding(MenuItem.IsCheckedProperty, new System.Windows.Data.Binding(nameof(pythonNode.EngineName))
{
Source = pythonNode,
Converter = new CompareToParameterConverter(),
ConverterParameter = engineName
});
}
else
{
//when updating multiple nodes checked value is not bound to any specific node,
//rather takes into account all the selected nodes
pythonEngineItem = new MenuItem { Header = engineName, IsCheckable = true };
pythonEngineItem.IsChecked = hasCommonEngine;
}
pythonEngineItem.Click += updateEngineDelegate;
pythonEngineVersionMenu.Items.Add(pythonEngineItem);
}
/// <summary>
/// Gets the Python nodes from the provided list, including python nodes inside custom nodes as well.
/// If no list is provided then the current selection will be considered.
/// </summary>
/// <returns></returns>
internal List<PythonNodeBase> GetSelectedPythonNodes(IEnumerable<NodeModel> nodes = null)
{
if (nodes == null)
{
nodes = DynamoSelection.Instance.Selection.OfType<NodeModel>();
}
var selectedPythonNodes = nodes.OfType<PythonNodeBase>().ToList();
var customNodes = nodes.Where(x => x.IsCustomFunction).ToList();
if (customNodes.Count > 0)
{
foreach (var cNode in customNodes)
{
var customNodeFunction = cNode as Function;
var pythonNodesInCN = customNodeFunction?.Definition.FunctionBody.OfType<PythonNodeBase>().ToList();
if (pythonNodesInCN.Count > 0)
{
selectedPythonNodes.AddRange(pythonNodesInCN);
}
}
}
return selectedPythonNodes;
}
private CustomNodeWorkspaceModel GetCustomNodeWorkspace(NodeModel node)
{
var wg = model.CustomNodeManager.LoadedWorkspaces.Where(x => x.Nodes.Contains(node)).FirstOrDefault();
return wg ?? null;
}
/// <summary>
/// After command framework is implemented, this method should now be only
/// called from a menu item (i.e. Ctrl + W). It should not be used as a way
Expand Down Expand Up @@ -2567,17 +2711,18 @@ internal bool CanShowPackageManager(object parameters)
}

/// <summary>
/// Change the currently visible workspace to a custom node's workspace
/// Change the currently visible workspace to a custom node's workspace, unless the silent flag is set to true.
/// </summary>
/// <param name="symbol">The function definition for the custom node workspace to be viewed</param>
internal void FocusCustomNodeWorkspace(Guid symbol)
/// <param name="silent">When true, the focus will not switch to the workspace, but it will be opened silently.</param>
internal void FocusCustomNodeWorkspace(Guid symbol, bool silent = false)
{
if (symbol == null)
{
throw new Exception(Resources.MessageNodeWithNullFunction);
}

if (model.OpenCustomNodeWorkspace(symbol))
var res = silent ? model.OpenCustomNodeWorkspaceSilent(symbol) : model.OpenCustomNodeWorkspace(symbol);
if (res)
{
//set the zoom and offsets events
CurrentSpace.OnCurrentOffsetChanged(this, new PointEventArgs(new Point2D(CurrentSpace.X, CurrentSpace.Y)));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ private void InitializeDelegateCommands()
ToggleFullscreenWatchShowingCommand = new DelegateCommand(ToggleFullscreenWatchShowing, CanToggleFullscreenWatchShowing);
ToggleBackgroundGridVisibilityCommand = new DelegateCommand(ToggleBackgroundGridVisibility, CanToggleBackgroundGridVisibility);
UpdateGraphicHelpersScaleCommand = new DelegateCommand(UpdateGraphicHelpersScale, CanUpdateGraphicHelpersScale);
AlignSelectedCommand = new DelegateCommand(AlignSelected, CanAlignSelected); ;
AlignSelectedCommand = new DelegateCommand(AlignSelected, CanAlignSelected);
UpdateAllPythonEngineCommand = new DelegateCommand(UpdateAllPythonEngine, CanUpdateAllPythonEngine);
UndoCommand = new DelegateCommand(Undo, CanUndo);
RedoCommand = new DelegateCommand(Redo, CanRedo);
CopyCommand = new DelegateCommand(_ => model.Copy(), CanCopy);
Expand Down Expand Up @@ -136,6 +137,7 @@ private void InitializeDelegateCommands()
public DelegateCommand GoToWorkspaceCommand { get; set; }
public DelegateCommand DeleteCommand { get; set; }
public DelegateCommand AlignSelectedCommand { get; set; }
public DelegateCommand UpdateAllPythonEngineCommand { get; set; }
public DelegateCommand PostUIActivationCommand { get; set; }
public DelegateCommand ToggleFullscreenWatchShowingCommand { get; set; }
public DelegateCommand ToggleBackgroundGridVisibilityCommand { get; set; }
Expand Down
3 changes: 2 additions & 1 deletion src/DynamoCoreWpf/ViewModels/Core/WorkspaceViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1780,7 +1780,8 @@ private void RefreshViewOnSelectionChange(object sender, NotifyCollectionChanged
RaisePropertyChanged("HasSelection");
RaisePropertyChanged("IsGeometryOperationEnabled");
RaisePropertyChanged("AnyNodeVisible");
RaisePropertyChanged("SelectionArgumentLacing");
RaisePropertyChanged("SelectionArgumentLacing");
RaisePropertyChanged("CanUpdatePythonEngine");
}

/// <summary>
Expand Down
2 changes: 2 additions & 0 deletions src/DynamoCoreWpf/Views/Core/DynamoView.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,8 @@
</MenuItem.Header>
</MenuItem>
</MenuItem>
<MenuItem Header="{x:Static p:Resources.UpdateAllPythonEngineMainMenuHeader}"
Name="PythonEngineMenu" />
<Separator />
<MenuItem Header="{x:Static p:Resources.DynamoViewEditMenuCleanupLayout}"
Command="{Binding GraphAutoLayoutCommand}"
Expand Down
Loading

0 comments on commit 7827b5e

Please sign in to comment.