unfortunately i haven´t found anything in the forum regarding this issue.
i wonder whether it is possible to add dynamically inputpins to a dynamic plugin, basically like we can do in the stallone node. and if yes, how can i call the fields?
But what if you want to add & delete pins from within the plug-in and not through the user editing values with the Inspektor? Are there any examples or do we have to bang our heads against the plug-in specs to find out if there is a way?
For example I would like to create more convenient Cons nodes that auto-magically changes input pin count so there’s always one empty NIL pin to the right so you can just connect stuff without trying to foresee how many inputs you’ll need or constantly have to use the Inspektor to change the input count as your needs change.
where attributes is either an Input-, Output- or ConfigAttribute.
you delete those pins via their Dispose method.
mind you, when implementing your example you might run into two issues:
it’s not possible to set the slice count of an input pin, as the internal data pointer points to the upstream output pin. though the ISpread interface will let you do it (as it buffers data in a managed array), the change won’t get through to vvvv and in the next frame your slice count will be back at one. you’ll probably have to use the Connected, Disconnected events on your last input pin.
adding and deleting pins on the fly, without making use of a config pin, will lead to issues when reopening the patch. config pins are like constants in c#, which can be evaluated at “compile” time so to say. when vvvv creates a node the calling order is like this:
create the node
node creates bunch of pins
call back the node with values from config pins stored in v4p file
optional: node creates additional pins based on values from config pins
connect the node with other nodes as described by v4p file
if you don’t go the route via a config pin i fear connecting your node will fail.
i tried to write down a first draft, didn’t test this code or anything, just to give you a clue how to address the whole thing. hope it helps!
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using VVVV.Hosting.Pins;
using VVVV.PluginInterfaces.V2;
namespace VVVV.Nodes
{
public class AdvancedCons<T> : IPluginEvaluate, IPartImportsSatisfiedNotification
{
[Config("Input Count", DefaultValue = 2)](Config("Input Count", DefaultValue = 2))
public IDiffSpread<int> FInputCountConfigPin;
[Import](Import)
protected IPluginHost2 FPluginHost;
private readonly List<Pin<T>> FInputPins = new List<Pin<T>>();
private Pin<T> FLastPin;
public void OnImportsSatisfied()
{
FInputCountConfigPin.Changed += FInputCountConfigPing_Changed;
// Not sure if Changed is raised here after,
// otherwise you'll have to raise it yourself
}
private Pin<T> LastPin
{
get
{
return FLastPin;
}
set
{
if (FLastPin != null)
{
FLastPin.Connected -= HandleLastPinConnected;
FLastPin.Disconnected -= HandleLastPinDisconnected;
}
FLastPin = value;
if (FLastPin != null)
{
FLastPin.Connected += HandleLastPinConnected;
FLastPin.Disconnected += HandleLastPinDisconnected;
}
}
}
void HandleInputCountConfigPinChanged(IDiffSpread<int> inputCountConfigPin)
{
// Create new pins and set LastPin accordingly
for (int i = FInputPins.Count; i < inputCountConfigPin.SliceCount; i++)
{
Pin<T> pin = PinFactory.CreatePin<T>(FPluginHost, new InputAttribute(string.Format("Input {0}", i)));
LastPin = pin;
}
// Delete old pins and set LastPin accordingly
for (int i = FInputPins.Count - 1; i >= inputCountConfigPin.SliceCount; i--)
{
Pin<T> pin = FInputPins[i](i);
FInputPins.Remove(pin);
LastPin = FInputPins[FInputPins.Count - 1](FInputPins.Count - 1);
pin.Dispose();
}
}
void HandleLastPinConnected(object sender, PinConnectionEventArgs args)
{
// This should trigger HandleInputCountConfigPinChanged
FInputCountConfigPin[0](0)++;
}
void HandleLastPinDisconnected(object sender, PinConnectionEventArgs args)
{
// This should trigger HandleInputCountConfigPinChanged
FInputCountConfigPin[0](0)--;
}
public void Evaluate(int SpreadMax)
{
throw new NotImplementedException();
}
}
}
i tried something similar, but i get weird exceptions. unfortunately the documentation is not really good for that kind of stuff.
what i eventually want to do is create input and output pins according to a configurable scheme.
can you point me in the right direction?
- region usings
using System;
using System.ComponentModel.Composition;
using VVVV.PluginInterfaces.V1;
using VVVV.PluginInterfaces.V2;
using VVVV.Utils.VColor;
using VVVV.Utils.VMath;
using VVVV.Core.Logging;
- endregion usings
namespace VVVV.Nodes
{
#region PluginInfo
[PluginInfo(Name = "Sender", Category = "Event", Help = "Basic template with one value in/out", Tags = "")](PluginInfo(Name = "Sender", Category = "Event", Help = "Basic template with one value in/out", Tags = ""))
#endregion PluginInfo
public class EventSenderNode : IPluginEvaluate
{
#region fields & pins
[Input("Create", DefaultValue = 0.0)](Input("Create", DefaultValue = 0.0))
ISpread<bool> FCreate;
[Output("Output")](Output("Output"))
ISpread<double> FOutput;
[Import()](Import())
ILogger FLogger;
IPluginHost2 FHost;
IPinFactory PinFactory;
#endregion fields & pins
//called when data for any output pin is requested
public void Evaluate(int SpreadMax)
{
FOutput.SliceCount = SpreadMax;
if (FCreate[0](0)) {
InputAttribute att = new InputAttribute("test");
att.DefaultValue = 0;
att.Name = "test";
Pin<double> pin = PinFactory.CreatePin<double>(att);
}
for (int i = 0; i < SpreadMax; i++)
FOutput[i](i) = 2;
//FLogger.Log(LogType.Debug, "hi tty!");
}
}
}
i keep getting the same runtime exception if i use this instead:
edit: put in the recommended change
- region usings
using System;
using System.ComponentModel.Composition;
using VVVV.PluginInterfaces.V1;
using VVVV.PluginInterfaces.V2;
using VVVV.Utils.VColor;
using VVVV.Utils.VMath;
using VVVV.Core.Logging;
- endregion usings
namespace VVVV.Nodes
{
#region PluginInfo
[PluginInfo(Name = "Sender", Category = "Event", Help = "Basic template with one value in/out", Tags = "")](PluginInfo(Name = "Sender", Category = "Event", Help = "Basic template with one value in/out", Tags = ""))
#endregion PluginInfo
public class EventSenderNode : IPluginEvaluate
{
#region fields & pins
[Input("Create", DefaultValue = 0.0)](Input("Create", DefaultValue = 0.0))
ISpread<bool> FCreate;
[Output("Output")](Output("Output"))
ISpread<double> FOutput;
[Import()](Import())
ILogger FLogger;
[Import()](Import())
IPluginHost2 FHost;
#endregion fields & pins
//called when data for any output pin is requested
public EventSenderNode() {
// IValueOut pin;
// FHost.CreateValueOutput("config", 1, new string[]() {"x"}, TSliceMode.Single, TPinVisibility.True, out pin);
}
public void Evaluate(int SpreadMax)
{
FOutput.SliceCount = SpreadMax;
if (FCreate[0](0)) {
IValueOut pin;
FHost.CreateValueOutput("test", 1, new string[]() {"x"}, TSliceMode.Dynamic, TPinVisibility.True, out pin);
}
for (int i = 0; i < SpreadMax; i++)
FOutput[i](i) = 2;
//FLogger.Log(LogType.Debug, "hi tty!");
}
}
}
proper documentation will follow, but as of yesterday a new development branch made it into develop where pin creation was refactored. if you use the sdk, pull in latest changes from upstream and have a look at the dynamic plugin Template (Value DynamicPins) to see how to create a stallone like node.
i found a little bit of time to continue working on it and quickly hit the next wall.
the current state of the plugin is still basic, it behaves pretty much like a decons but without the magic of PinGroup, because I want the output pins to be named dynamically.
the wall is when i reload the patch- the dynamically produced pins are gone and if i recreate them during the first evaluate, the corrupt links are long deleted. creating them in the constructor gives exceptions and the method SetPluginHost does not work with V2-plugs.
to use the host in the constructor, you need to use the ImportingConstructor and Import tag together with the constructor. search the forums, there are some examples.
oh, now i found another problem. you cannot read the pin values in the constructor. if you want to define pin names by a pin you have to consider a Config pin and do the pin creation in the config callback.