Skip to content

Commit

Permalink
LiveCharts Tooltip Update (#13776)
Browse files Browse the repository at this point in the history
* Updated Index-Value Line description

- updated the in-depth description of the Index-Value Line Plot chart

* Updated outport tooltipl

- updated live charts input and ouptut tooltip for all charts

* Port Description to Resources

- rework of inport and outport tooltip messages from strings to resources

* Default Values Part 1

- wip for default values

* Default values functional

- now will output default values to go with the image of the chart
- adjusted outputs of some of the nodes
- changed resource strings accordingly

* Port logic, warning and info logic WIP

- the logic is not very clear, still working through it

* Port interaction bugfix, min size for chart node

WIP CHANGES ONLY FOR THE XYLineChartControl for now! The rest of the LiveCharts to follow after approving this one
- fixed a serious issue causing Dynamo to freeze because of a deadlock
- the issue was caused by the Info state of the node interaction inside the DataBridgeCallback
- minor addition to the chart node view - now restricts the minimum size to which the user can resize the window

* All conditions satisfied

- now checks all conditions and combinations of port values
- OK - all ports correct values, no ports - default values, label and value ports correct values + default colors
- Warning - all ports provided but mismatching lists
- Warning - all ports, but wrong port type
- No Warning, no run - missing ports

* Fixed inport names

- fixed x-values, y-values port names

* Rework of default and update of UI control

- fixed a state where the UI would not go back to default state if initialized with null data (after save/open), or would retain default data on UI when wrong/incomplete values were provided to the Node

* Fix null checks

- moved viewmodel properties initialization up before calling the view to prevent null values
- correctly checks for null and empty lists before initializing to allow proper initialization with empty series

* Heat Series Fixed

- following XYLineChart and ScatterPlot now fixed all connectivity issues with the Heat Series

* BarChart fixed

- a follow up for Bar Chart

* Basic Line Chart fix

- follow up with basic line chart

* Pie Chart fix

- finally the changes of pie chart

* Added live chart test conditions

- detailed live chart tests to cover a lot more use cases and interactions
- modified ClearInfoMessages API - now removes `info` state if node is in it

* Recover lost resources

- manually recovered lost resources after merge

* Fix resources en-US file

* Add failure category to live chart tests

* Fix live chart tests

---------

Co-authored-by: reddyashish <[email protected]>
  • Loading branch information
2 people authored and sm6srw committed Apr 5, 2023
1 parent 95872aa commit b52dfdc
Show file tree
Hide file tree
Showing 24 changed files with 2,409 additions and 637 deletions.
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

0 comments on commit b52dfdc

Please sign in to comment.