Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LiveCharts Tooltip Update #13776

Merged
merged 22 commits into from
Mar 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
## In Depth

Basic Line creates a line chart with one or multiple labeled and color-coded lines plotted along a series of points. The number of string values in the list entered in the labels input determines the number of lines in the chart, along with their corresponding labels.
Index-Value Line Plot creates a line chart with one or multiple labeled and color-coded lines plotted along a series of points. The number of string values in the list entered in the labels input determines the number of lines in the chart, along with their corresponding labels.

A list of double value lists entered into the values input determines the position of each point along the line(s). Assign a color for each line by entering a list of color values into the colors input.
___
Expand Down
7 changes: 6 additions & 1 deletion src/DynamoCore/Graph/Nodes/NodeModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1735,10 +1735,15 @@ public virtual void ClearErrorsAndWarnings()

/// <summary>
/// Clears the info messages that are generated when running the graph,
/// the State will be set to ElementState.Dead.
/// the State will be set to ElementState.Active.
/// </summary>
public virtual void ClearInfoMessages()
{
if (State == ElementState.Info)
{
State = ElementState.Active;
}

infos.RemoveWhere(x => x.State == ElementState.Info);
OnNodeInfoMessagesClearing();
}
Expand Down
197 changes: 155 additions & 42 deletions src/Libraries/CoreNodeModelsWpf/Charts/BarChartNodeModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
using Dynamo.UI;
using DynamoServices;
using Dynamo.Wpf.Properties;
using Dynamo.Graph.Connectors;
using System.Linq;
using Newtonsoft.Json.Linq;
using Dynamo.ViewModels;

namespace CoreNodeModelsWpf.Charts
{
Expand All @@ -26,9 +30,16 @@ namespace CoreNodeModelsWpf.Charts
[NodeCategory("Display.Charts.Create")]
[NodeDescription("ChartsBarChartDescription", typeof(CoreNodeModelWpfResources))]
[NodeSearchTags("ChartsBarChartSearchTags", typeof(CoreNodeModelWpfResources))]

[InPortNames("labels", "values", "colors")]
[InPortTypes("List<string>", "List<var>", "List<color>")]
[OutPortTypes("Dictionary<Label, Value>")]
[InPortDescriptions(typeof(CoreNodeModelWpfResources),
"ChartsBarChartLabelsDataPortToolTip",
"ChartsBarChartValuesDataPortToolTip",
"ChartsBarChartColorsDataPortToolTip")]
[OutPortNames("labels:values")]
[OutPortTypes("Dictionary<string, double>")]
[OutPortDescriptions(typeof(CoreNodeModelWpfResources),
"ChartsBarChartLabelsValuesDataPortToolTip")]
[AlsoKnownAs("CoreNodeModelsWpf.Charts.BarChart")]
public class BarChartNodeModel : NodeModel
{
Expand All @@ -50,6 +61,16 @@ public class BarChartNodeModel : NodeModel
/// Bar chart color values.
/// </summary>
public List<SolidColorBrush> Colors { get; set; }

/// <summary>
/// Triggers when port is connected or disconnected
/// </summary>
public event EventHandler PortUpdated;

protected virtual void OnPortUpdated(EventArgs args)
{
PortUpdated?.Invoke(this, args);
}
#endregion

#region Constructors
Expand All @@ -58,42 +79,50 @@ public class BarChartNodeModel : NodeModel
/// </summary>
public BarChartNodeModel()
{
InPorts.Add(new PortModel(PortType.Input, this, new PortData("labels", "A list of labels for the bar chart categories.")));
InPorts.Add(new PortModel(PortType.Input, this, new PortData("values", "A list (of lists) to supply values for the bars in each category.")));
InPorts.Add(new PortModel(PortType.Input, this, new PortData("colors", "A list of colors for each bar chart category.")));

OutPorts.Add(new PortModel(PortType.Output, this, new PortData("labels:values", "Dictionary containing label:value key-pairs")));

RegisterAllPorts();

PortConnected += BarChartNodeModel_PortConnected;
PortDisconnected += BarChartNodeModel_PortDisconnected;

ArgumentLacing = LacingStrategy.Disabled;
}

[JsonConstructor]
/// <summary>
/// Instantiate a new NodeModel instance.
/// </summary>
[JsonConstructor]
public BarChartNodeModel(IEnumerable<PortModel> inPorts, IEnumerable<PortModel> outPorts) : base(inPorts, outPorts)
{
PortConnected += BarChartNodeModel_PortConnected;
PortDisconnected += BarChartNodeModel_PortDisconnected;
}
#endregion

#region Events
private void BarChartNodeModel_PortDisconnected(PortModel port)
{
OnPortUpdated(null);
// Clear UI when a input port is disconnected
if (port.PortType == PortType.Input && this.State == ElementState.Active)
if (port.PortType == PortType.Input)
{
Labels.Clear();
Values.Clear();
Colors.Clear();
Labels?.Clear();
Values?.Clear();
Colors?.Clear();

RaisePropertyChanged("DataUpdated");
}
}
private void BarChartNodeModel_PortConnected(PortModel port, ConnectorModel arg2)
{
// Reset an info states if any
if (port.PortType == PortType.Input && InPorts[2].IsConnected && NodeInfos.Any(x => x.State.Equals(ElementState.Info)))
{
this.ClearInfoMessages();
}

OnPortUpdated(null);
RaisePropertyChanged("DataUpdated");
}
#endregion

#region databridge
Expand Down Expand Up @@ -126,17 +155,24 @@ private void DataBridgeCallback(object data)
var values = inputs[1] as ArrayList;
var colors = inputs[2] as ArrayList;

// Only continue if key/values match in length
if (labels.Count != values.Count && labels.Count != (values[0] as ArrayList).Count)
if (!InPorts[0].IsConnected && !InPorts[1].IsConnected && !InPorts[2].IsConnected)
{
throw new Exception("Label and Values do not properly align in length.");
return;
}

// Update chart properties
Labels = new List<string>();
Values = new List<List<double>>();
Colors = new List<SolidColorBrush>();

var anyNullData = labels == null || values == null;

// Only continue if key/values match in length
if (anyNullData || labels.Count != values.Count && labels.Count != (values[0] as ArrayList).Count || labels.Count == 0)
{
throw new Exception("Label and Values do not properly align in length.");
}

// If the bar chart contains nested lists
if (values[0] is ArrayList)
{
Expand All @@ -159,14 +195,15 @@ private void DataBridgeCallback(object data)
Color color;
if (colors == null || colors.Count == 0 || colors.Count != labels.Count)
{
if (InPorts[2].IsConnected) return;

// In case colors are not provided, we supply some from the default library of colors
Info(Dynamo.Wpf.Properties.CoreNodeModelWpfResources.ProvideDefaultColorsWarningMessage);

color = Utilities.Colors.GetColor();
}
else
{

var dynColor = (DSCore.Color)colors[i];
color = Color.FromArgb(dynColor.Alpha, dynColor.Red, dynColor.Green, dynColor.Blue);
}
Expand All @@ -191,6 +228,11 @@ private void DataBridgeCallback(object data)
Color color;
if (colors == null || colors.Count == 0 || colors.Count != labels.Count)
{
if (InPorts[3].IsConnected) return;

// In case colors are not provided, we supply some from the default library of colors
Info(Dynamo.Wpf.Properties.CoreNodeModelWpfResources.ProvideDefaultColorsWarningMessage);

color = Utilities.Colors.GetColor();
}
else
Expand Down Expand Up @@ -227,41 +269,61 @@ public override IEnumerable<AssociativeNode> BuildOutputAst(List<AssociativeNode
// WARNING!!!
// Do not throw an exception during AST creation.

// If inputs are not connected return null
if (!InPorts[0].IsConnected ||
!InPorts[1].IsConnected)
AssociativeNode inputNode;

// If inputs are not connected return default input
if (!InPorts[0].IsConnected && !InPorts[1].IsConnected)
{
inputNode = AstFactory.BuildFunctionCall(
new Func<List<string>, List<double>, List<DSCore.Color>, Dictionary<string, double>>(BarChartFunctions.GetNodeInput),
new List<AssociativeNode> { inputAstNodes[0], inputAstNodes[1], inputAstNodes[2] }
);

return new[]
{
AstFactory.BuildAssignment(GetAstIdentifierForOutputIndex(0), AstFactory.BuildNullNode()),
AstFactory.BuildAssignment(GetAstIdentifierForOutputIndex(0), inputNode),
AstFactory.BuildAssignment(
AstFactory.BuildIdentifier(AstIdentifierBase + "_dummy"),
VMDataBridge.DataBridge.GenerateBridgeDataAst(GUID.ToString(), AstFactory.BuildExprList(inputAstNodes)
)
),
};
}

AssociativeNode inputNode;
if (isNestedList)
else if (!InPorts[0].IsConnected || !InPorts[1].IsConnected)
{
inputNode = AstFactory.BuildFunctionCall(
new Func<List<string>, List<List<double>>, List<DSCore.Color>, Dictionary<string, List<double>>>(BarChartFunctions.GetNodeInput),
new List<AssociativeNode> { inputAstNodes[0], inputAstNodes[1], inputAstNodes[2] }
);
return new[]
{
AstFactory.BuildAssignment(GetAstIdentifierForOutputIndex(0), AstFactory.BuildNullNode()),
};
}
else
{
inputNode = AstFactory.BuildFunctionCall(
new Func<List<string>, List<double>, List<DSCore.Color>, Dictionary<string, double>>(BarChartFunctions.GetNodeInput),
new List<AssociativeNode> { inputAstNodes[0], inputAstNodes[1], inputAstNodes[2] }
);
if (isNestedList)
{
inputNode = AstFactory.BuildFunctionCall(
new Func<List<string>, List<List<double>>, List<DSCore.Color>, Dictionary<string, List<double>>>(BarChartFunctions.GetNodeInput),
new List<AssociativeNode> { inputAstNodes[0], inputAstNodes[1], inputAstNodes[2] }
);
}
else
{
inputNode = AstFactory.BuildFunctionCall(
new Func<List<string>, List<double>, List<DSCore.Color>, Dictionary<string, double>>(BarChartFunctions.GetNodeInput),
new List<AssociativeNode> { inputAstNodes[0], inputAstNodes[1], inputAstNodes[2] }
);
}

return new[]
{
AstFactory.BuildAssignment(GetAstIdentifierForOutputIndex(0), inputNode),
AstFactory.BuildAssignment(
AstFactory.BuildIdentifier(AstIdentifierBase + "_dummy"),
VMDataBridge.DataBridge.GenerateBridgeDataAst(GUID.ToString(), AstFactory.BuildExprList(inputAstNodes)
)
),
};
}

return new[]
{
AstFactory.BuildAssignment(GetAstIdentifierForOutputIndex(0), inputNode),
AstFactory.BuildAssignment(
AstFactory.BuildIdentifier(AstIdentifierBase + "_dummy"),
VMDataBridge.DataBridge.GenerateBridgeDataAst(GUID.ToString(), AstFactory.BuildExprList(inputAstNodes)
)
),
};
}
#endregion

Expand All @@ -272,6 +334,7 @@ public override IEnumerable<AssociativeNode> BuildOutputAst(List<AssociativeNode
/// </summary>
public override void Dispose()
{
PortConnected -= BarChartNodeModel_PortConnected;
PortDisconnected -= BarChartNodeModel_PortDisconnected;
VMDataBridge.DataBridge.Instance.UnregisterCallback(GUID.ToString());
}
Expand All @@ -285,6 +348,8 @@ public override void Dispose()
public class BarChartNodeView : INodeViewCustomization<BarChartNodeModel>
{
private BarChartControl barChartControl;
private NodeView view;
private BarChartNodeModel model;

/// <summary>
/// At run-time, this method is called during the node
Expand All @@ -294,6 +359,8 @@ public class BarChartNodeView : INodeViewCustomization<BarChartNodeModel>
/// <param name="nodeView">The NodeView representing the node in the graph.</param>
public void CustomizeView(BarChartNodeModel model, NodeView nodeView)
{
this.model = model;
this.view = nodeView;
barChartControl = new BarChartControl(model);
nodeView.inputGrid.Children.Add(barChartControl);

Expand All @@ -303,6 +370,49 @@ public void CustomizeView(BarChartNodeModel model, NodeView nodeView)

var contextMenu = (nodeView.Content as Grid).ContextMenu;
contextMenu.Items.Add(exportImage);

UpdateDefaultInPortValues();

model.PortUpdated += ModelOnPortUpdated;
}

private void ModelOnPortUpdated(object sender, EventArgs e)
{
UpdateDefaultInPortValues();
}

private void UpdateDefaultInPortValues()
{
if (!this.view.ViewModel.InPorts.Any()) return;
var inPorts = this.view.ViewModel.InPorts;

// Only apply default values if all ports are disconnected
if (!model.IsInErrorState &&
model.State != ElementState.Active &&
!inPorts[0].IsConnected &&
!inPorts[1].IsConnected)
{
((InPortViewModel)inPorts[0]).PortDefaultValueMarkerVisible = true;
((InPortViewModel)inPorts[1]).PortDefaultValueMarkerVisible = true;
}
else
{
((InPortViewModel)inPorts[0]).PortDefaultValueMarkerVisible = false;
((InPortViewModel)inPorts[1]).PortDefaultValueMarkerVisible = false;
}

var allPortsConnected = inPorts[0].IsConnected && inPorts[1].IsConnected && model.State != ElementState.Warning;
var noPortsConnected = !inPorts[0].IsConnected && !inPorts[1].IsConnected;

// The color input uses default values if it's not connected
if (!inPorts[2].IsConnected && (allPortsConnected || noPortsConnected))
{
((InPortViewModel)inPorts[2]).PortDefaultValueMarkerVisible = true;
}
else
{
((InPortViewModel)inPorts[2]).PortDefaultValueMarkerVisible = false;
}
}

private void ExportImage_Click(object sender, RoutedEventArgs e)
Expand All @@ -314,6 +424,9 @@ private void ExportImage_Click(object sender, RoutedEventArgs e)
/// Here you can do any cleanup you require if you've assigned callbacks for particular
/// UI events on your node.
/// </summary>
public void Dispose() { }
public void Dispose()
{
model.PortUpdated -= ModelOnPortUpdated;
}
}
}
Loading