diff --git a/Bonsai.Scripting.Expressions.Design/Bonsai.Scripting.Expressions.Design.csproj b/Bonsai.Scripting.Expressions.Design/Bonsai.Scripting.Expressions.Design.csproj new file mode 100644 index 000000000..f4c97d6a6 --- /dev/null +++ b/Bonsai.Scripting.Expressions.Design/Bonsai.Scripting.Expressions.Design.csproj @@ -0,0 +1,16 @@ + + + + Bonsai - Expression Scripting Design Library + Bonsai Design Library containing editor classes for expression scripting in Bonsai. + Bonsai Rx Scripting Expressions Design + net5.0-windows10.0.19041 + 3.0.0 + + + + + + + + \ No newline at end of file diff --git a/Bonsai.Scripting/ExpressionScriptEditor.cs b/Bonsai.Scripting.Expressions.Design/ExpressionScriptEditor.cs similarity index 94% rename from Bonsai.Scripting/ExpressionScriptEditor.cs rename to Bonsai.Scripting.Expressions.Design/ExpressionScriptEditor.cs index 41bca6cf5..ae8625fe2 100644 --- a/Bonsai.Scripting/ExpressionScriptEditor.cs +++ b/Bonsai.Scripting.Expressions.Design/ExpressionScriptEditor.cs @@ -1,33 +1,33 @@ -using System; -using System.Drawing.Design; -using System.ComponentModel; -using System.Windows.Forms.Design; -using System.Windows.Forms; - -namespace Bonsai.Scripting -{ - public class ExpressionScriptEditor : UITypeEditor - { - public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context) - { - return UITypeEditorEditStyle.Modal; - } - - public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value) - { - var editorService = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService)); - if (editorService != null) - { - var script = value as string; - var editorDialog = new ExpressionScriptEditorDialog(); - editorDialog.Script = script; - if (editorService.ShowDialog(editorDialog) == DialogResult.OK) - { - return editorDialog.Script; - } - } - - return base.EditValue(context, provider, value); - } - } -} +using System; +using System.Drawing.Design; +using System.ComponentModel; +using System.Windows.Forms.Design; +using System.Windows.Forms; + +namespace Bonsai.Scripting.Expressions.Design +{ + public class ExpressionScriptEditor : UITypeEditor + { + public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context) + { + return UITypeEditorEditStyle.Modal; + } + + public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value) + { + var editorService = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService)); + if (editorService != null) + { + var script = value as string; + var editorDialog = new ExpressionScriptEditorDialog(); + editorDialog.Script = script; + if (editorService.ShowDialog(editorDialog) == DialogResult.OK) + { + return editorDialog.Script; + } + } + + return base.EditValue(context, provider, value); + } + } +} diff --git a/Bonsai.Scripting/ExpressionScriptEditorDialog.Designer.cs b/Bonsai.Scripting.Expressions.Design/ExpressionScriptEditorDialog.Designer.cs similarity index 97% rename from Bonsai.Scripting/ExpressionScriptEditorDialog.Designer.cs rename to Bonsai.Scripting.Expressions.Design/ExpressionScriptEditorDialog.Designer.cs index 0070a70c6..47fc25992 100644 --- a/Bonsai.Scripting/ExpressionScriptEditorDialog.Designer.cs +++ b/Bonsai.Scripting.Expressions.Design/ExpressionScriptEditorDialog.Designer.cs @@ -1,96 +1,96 @@ -namespace Bonsai.Scripting -{ - partial class ExpressionScriptEditorDialog - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.scintilla = new ScintillaNET.Scintilla(); - this.okButton = new System.Windows.Forms.Button(); - this.cancelButton = new System.Windows.Forms.Button(); - this.SuspendLayout(); - // - // scintilla - // - this.scintilla.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) - | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.scintilla.Location = new System.Drawing.Point(12, 12); - this.scintilla.Name = "scintilla"; - this.scintilla.Size = new System.Drawing.Size(600, 229); - this.scintilla.TabIndex = 3; - this.scintilla.TabWidth = 2; - this.scintilla.UseTabs = false; - this.scintilla.WrapMode = ScintillaNET.WrapMode.Word; - this.scintilla.KeyDown += new System.Windows.Forms.KeyEventHandler(this.scintilla_KeyDown); - this.scintilla.TextChanged += new System.EventHandler(this.scintilla_TextChanged); - // - // okButton - // - this.okButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.okButton.DialogResult = System.Windows.Forms.DialogResult.OK; - this.okButton.Location = new System.Drawing.Point(456, 247); - this.okButton.Name = "okButton"; - this.okButton.Size = new System.Drawing.Size(75, 23); - this.okButton.TabIndex = 1; - this.okButton.Text = "OK"; - this.okButton.UseVisualStyleBackColor = true; - // - // cancelButton - // - this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; - this.cancelButton.Location = new System.Drawing.Point(537, 247); - this.cancelButton.Name = "cancelButton"; - this.cancelButton.Size = new System.Drawing.Size(75, 23); - this.cancelButton.TabIndex = 2; - this.cancelButton.Text = "Cancel"; - this.cancelButton.UseVisualStyleBackColor = true; - // - // ExpressionScriptEditorDialog - // - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(624, 282); - this.Controls.Add(this.cancelButton); - this.Controls.Add(this.okButton); - this.Controls.Add(this.scintilla); - this.KeyPreview = true; - this.MinimumSize = new System.Drawing.Size(640, 320); - this.Name = "ExpressionScriptEditorDialog"; - this.ShowIcon = false; - this.Text = "Expression Script"; - this.ResumeLayout(false); - - } - - #endregion - - private ScintillaNET.Scintilla scintilla; - private System.Windows.Forms.Button okButton; - private System.Windows.Forms.Button cancelButton; - } +namespace Bonsai.Scripting.Expressions.Design +{ + partial class ExpressionScriptEditorDialog + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.scintilla = new ScintillaNET.Scintilla(); + this.okButton = new System.Windows.Forms.Button(); + this.cancelButton = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // scintilla + // + this.scintilla.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.scintilla.Location = new System.Drawing.Point(12, 12); + this.scintilla.Name = "scintilla"; + this.scintilla.Size = new System.Drawing.Size(600, 229); + this.scintilla.TabIndex = 3; + this.scintilla.TabWidth = 2; + this.scintilla.UseTabs = false; + this.scintilla.WrapMode = ScintillaNET.WrapMode.Word; + this.scintilla.KeyDown += new System.Windows.Forms.KeyEventHandler(this.scintilla_KeyDown); + this.scintilla.TextChanged += new System.EventHandler(this.scintilla_TextChanged); + // + // okButton + // + this.okButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.okButton.DialogResult = System.Windows.Forms.DialogResult.OK; + this.okButton.Location = new System.Drawing.Point(456, 247); + this.okButton.Name = "okButton"; + this.okButton.Size = new System.Drawing.Size(75, 23); + this.okButton.TabIndex = 1; + this.okButton.Text = "OK"; + this.okButton.UseVisualStyleBackColor = true; + // + // cancelButton + // + this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancelButton.Location = new System.Drawing.Point(537, 247); + this.cancelButton.Name = "cancelButton"; + this.cancelButton.Size = new System.Drawing.Size(75, 23); + this.cancelButton.TabIndex = 2; + this.cancelButton.Text = "Cancel"; + this.cancelButton.UseVisualStyleBackColor = true; + // + // ExpressionScriptEditorDialog + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(624, 282); + this.Controls.Add(this.cancelButton); + this.Controls.Add(this.okButton); + this.Controls.Add(this.scintilla); + this.KeyPreview = true; + this.MinimumSize = new System.Drawing.Size(640, 320); + this.Name = "ExpressionScriptEditorDialog"; + this.ShowIcon = false; + this.Text = "Expression Script"; + this.ResumeLayout(false); + + } + + #endregion + + private ScintillaNET.Scintilla scintilla; + private System.Windows.Forms.Button okButton; + private System.Windows.Forms.Button cancelButton; + } } \ No newline at end of file diff --git a/Bonsai.Scripting/ExpressionScriptEditorDialog.cs b/Bonsai.Scripting.Expressions.Design/ExpressionScriptEditorDialog.cs similarity index 96% rename from Bonsai.Scripting/ExpressionScriptEditorDialog.cs rename to Bonsai.Scripting.Expressions.Design/ExpressionScriptEditorDialog.cs index 4d65a093c..ae8b1570d 100644 --- a/Bonsai.Scripting/ExpressionScriptEditorDialog.cs +++ b/Bonsai.Scripting.Expressions.Design/ExpressionScriptEditorDialog.cs @@ -1,72 +1,72 @@ -using ScintillaNET; -using System; -using System.Drawing; -using System.Windows.Forms; - -namespace Bonsai.Scripting -{ - public partial class ExpressionScriptEditorDialog : Form - { - public ExpressionScriptEditorDialog() - { - InitializeComponent(); - scintilla.StyleResetDefault(); - scintilla.Styles[Style.Default].Font = "Consolas"; - scintilla.Styles[Style.Default].Size = 10; - scintilla.StyleClearAll(); - - scintilla.CaretLineBackColor = ColorTranslator.FromHtml("#feefff"); - scintilla.Styles[Style.Cpp.Default].ForeColor = Color.Black; - scintilla.Styles[Style.Cpp.Number].ForeColor = Color.Black; - scintilla.Styles[Style.Cpp.Character].ForeColor = ColorTranslator.FromHtml("#a31515"); - scintilla.Styles[Style.Cpp.String].ForeColor = ColorTranslator.FromHtml("#a31515"); - scintilla.Styles[Style.Cpp.StringEol].ForeColor = ColorTranslator.FromHtml("#a31515"); - scintilla.Styles[Style.Cpp.Word].ForeColor = ColorTranslator.FromHtml("#0000ff"); - scintilla.Styles[Style.Cpp.Word2].ForeColor = ColorTranslator.FromHtml("#2b91af"); - scintilla.Lexer = Lexer.Cpp; - - var types = "Object Boolean Char String SByte Byte Int16 UInt16 Int32 UInt32 Int64 UInt64 Single Double Decimal DateTime DateTimeOffset TimeSpan Guid Math Convert"; - scintilla.SetKeywords(0, "it iif new outerIt as true false null"); - scintilla.SetKeywords(1, string.Join(" ", types, types.ToLowerInvariant())); - } - - public string Script { get; set; } - - protected override void OnLoad(EventArgs e) - { - scintilla.Text = Script; - scintilla.EmptyUndoBuffer(); - if (Owner != null) - { - Icon = Owner.Icon; - ShowIcon = true; - } - - base.OnLoad(e); - } - - protected override void OnKeyDown(KeyEventArgs e) - { - if (e.KeyCode == Keys.Escape && !e.Handled) - { - Close(); - e.Handled = true; - } - - base.OnKeyDown(e); - } - - private void scintilla_KeyDown(object sender, KeyEventArgs e) - { - if (e.KeyCode == Keys.Enter && e.Modifiers == Keys.Control) - { - okButton.PerformClick(); - } - } - - private void scintilla_TextChanged(object sender, EventArgs e) - { - Script = scintilla.Text; - } - } -} +using ScintillaNET; +using System; +using System.Drawing; +using System.Windows.Forms; + +namespace Bonsai.Scripting.Expressions.Design +{ + public partial class ExpressionScriptEditorDialog : Form + { + public ExpressionScriptEditorDialog() + { + InitializeComponent(); + scintilla.StyleResetDefault(); + scintilla.Styles[Style.Default].Font = "Consolas"; + scintilla.Styles[Style.Default].Size = 10; + scintilla.StyleClearAll(); + + scintilla.CaretLineBackColor = ColorTranslator.FromHtml("#feefff"); + scintilla.Styles[Style.Cpp.Default].ForeColor = Color.Black; + scintilla.Styles[Style.Cpp.Number].ForeColor = Color.Black; + scintilla.Styles[Style.Cpp.Character].ForeColor = ColorTranslator.FromHtml("#a31515"); + scintilla.Styles[Style.Cpp.String].ForeColor = ColorTranslator.FromHtml("#a31515"); + scintilla.Styles[Style.Cpp.StringEol].ForeColor = ColorTranslator.FromHtml("#a31515"); + scintilla.Styles[Style.Cpp.Word].ForeColor = ColorTranslator.FromHtml("#0000ff"); + scintilla.Styles[Style.Cpp.Word2].ForeColor = ColorTranslator.FromHtml("#2b91af"); + scintilla.Lexer = Lexer.Cpp; + + var types = "Object Boolean Char String SByte Byte Int16 UInt16 Int32 UInt32 Int64 UInt64 Single Double Decimal DateTime DateTimeOffset TimeSpan Guid Math Convert"; + scintilla.SetKeywords(0, "it iif new outerIt as true false null"); + scintilla.SetKeywords(1, string.Join(" ", types, types.ToLowerInvariant())); + } + + public string Script { get; set; } + + protected override void OnLoad(EventArgs e) + { + scintilla.Text = Script; + scintilla.EmptyUndoBuffer(); + if (Owner != null) + { + Icon = Owner.Icon; + ShowIcon = true; + } + + base.OnLoad(e); + } + + protected override void OnKeyDown(KeyEventArgs e) + { + if (e.KeyCode == Keys.Escape && !e.Handled) + { + Close(); + e.Handled = true; + } + + base.OnKeyDown(e); + } + + private void scintilla_KeyDown(object sender, KeyEventArgs e) + { + if (e.KeyCode == Keys.Enter && e.Modifiers == Keys.Control) + { + okButton.PerformClick(); + } + } + + private void scintilla_TextChanged(object sender, EventArgs e) + { + Script = scintilla.Text; + } + } +} diff --git a/Bonsai.Scripting/ExpressionScriptEditorDialog.resx b/Bonsai.Scripting.Expressions.Design/ExpressionScriptEditorDialog.resx similarity index 97% rename from Bonsai.Scripting/ExpressionScriptEditorDialog.resx rename to Bonsai.Scripting.Expressions.Design/ExpressionScriptEditorDialog.resx index 29dcb1b3a..1af7de150 100644 --- a/Bonsai.Scripting/ExpressionScriptEditorDialog.resx +++ b/Bonsai.Scripting.Expressions.Design/ExpressionScriptEditorDialog.resx @@ -1,120 +1,120 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + \ No newline at end of file diff --git a/Bonsai.Scripting.Expressions/Bonsai.Scripting.Expressions.csproj b/Bonsai.Scripting.Expressions/Bonsai.Scripting.Expressions.csproj new file mode 100644 index 000000000..fd45ae96f --- /dev/null +++ b/Bonsai.Scripting.Expressions/Bonsai.Scripting.Expressions.csproj @@ -0,0 +1,16 @@ + + + + Bonsai - Expression Scripting Library + Bonsai Scripting Library containing expression scripting infrastructure for Bonsai. + Bonsai Rx Scripting Expressions + netstandard2.0 + 3.0.0 + + + + + + + + \ No newline at end of file diff --git a/Bonsai.Scripting/ExpressionCondition.cs b/Bonsai.Scripting.Expressions/ExpressionCondition.cs similarity index 89% rename from Bonsai.Scripting/ExpressionCondition.cs rename to Bonsai.Scripting.Expressions/ExpressionCondition.cs index 44d734861..42657e9f2 100644 --- a/Bonsai.Scripting/ExpressionCondition.cs +++ b/Bonsai.Scripting.Expressions/ExpressionCondition.cs @@ -1,67 +1,67 @@ -using Bonsai.Expressions; -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; -using System.Linq.Expressions; -using System.Reactive.Linq; -using System.Reflection; - -namespace Bonsai.Scripting -{ - [DefaultProperty("Expression")] - [WorkflowElementCategory(ElementCategory.Condition)] - [TypeDescriptionProvider(typeof(ExpressionConditionTypeDescriptionProvider))] - [Description("An expression script used to determine which values of the input sequence are accepted.")] - public class ExpressionCondition : SingleArgumentExpressionBuilder, IScriptingElement - { - static readonly MethodInfo whereMethod = typeof(Observable).GetMethods() - .Single(m => m.Name == "Where" && - m.GetParameters().Length == 2 && - m.GetParameters()[1].ParameterType.GetGenericTypeDefinition() == typeof(Func<,>)); - - public ExpressionCondition() - { - Expression = "it"; - } - - [Category("Design")] - [Externalizable(false)] - [Description("The name of the expression condition.")] - public string Name { get; set; } - - [Category("Design")] - [Externalizable(false)] - [Description("A description for the expression condition.")] - [Editor(DesignTypes.MultilineStringEditor, DesignTypes.UITypeEditor)] - public string Description { get; set; } - - [Editor("Bonsai.Scripting.ExpressionScriptEditor, Bonsai.Scripting", DesignTypes.UITypeEditor)] - [Description("The expression that determines which elements to filter.")] - public string Expression { get; set; } - - public override Expression Build(IEnumerable arguments) - { - var source = arguments.First(); - var sourceType = source.Type.GetGenericArguments()[0]; - var predicate = global::System.Linq.Dynamic.DynamicExpression.ParseLambda(sourceType, typeof(bool), Expression); - return System.Linq.Expressions.Expression.Call(whereMethod.MakeGenericMethod(sourceType), source, predicate); - } - - class ExpressionConditionTypeDescriptionProvider : TypeDescriptionProvider - { - static readonly TypeDescriptionProvider parentProvider = TypeDescriptor.GetProvider(typeof(ExpressionCondition)); - - public ExpressionConditionTypeDescriptionProvider() - : base(parentProvider) - { - } - - public override ICustomTypeDescriptor GetExtendedTypeDescriptor(object instance) - { - return new ScriptingElementTypeDescriptor(instance, - "An expression that is used to filter individual elements of the input sequence."); - } - } - } -} +using Bonsai.Expressions; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Linq.Expressions; +using System.Reactive.Linq; +using System.Reflection; + +namespace Bonsai.Scripting.Expressions +{ + [DefaultProperty("Expression")] + [WorkflowElementCategory(ElementCategory.Condition)] + [TypeDescriptionProvider(typeof(ExpressionConditionTypeDescriptionProvider))] + [Description("An expression script used to determine which values of the input sequence are accepted.")] + public class ExpressionCondition : SingleArgumentExpressionBuilder, IScriptingElement + { + static readonly MethodInfo whereMethod = typeof(Observable).GetMethods() + .Single(m => m.Name == "Where" && + m.GetParameters().Length == 2 && + m.GetParameters()[1].ParameterType.GetGenericTypeDefinition() == typeof(Func<,>)); + + public ExpressionCondition() + { + Expression = "it"; + } + + [Category("Design")] + [Externalizable(false)] + [Description("The name of the expression condition.")] + public string Name { get; set; } + + [Category("Design")] + [Externalizable(false)] + [Description("A description for the expression condition.")] + [Editor(DesignTypes.MultilineStringEditor, DesignTypes.UITypeEditor)] + public string Description { get; set; } + + [Editor("Bonsai.Scripting.Expressions.Design.ExpressionScriptEditor, Bonsai.Scripting.Expressions.Design", DesignTypes.UITypeEditor)] + [Description("The expression that determines which elements to filter.")] + public string Expression { get; set; } + + public override Expression Build(IEnumerable arguments) + { + var source = arguments.First(); + var sourceType = source.Type.GetGenericArguments()[0]; + var predicate = global::System.Linq.Dynamic.Core.DynamicExpressionParser.ParseLambda(sourceType, typeof(bool), Expression); + return System.Linq.Expressions.Expression.Call(whereMethod.MakeGenericMethod(sourceType), source, predicate); + } + + class ExpressionConditionTypeDescriptionProvider : TypeDescriptionProvider + { + static readonly TypeDescriptionProvider parentProvider = TypeDescriptor.GetProvider(typeof(ExpressionCondition)); + + public ExpressionConditionTypeDescriptionProvider() + : base(parentProvider) + { + } + + public override ICustomTypeDescriptor GetExtendedTypeDescriptor(object instance) + { + return new ScriptingElementTypeDescriptor(instance, + "An expression that is used to filter individual elements of the input sequence."); + } + } + } +} diff --git a/Bonsai.Scripting/ExpressionSink.cs b/Bonsai.Scripting.Expressions/ExpressionSink.cs similarity index 89% rename from Bonsai.Scripting/ExpressionSink.cs rename to Bonsai.Scripting.Expressions/ExpressionSink.cs index 115006bcf..5cf30dfad 100644 --- a/Bonsai.Scripting/ExpressionSink.cs +++ b/Bonsai.Scripting.Expressions/ExpressionSink.cs @@ -1,69 +1,69 @@ -using Bonsai.Expressions; -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; -using System.Linq.Expressions; -using System.Reactive.Linq; -using System.Reflection; - -namespace Bonsai.Scripting -{ - [DefaultProperty("Expression")] - [WorkflowElementCategory(ElementCategory.Sink)] - [TypeDescriptionProvider(typeof(ExpressionSinkTypeDescriptionProvider))] - [Description("An expression script used to operate on individual values of the input sequence.")] - public class ExpressionSink : SingleArgumentExpressionBuilder, IScriptingElement - { - static readonly MethodInfo doMethod = typeof(Observable).GetMethods() - .Single(m => m.Name == "Do" && - m.GetParameters().Length == 2 && - m.GetParameters()[1].ParameterType.GetGenericTypeDefinition() == typeof(Action<>)); - - [Category("Design")] - [Externalizable(false)] - [Description("The name of the expression sink.")] - public string Name { get; set; } - - [Category("Design")] - [Externalizable(false)] - [Description("A description for the expression sink.")] - [Editor(DesignTypes.MultilineStringEditor, DesignTypes.UITypeEditor)] - public string Description { get; set; } - - [Editor("Bonsai.Scripting.ExpressionScriptEditor, Bonsai.Scripting", DesignTypes.UITypeEditor)] - [Description("The expression that determines the action to perform on the input elements.")] - public string Expression { get; set; } - - public override Expression Build(IEnumerable arguments) - { - var expression = Expression; - var source = arguments.First(); - if (!string.IsNullOrEmpty(expression)) - { - var sourceType = source.Type.GetGenericArguments()[0]; - var actionType = System.Linq.Expressions.Expression.GetActionType(sourceType); - var itParameter = new[] { System.Linq.Expressions.Expression.Parameter(sourceType, string.Empty) }; - var onNext = global::System.Linq.Dynamic.DynamicExpression.ParseLambda(actionType, itParameter, null, Expression); - return System.Linq.Expressions.Expression.Call(doMethod.MakeGenericMethod(sourceType), source, onNext); - } - else return source; - } - - class ExpressionSinkTypeDescriptionProvider : TypeDescriptionProvider - { - static readonly TypeDescriptionProvider parentProvider = TypeDescriptor.GetProvider(typeof(ExpressionSink)); - - public ExpressionSinkTypeDescriptionProvider() - : base(parentProvider) - { - } - - public override ICustomTypeDescriptor GetExtendedTypeDescriptor(object instance) - { - return new ScriptingElementTypeDescriptor(instance, - "An expression that is used to perform an action on individual elements of the input sequence."); - } - } - } -} +using Bonsai.Expressions; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Linq.Expressions; +using System.Reactive.Linq; +using System.Reflection; + +namespace Bonsai.Scripting.Expressions +{ + [DefaultProperty("Expression")] + [WorkflowElementCategory(ElementCategory.Sink)] + [TypeDescriptionProvider(typeof(ExpressionSinkTypeDescriptionProvider))] + [Description("An expression script used to operate on individual values of the input sequence.")] + public class ExpressionSink : SingleArgumentExpressionBuilder, IScriptingElement + { + static readonly MethodInfo doMethod = typeof(Observable).GetMethods() + .Single(m => m.Name == "Do" && + m.GetParameters().Length == 2 && + m.GetParameters()[1].ParameterType.GetGenericTypeDefinition() == typeof(Action<>)); + + [Category("Design")] + [Externalizable(false)] + [Description("The name of the expression sink.")] + public string Name { get; set; } + + [Category("Design")] + [Externalizable(false)] + [Description("A description for the expression sink.")] + [Editor(DesignTypes.MultilineStringEditor, DesignTypes.UITypeEditor)] + public string Description { get; set; } + + [Editor("Bonsai.Scripting.Expressions.Design.ExpressionScriptEditor, Bonsai.Scripting.Expressions.Design", DesignTypes.UITypeEditor)] + [Description("The expression that determines the action to perform on the input elements.")] + public string Expression { get; set; } + + public override Expression Build(IEnumerable arguments) + { + var expression = Expression; + var source = arguments.First(); + if (!string.IsNullOrEmpty(expression)) + { + var sourceType = source.Type.GetGenericArguments()[0]; + var actionType = System.Linq.Expressions.Expression.GetActionType(sourceType); + var itParameter = new[] { System.Linq.Expressions.Expression.Parameter(sourceType, string.Empty) }; + var onNext = global::System.Linq.Dynamic.Core.DynamicExpressionParser.ParseLambda(actionType, itParameter, null, Expression); + return System.Linq.Expressions.Expression.Call(doMethod.MakeGenericMethod(sourceType), source, onNext); + } + else return source; + } + + class ExpressionSinkTypeDescriptionProvider : TypeDescriptionProvider + { + static readonly TypeDescriptionProvider parentProvider = TypeDescriptor.GetProvider(typeof(ExpressionSink)); + + public ExpressionSinkTypeDescriptionProvider() + : base(parentProvider) + { + } + + public override ICustomTypeDescriptor GetExtendedTypeDescriptor(object instance) + { + return new ScriptingElementTypeDescriptor(instance, + "An expression that is used to perform an action on individual elements of the input sequence."); + } + } + } +} diff --git a/Bonsai.Scripting/ExpressionTransform.cs b/Bonsai.Scripting.Expressions/ExpressionTransform.cs similarity index 89% rename from Bonsai.Scripting/ExpressionTransform.cs rename to Bonsai.Scripting.Expressions/ExpressionTransform.cs index 174b5a4cf..a94110c91 100644 --- a/Bonsai.Scripting/ExpressionTransform.cs +++ b/Bonsai.Scripting.Expressions/ExpressionTransform.cs @@ -1,67 +1,67 @@ -using Bonsai.Expressions; -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; -using System.Linq.Expressions; -using System.Reactive.Linq; -using System.Reflection; - -namespace Bonsai.Scripting -{ - [DefaultProperty("Expression")] - [WorkflowElementCategory(ElementCategory.Transform)] - [TypeDescriptionProvider(typeof(ExpressionTransformTypeDescriptionProvider))] - [Description("An expression script used to transform individual values of the input sequence.")] - public class ExpressionTransform : SingleArgumentExpressionBuilder, IScriptingElement - { - static readonly MethodInfo selectMethod = typeof(Observable).GetMethods() - .Single(m => m.Name == "Select" && - m.GetParameters().Length == 2 && - m.GetParameters()[1].ParameterType.GetGenericTypeDefinition() == typeof(Func<,>)); - - public ExpressionTransform() - { - Expression = "it"; - } - - [Category("Design")] - [Externalizable(false)] - [Description("The name of the expression transform.")] - public string Name { get; set; } - - [Category("Design")] - [Externalizable(false)] - [Description("A description for the expression transform.")] - [Editor(DesignTypes.MultilineStringEditor, DesignTypes.UITypeEditor)] - public string Description { get; set; } - - [Editor("Bonsai.Scripting.ExpressionScriptEditor, Bonsai.Scripting", DesignTypes.UITypeEditor)] - [Description("The expression that determines the operation of the transform.")] - public string Expression { get; set; } - - public override Expression Build(IEnumerable arguments) - { - var source = arguments.First(); - var sourceType = source.Type.GetGenericArguments()[0]; - var selector = global::System.Linq.Dynamic.DynamicExpression.ParseLambda(sourceType, null, Expression); - return System.Linq.Expressions.Expression.Call(selectMethod.MakeGenericMethod(sourceType, selector.ReturnType), source, selector); - } - - class ExpressionTransformTypeDescriptionProvider : TypeDescriptionProvider - { - static readonly TypeDescriptionProvider parentProvider = TypeDescriptor.GetProvider(typeof(ExpressionTransform)); - - public ExpressionTransformTypeDescriptionProvider() - : base(parentProvider) - { - } - - public override ICustomTypeDescriptor GetExtendedTypeDescriptor(object instance) - { - return new ScriptingElementTypeDescriptor(instance, - "An expression that is used to process and convert individual elements of the input sequence."); - } - } - } -} +using Bonsai.Expressions; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Linq.Expressions; +using System.Reactive.Linq; +using System.Reflection; + +namespace Bonsai.Scripting.Expressions +{ + [DefaultProperty("Expression")] + [WorkflowElementCategory(ElementCategory.Transform)] + [TypeDescriptionProvider(typeof(ExpressionTransformTypeDescriptionProvider))] + [Description("An expression script used to transform individual values of the input sequence.")] + public class ExpressionTransform : SingleArgumentExpressionBuilder, IScriptingElement + { + static readonly MethodInfo selectMethod = typeof(Observable).GetMethods() + .Single(m => m.Name == "Select" && + m.GetParameters().Length == 2 && + m.GetParameters()[1].ParameterType.GetGenericTypeDefinition() == typeof(Func<,>)); + + public ExpressionTransform() + { + Expression = "it"; + } + + [Category("Design")] + [Externalizable(false)] + [Description("The name of the expression transform.")] + public string Name { get; set; } + + [Category("Design")] + [Externalizable(false)] + [Description("A description for the expression transform.")] + [Editor(DesignTypes.MultilineStringEditor, DesignTypes.UITypeEditor)] + public string Description { get; set; } + + [Editor("Bonsai.Scripting.Expressions.Design.ExpressionScriptEditor, Bonsai.Scripting.Expressions.Design", DesignTypes.UITypeEditor)] + [Description("The expression that determines the operation of the transform.")] + public string Expression { get; set; } + + public override Expression Build(IEnumerable arguments) + { + var source = arguments.First(); + var sourceType = source.Type.GetGenericArguments()[0]; + var selector = global::System.Linq.Dynamic.Core.DynamicExpressionParser.ParseLambda(sourceType, null, Expression); + return System.Linq.Expressions.Expression.Call(selectMethod.MakeGenericMethod(sourceType, selector.ReturnType), source, selector); + } + + class ExpressionTransformTypeDescriptionProvider : TypeDescriptionProvider + { + static readonly TypeDescriptionProvider parentProvider = TypeDescriptor.GetProvider(typeof(ExpressionTransform)); + + public ExpressionTransformTypeDescriptionProvider() + : base(parentProvider) + { + } + + public override ICustomTypeDescriptor GetExtendedTypeDescriptor(object instance) + { + return new ScriptingElementTypeDescriptor(instance, + "An expression that is used to process and convert individual elements of the input sequence."); + } + } + } +} diff --git a/Bonsai.Scripting.Expressions/IScriptingElement.cs b/Bonsai.Scripting.Expressions/IScriptingElement.cs new file mode 100644 index 000000000..36417da57 --- /dev/null +++ b/Bonsai.Scripting.Expressions/IScriptingElement.cs @@ -0,0 +1,7 @@ +namespace Bonsai.Scripting.Expressions +{ + interface IScriptingElement : INamedElement + { + string Description { get; } + } +} diff --git a/Bonsai.Scripting.Expressions/Properties/AssemblyInfo.cs b/Bonsai.Scripting.Expressions/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..e48fad8a3 --- /dev/null +++ b/Bonsai.Scripting.Expressions/Properties/AssemblyInfo.cs @@ -0,0 +1,7 @@ +using Bonsai; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: XmlNamespacePrefix("clr-namespace:Bonsai.Scripting.Expressions", "scr")] +[assembly: WorkflowNamespaceIcon("Bonsai:ElementIcon.Scripting")] diff --git a/Bonsai.Scripting/ScriptingElementTypeDescriptor.cs b/Bonsai.Scripting.Expressions/ScriptingElementTypeDescriptor.cs similarity index 87% rename from Bonsai.Scripting/ScriptingElementTypeDescriptor.cs rename to Bonsai.Scripting.Expressions/ScriptingElementTypeDescriptor.cs index cb8d31826..63d0a4ab5 100644 --- a/Bonsai.Scripting/ScriptingElementTypeDescriptor.cs +++ b/Bonsai.Scripting.Expressions/ScriptingElementTypeDescriptor.cs @@ -1,33 +1,33 @@ -using System; -using System.ComponentModel; - -namespace Bonsai.Scripting -{ - class ScriptingElementTypeDescriptor : CustomTypeDescriptor - { - readonly IScriptingElement element; - readonly string defaultDescription; - - public ScriptingElementTypeDescriptor(object instance, string description) - { - if (instance == null) - { - throw new ArgumentNullException("instance"); - } - - element = instance as IScriptingElement; - defaultDescription = description; - } - - public override AttributeCollection GetAttributes() - { - var description = defaultDescription; - if (element != null && !string.IsNullOrEmpty(element.Description)) - { - description = element.Description; - } - - return new AttributeCollection(new DescriptionAttribute(description)); - } - } -} +using System; +using System.ComponentModel; + +namespace Bonsai.Scripting.Expressions +{ + class ScriptingElementTypeDescriptor : CustomTypeDescriptor + { + readonly IScriptingElement element; + readonly string defaultDescription; + + public ScriptingElementTypeDescriptor(object instance, string description) + { + if (instance == null) + { + throw new ArgumentNullException(nameof(instance)); + } + + element = instance as IScriptingElement; + defaultDescription = description; + } + + public override AttributeCollection GetAttributes() + { + var description = defaultDescription; + if (element != null && !string.IsNullOrEmpty(element.Description)) + { + description = element.Description; + } + + return new AttributeCollection(new DescriptionAttribute(description)); + } + } +} diff --git a/Bonsai.Scripting.Python.Design/Bonsai.Scripting.Python.Design.csproj b/Bonsai.Scripting.Python.Design/Bonsai.Scripting.Python.Design.csproj new file mode 100644 index 000000000..1c4c3ce2a --- /dev/null +++ b/Bonsai.Scripting.Python.Design/Bonsai.Scripting.Python.Design.csproj @@ -0,0 +1,16 @@ + + + + Bonsai - Python Scripting Design Library + Bonsai Design Library containing editor classes for Python scripting in Bonsai. + Bonsai Rx Scripting Python Design + net5.0-windows10.0.19041 + 3.0.0 + + + + + + + + \ No newline at end of file diff --git a/Bonsai.Scripting/PythonScriptEditor.cs b/Bonsai.Scripting.Python.Design/PythonScriptEditor.cs similarity index 94% rename from Bonsai.Scripting/PythonScriptEditor.cs rename to Bonsai.Scripting.Python.Design/PythonScriptEditor.cs index 61697a0bf..f2b3835e5 100644 --- a/Bonsai.Scripting/PythonScriptEditor.cs +++ b/Bonsai.Scripting.Python.Design/PythonScriptEditor.cs @@ -1,33 +1,33 @@ -using System; -using System.Drawing.Design; -using System.ComponentModel; -using System.Windows.Forms.Design; -using System.Windows.Forms; - -namespace Bonsai.Scripting -{ - public class PythonScriptEditor : UITypeEditor - { - public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context) - { - return UITypeEditorEditStyle.Modal; - } - - public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value) - { - var editorService = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService)); - if (editorService != null) - { - var script = value as string; - var editorDialog = new PythonScriptEditorDialog(); - editorDialog.Script = script; - if (editorService.ShowDialog(editorDialog) == DialogResult.OK) - { - return editorDialog.Script; - } - } - - return base.EditValue(context, provider, value); - } - } -} +using System; +using System.Drawing.Design; +using System.ComponentModel; +using System.Windows.Forms.Design; +using System.Windows.Forms; + +namespace Bonsai.Scripting.Python.Design +{ + public class PythonScriptEditor : UITypeEditor + { + public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context) + { + return UITypeEditorEditStyle.Modal; + } + + public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value) + { + var editorService = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService)); + if (editorService != null) + { + var script = value as string; + var editorDialog = new PythonScriptEditorDialog(); + editorDialog.Script = script; + if (editorService.ShowDialog(editorDialog) == DialogResult.OK) + { + return editorDialog.Script; + } + } + + return base.EditValue(context, provider, value); + } + } +} diff --git a/Bonsai.Scripting/PythonScriptEditorDialog.Designer.cs b/Bonsai.Scripting.Python.Design/PythonScriptEditorDialog.Designer.cs similarity index 97% rename from Bonsai.Scripting/PythonScriptEditorDialog.Designer.cs rename to Bonsai.Scripting.Python.Design/PythonScriptEditorDialog.Designer.cs index 7e6d429c7..fb3258e4b 100644 --- a/Bonsai.Scripting/PythonScriptEditorDialog.Designer.cs +++ b/Bonsai.Scripting.Python.Design/PythonScriptEditorDialog.Designer.cs @@ -1,96 +1,96 @@ -namespace Bonsai.Scripting -{ - partial class PythonScriptEditorDialog - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.scintilla = new ScintillaNET.Scintilla(); - this.okButton = new System.Windows.Forms.Button(); - this.cancelButton = new System.Windows.Forms.Button(); - this.SuspendLayout(); - // - // scintilla - // - this.scintilla.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) - | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.scintilla.Location = new System.Drawing.Point(12, 12); - this.scintilla.Name = "scintilla"; - this.scintilla.Size = new System.Drawing.Size(600, 229); - this.scintilla.TabIndex = 3; - this.scintilla.TabWidth = 2; - this.scintilla.UseTabs = false; - this.scintilla.WrapMode = ScintillaNET.WrapMode.Word; - this.scintilla.KeyDown += new System.Windows.Forms.KeyEventHandler(this.scintilla_KeyDown); - this.scintilla.TextChanged += new System.EventHandler(this.scintilla_TextChanged); - // - // okButton - // - this.okButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.okButton.DialogResult = System.Windows.Forms.DialogResult.OK; - this.okButton.Location = new System.Drawing.Point(456, 247); - this.okButton.Name = "okButton"; - this.okButton.Size = new System.Drawing.Size(75, 23); - this.okButton.TabIndex = 1; - this.okButton.Text = "OK"; - this.okButton.UseVisualStyleBackColor = true; - // - // cancelButton - // - this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; - this.cancelButton.Location = new System.Drawing.Point(537, 247); - this.cancelButton.Name = "cancelButton"; - this.cancelButton.Size = new System.Drawing.Size(75, 23); - this.cancelButton.TabIndex = 2; - this.cancelButton.Text = "Cancel"; - this.cancelButton.UseVisualStyleBackColor = true; - // - // PythonScriptEditorDialog - // - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(624, 282); - this.Controls.Add(this.cancelButton); - this.Controls.Add(this.okButton); - this.Controls.Add(this.scintilla); - this.KeyPreview = true; - this.MinimumSize = new System.Drawing.Size(640, 320); - this.Name = "PythonScriptEditorDialog"; - this.ShowIcon = false; - this.Text = "Python Script"; - this.ResumeLayout(false); - - } - - #endregion - - private ScintillaNET.Scintilla scintilla; - private System.Windows.Forms.Button okButton; - private System.Windows.Forms.Button cancelButton; - } +namespace Bonsai.Scripting.Python.Design +{ + partial class PythonScriptEditorDialog + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.scintilla = new ScintillaNET.Scintilla(); + this.okButton = new System.Windows.Forms.Button(); + this.cancelButton = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // scintilla + // + this.scintilla.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.scintilla.Location = new System.Drawing.Point(12, 12); + this.scintilla.Name = "scintilla"; + this.scintilla.Size = new System.Drawing.Size(600, 229); + this.scintilla.TabIndex = 3; + this.scintilla.TabWidth = 2; + this.scintilla.UseTabs = false; + this.scintilla.WrapMode = ScintillaNET.WrapMode.Word; + this.scintilla.KeyDown += new System.Windows.Forms.KeyEventHandler(this.scintilla_KeyDown); + this.scintilla.TextChanged += new System.EventHandler(this.scintilla_TextChanged); + // + // okButton + // + this.okButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.okButton.DialogResult = System.Windows.Forms.DialogResult.OK; + this.okButton.Location = new System.Drawing.Point(456, 247); + this.okButton.Name = "okButton"; + this.okButton.Size = new System.Drawing.Size(75, 23); + this.okButton.TabIndex = 1; + this.okButton.Text = "OK"; + this.okButton.UseVisualStyleBackColor = true; + // + // cancelButton + // + this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancelButton.Location = new System.Drawing.Point(537, 247); + this.cancelButton.Name = "cancelButton"; + this.cancelButton.Size = new System.Drawing.Size(75, 23); + this.cancelButton.TabIndex = 2; + this.cancelButton.Text = "Cancel"; + this.cancelButton.UseVisualStyleBackColor = true; + // + // PythonScriptEditorDialog + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(624, 282); + this.Controls.Add(this.cancelButton); + this.Controls.Add(this.okButton); + this.Controls.Add(this.scintilla); + this.KeyPreview = true; + this.MinimumSize = new System.Drawing.Size(640, 320); + this.Name = "PythonScriptEditorDialog"; + this.ShowIcon = false; + this.Text = "Python Script"; + this.ResumeLayout(false); + + } + + #endregion + + private ScintillaNET.Scintilla scintilla; + private System.Windows.Forms.Button okButton; + private System.Windows.Forms.Button cancelButton; + } } \ No newline at end of file diff --git a/Bonsai.Scripting/PythonScriptEditorDialog.cs b/Bonsai.Scripting.Python.Design/PythonScriptEditorDialog.cs similarity index 97% rename from Bonsai.Scripting/PythonScriptEditorDialog.cs rename to Bonsai.Scripting.Python.Design/PythonScriptEditorDialog.cs index 9ca7e5678..ca0a9eaad 100644 --- a/Bonsai.Scripting/PythonScriptEditorDialog.cs +++ b/Bonsai.Scripting.Python.Design/PythonScriptEditorDialog.cs @@ -1,79 +1,79 @@ -using ScintillaNET; -using System; -using System.Drawing; -using System.Windows.Forms; - -namespace Bonsai.Scripting -{ - public partial class PythonScriptEditorDialog : Form - { - public PythonScriptEditorDialog() - { - InitializeComponent(); - scintilla.StyleResetDefault(); - scintilla.Styles[Style.Default].Font = "Consolas"; - scintilla.Styles[Style.Default].Size = 10; - scintilla.StyleClearAll(); - - scintilla.CaretLineBackColor = ColorTranslator.FromHtml("#feefff"); - scintilla.Styles[Style.Python.Default].ForeColor = Color.Black; - scintilla.Styles[Style.Python.Character].ForeColor = ColorTranslator.FromHtml("#00aa00"); - scintilla.Styles[Style.Python.ClassName].ForeColor = Color.Black; - scintilla.Styles[Style.Python.ClassName].Bold = true; - scintilla.Styles[Style.Python.CommentLine].ForeColor = ColorTranslator.FromHtml("#adadad"); - scintilla.Styles[Style.Python.CommentBlock].ForeColor = ColorTranslator.FromHtml("#adadad"); - scintilla.Styles[Style.Python.DefName].ForeColor = Color.Black; - scintilla.Styles[Style.Python.DefName].Bold = true; - scintilla.Styles[Style.Python.Number].ForeColor = ColorTranslator.FromHtml("#800000"); - scintilla.Styles[Style.Python.String].ForeColor = ColorTranslator.FromHtml("#00aa00"); - scintilla.Styles[Style.Python.StringEol].ForeColor = ColorTranslator.FromHtml("#00aa00"); - scintilla.Styles[Style.Python.Triple].ForeColor = ColorTranslator.FromHtml("#adadad"); - scintilla.Styles[Style.Python.TripleDouble].ForeColor = ColorTranslator.FromHtml("#adadad"); - scintilla.Styles[Style.Python.Word].ForeColor = ColorTranslator.FromHtml("#0000ff"); - scintilla.Styles[Style.Python.Word2].ForeColor = ColorTranslator.FromHtml("#900090"); - scintilla.Lexer = Lexer.Python; - - scintilla.SetKeywords(0, "and del from not while as elif global or with assert else if pass yield break except import print class exec in raise continue finally is return def for lambda try"); - scintilla.SetKeywords(1, "self None True False abs divmod input open staticmethod all enumerate int ord str any eval isinstance pow sum basestring execfile issubclass print super bin file iter property tuple bool filter len range type bytearray float list raw_input unichr callable format locals reduce unicode chr frozenset long reload vars classmethod getattr map repr xrange cmp globals max reversed zip compile hasattr memoryview round __import__ complex hash min set delattr help next setattr dict hex object slice dir id oct sorted"); - } - - public string Script { get; set; } - - protected override void OnLoad(EventArgs e) - { - scintilla.Text = Script; - scintilla.EmptyUndoBuffer(); - if (Owner != null) - { - Icon = Owner.Icon; - ShowIcon = true; - } - - base.OnLoad(e); - } - - protected override void OnKeyDown(KeyEventArgs e) - { - if (e.KeyCode == Keys.Escape && !e.Handled) - { - Close(); - e.Handled = true; - } - - base.OnKeyDown(e); - } - - private void scintilla_KeyDown(object sender, KeyEventArgs e) - { - if (e.KeyCode == Keys.Enter && e.Modifiers == Keys.Control) - { - okButton.PerformClick(); - } - } - - private void scintilla_TextChanged(object sender, EventArgs e) - { - Script = scintilla.Text; - } - } -} +using ScintillaNET; +using System; +using System.Drawing; +using System.Windows.Forms; + +namespace Bonsai.Scripting.Python.Design +{ + public partial class PythonScriptEditorDialog : Form + { + public PythonScriptEditorDialog() + { + InitializeComponent(); + scintilla.StyleResetDefault(); + scintilla.Styles[Style.Default].Font = "Consolas"; + scintilla.Styles[Style.Default].Size = 10; + scintilla.StyleClearAll(); + + scintilla.CaretLineBackColor = ColorTranslator.FromHtml("#feefff"); + scintilla.Styles[Style.Python.Default].ForeColor = Color.Black; + scintilla.Styles[Style.Python.Character].ForeColor = ColorTranslator.FromHtml("#00aa00"); + scintilla.Styles[Style.Python.ClassName].ForeColor = Color.Black; + scintilla.Styles[Style.Python.ClassName].Bold = true; + scintilla.Styles[Style.Python.CommentLine].ForeColor = ColorTranslator.FromHtml("#adadad"); + scintilla.Styles[Style.Python.CommentBlock].ForeColor = ColorTranslator.FromHtml("#adadad"); + scintilla.Styles[Style.Python.DefName].ForeColor = Color.Black; + scintilla.Styles[Style.Python.DefName].Bold = true; + scintilla.Styles[Style.Python.Number].ForeColor = ColorTranslator.FromHtml("#800000"); + scintilla.Styles[Style.Python.String].ForeColor = ColorTranslator.FromHtml("#00aa00"); + scintilla.Styles[Style.Python.StringEol].ForeColor = ColorTranslator.FromHtml("#00aa00"); + scintilla.Styles[Style.Python.Triple].ForeColor = ColorTranslator.FromHtml("#adadad"); + scintilla.Styles[Style.Python.TripleDouble].ForeColor = ColorTranslator.FromHtml("#adadad"); + scintilla.Styles[Style.Python.Word].ForeColor = ColorTranslator.FromHtml("#0000ff"); + scintilla.Styles[Style.Python.Word2].ForeColor = ColorTranslator.FromHtml("#900090"); + scintilla.Lexer = Lexer.Python; + + scintilla.SetKeywords(0, "and del from not while as elif global or with assert else if pass yield break except import print class exec in raise continue finally is return def for lambda try"); + scintilla.SetKeywords(1, "self None True False abs divmod input open staticmethod all enumerate int ord str any eval isinstance pow sum basestring execfile issubclass print super bin file iter property tuple bool filter len range type bytearray float list raw_input unichr callable format locals reduce unicode chr frozenset long reload vars classmethod getattr map repr xrange cmp globals max reversed zip compile hasattr memoryview round __import__ complex hash min set delattr help next setattr dict hex object slice dir id oct sorted"); + } + + public string Script { get; set; } + + protected override void OnLoad(EventArgs e) + { + scintilla.Text = Script; + scintilla.EmptyUndoBuffer(); + if (Owner != null) + { + Icon = Owner.Icon; + ShowIcon = true; + } + + base.OnLoad(e); + } + + protected override void OnKeyDown(KeyEventArgs e) + { + if (e.KeyCode == Keys.Escape && !e.Handled) + { + Close(); + e.Handled = true; + } + + base.OnKeyDown(e); + } + + private void scintilla_KeyDown(object sender, KeyEventArgs e) + { + if (e.KeyCode == Keys.Enter && e.Modifiers == Keys.Control) + { + okButton.PerformClick(); + } + } + + private void scintilla_TextChanged(object sender, EventArgs e) + { + Script = scintilla.Text; + } + } +} diff --git a/Bonsai.Scripting/PythonScriptEditorDialog.resx b/Bonsai.Scripting.Python.Design/PythonScriptEditorDialog.resx similarity index 97% rename from Bonsai.Scripting/PythonScriptEditorDialog.resx rename to Bonsai.Scripting.Python.Design/PythonScriptEditorDialog.resx index 29dcb1b3a..1af7de150 100644 --- a/Bonsai.Scripting/PythonScriptEditorDialog.resx +++ b/Bonsai.Scripting.Python.Design/PythonScriptEditorDialog.resx @@ -1,120 +1,120 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + \ No newline at end of file diff --git a/Bonsai.Scripting.Python/Bonsai.Scripting.Python.csproj b/Bonsai.Scripting.Python/Bonsai.Scripting.Python.csproj new file mode 100644 index 000000000..f5e7b78a6 --- /dev/null +++ b/Bonsai.Scripting.Python/Bonsai.Scripting.Python.csproj @@ -0,0 +1,17 @@ + + + + Bonsai - Python Scripting Library + Bonsai Scripting Library containing Python scripting infrastructure for Bonsai. + Bonsai Rx Scripting Python + netstandard2.0 + 3.0.0 + + + + + + + + + \ No newline at end of file diff --git a/Bonsai.Scripting/IScriptingElement.cs b/Bonsai.Scripting.Python/IScriptingElement.cs similarity index 72% rename from Bonsai.Scripting/IScriptingElement.cs rename to Bonsai.Scripting.Python/IScriptingElement.cs index 4ee3c1e2f..b6b81833f 100644 --- a/Bonsai.Scripting/IScriptingElement.cs +++ b/Bonsai.Scripting.Python/IScriptingElement.cs @@ -1,7 +1,7 @@ -namespace Bonsai.Scripting -{ - interface IScriptingElement : INamedElement - { - string Description { get; } - } -} +namespace Bonsai.Scripting.Python +{ + interface IScriptingElement : INamedElement + { + string Description { get; } + } +} diff --git a/Bonsai.Scripting/Properties/AssemblyInfo.cs b/Bonsai.Scripting.Python/Properties/AssemblyInfo.cs similarity index 77% rename from Bonsai.Scripting/Properties/AssemblyInfo.cs rename to Bonsai.Scripting.Python/Properties/AssemblyInfo.cs index cdfc79ed5..2afc5f689 100644 --- a/Bonsai.Scripting/Properties/AssemblyInfo.cs +++ b/Bonsai.Scripting.Python/Properties/AssemblyInfo.cs @@ -1,7 +1,7 @@ -using Bonsai; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: XmlNamespacePrefix("clr-namespace:Bonsai.Scripting", "scr")] -[assembly: WorkflowNamespaceIcon("Bonsai:ElementIcon.Scripting")] +using Bonsai; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: XmlNamespacePrefix("clr-namespace:Bonsai.Scripting.Python", "py")] +[assembly: WorkflowNamespaceIcon("Bonsai:ElementIcon.Scripting")] diff --git a/Bonsai.Scripting/PythonCondition.cs b/Bonsai.Scripting.Python/PythonCondition.cs similarity index 90% rename from Bonsai.Scripting/PythonCondition.cs rename to Bonsai.Scripting.Python/PythonCondition.cs index 800906f18..c1f9723d8 100644 --- a/Bonsai.Scripting/PythonCondition.cs +++ b/Bonsai.Scripting.Python/PythonCondition.cs @@ -1,54 +1,54 @@ -using System; -using System.Linq; -using System.ComponentModel; -using System.Reactive.Linq; - -namespace Bonsai.Scripting -{ - [Obsolete] - [DefaultProperty("Script")] - [WorkflowElementCategory(ElementCategory.Condition)] - [Description("A Python script used to determine which elements of the input sequence are accepted.")] - public class PythonCondition : Combinator - { - public PythonCondition() - { - Script = "def process(value):\n return True"; - } - - [Editor("Bonsai.Scripting.PythonScriptEditor, Bonsai.Scripting", DesignTypes.UITypeEditor)] - [Description("The script that determines the criteria for the condition.")] - public string Script { get; set; } - - public override IObservable Process(IObservable source) - { - return Observable.Defer(() => - { - var engine = PythonEngine.Create(); - var scope = engine.CreateScope(); - engine.Execute(Script, scope); - - object condition; - PythonProcessor processor; - if (PythonHelper.TryGetClass(scope, "Condition", out condition)) - { - processor = new PythonProcessor(engine.Operations, condition); - } - else processor = new PythonProcessor(scope); - - if (processor.Load != null) - { - processor.Load(); - } - - var result = source.Where(processor.Process); - if (processor.Unload != null) - { - result = result.Finally(processor.Unload); - } - - return result; - }); - } - } -} +using System; +using System.Linq; +using System.ComponentModel; +using System.Reactive.Linq; + +namespace Bonsai.Scripting.Python +{ + [Obsolete] + [DefaultProperty("Script")] + [WorkflowElementCategory(ElementCategory.Condition)] + [Description("A Python script used to determine which elements of the input sequence are accepted.")] + public class PythonCondition : Combinator + { + public PythonCondition() + { + Script = "def process(value):\n return True"; + } + + [Editor("Bonsai.Scripting.Python.Design.PythonScriptEditor, Bonsai.Scripting.Python.Design", DesignTypes.UITypeEditor)] + [Description("The script that determines the criteria for the condition.")] + public string Script { get; set; } + + public override IObservable Process(IObservable source) + { + return Observable.Defer(() => + { + var engine = PythonEngine.Create(); + var scope = engine.CreateScope(); + engine.Execute(Script, scope); + + object condition; + PythonProcessor processor; + if (PythonHelper.TryGetClass(scope, "Condition", out condition)) + { + processor = new PythonProcessor(engine.Operations, condition); + } + else processor = new PythonProcessor(scope); + + if (processor.Load != null) + { + processor.Load(); + } + + var result = source.Where(processor.Process); + if (processor.Unload != null) + { + result = result.Finally(processor.Unload); + } + + return result; + }); + } + } +} diff --git a/Bonsai.Scripting/PythonEngine.cs b/Bonsai.Scripting.Python/PythonEngine.cs similarity index 79% rename from Bonsai.Scripting/PythonEngine.cs rename to Bonsai.Scripting.Python/PythonEngine.cs index dfd9f886a..71038b25e 100644 --- a/Bonsai.Scripting/PythonEngine.cs +++ b/Bonsai.Scripting.Python/PythonEngine.cs @@ -1,15 +1,14 @@ -using IronPython.Hosting; -using Microsoft.Scripting.Hosting; +using Microsoft.Scripting.Hosting; using System.IO; using System.Reflection; -namespace Bonsai.Scripting +namespace Bonsai.Scripting.Python { static class PythonEngine { internal static ScriptEngine Create() { - var engine = Python.CreateEngine(); + var engine = global::IronPython.Hosting.Python.CreateEngine(); var basePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); var libPath = Directory.GetDirectories(Path.Combine(basePath, "../../../"), "IronPython.StdLib.*"); if (libPath.Length == 1) diff --git a/Bonsai.Scripting/PythonHelper.cs b/Bonsai.Scripting.Python/PythonHelper.cs similarity index 96% rename from Bonsai.Scripting/PythonHelper.cs rename to Bonsai.Scripting.Python/PythonHelper.cs index 1d2448cdb..78f0fd143 100644 --- a/Bonsai.Scripting/PythonHelper.cs +++ b/Bonsai.Scripting.Python/PythonHelper.cs @@ -1,70 +1,70 @@ -using IronPython.Runtime; -using IronPython.Runtime.Types; -using Microsoft.Scripting.Hosting; -using System; - -namespace Bonsai.Scripting -{ - static class PythonHelper - { - internal const string ReturnsDecorator = "import clr\ndef returns(type):\n def decorator(func):\n func.__returntype__ = clr.GetClrType(type)\n return func\n return decorator\n\n"; - internal const string ReturnTypeAttribute = "__returntype__"; - internal const string LoadFunction = "load"; - internal const string UnloadFunction = "unload"; - internal const string ProcessFunction = "process"; - internal const string GenerateFunction = "generate"; - - internal static bool TryGetClass(ScriptScope scope, string className, out object pythonClass) - { - object variable; - if (scope.TryGetVariable(className, out variable)) - { - pythonClass = variable as OldClass; - if (pythonClass != null) - { - return true; - } - - pythonClass = variable as PythonType; - return pythonClass != null; - } - - pythonClass = null; - return false; - } - - internal static Type GetOutputType(ObjectOperations op, object pythonClass, string methodName) - { - object returnType; - var function = (PythonFunction)op.GetMember(pythonClass, methodName).__func__; - if (function.func_dict.TryGetValue(PythonHelper.ReturnTypeAttribute, out returnType)) - { - return (Type)returnType; - } - - return typeof(object); - } - - internal static Type GetOutputType(ScriptScope scope, string functionName) - { - object returnType; - var function = scope.GetVariable(functionName); - if (function.func_dict.TryGetValue(PythonHelper.ReturnTypeAttribute, out returnType)) - { - return (Type)returnType; - } - else - { - Func getOutputType; - if (scope.TryGetVariable>("getOutputType", out getOutputType)) - { - return getOutputType(); - } - else - { - return typeof(object); - } - } - } - } -} +using IronPython.Runtime; +using IronPython.Runtime.Types; +using Microsoft.Scripting.Hosting; +using System; + +namespace Bonsai.Scripting.Python +{ + static class PythonHelper + { + internal const string ReturnsDecorator = "import clr\ndef returns(type):\n def decorator(func):\n func.__returntype__ = clr.GetClrType(type)\n return func\n return decorator\n\n"; + internal const string ReturnTypeAttribute = "__returntype__"; + internal const string LoadFunction = "load"; + internal const string UnloadFunction = "unload"; + internal const string ProcessFunction = "process"; + internal const string GenerateFunction = "generate"; + + internal static bool TryGetClass(ScriptScope scope, string className, out object pythonClass) + { + object variable; + if (scope.TryGetVariable(className, out variable)) + { + pythonClass = variable as OldClass; + if (pythonClass != null) + { + return true; + } + + pythonClass = variable as PythonType; + return pythonClass != null; + } + + pythonClass = null; + return false; + } + + internal static Type GetOutputType(ObjectOperations op, object pythonClass, string methodName) + { + object returnType; + var function = (PythonFunction)op.GetMember(pythonClass, methodName).__func__; + if (function.func_dict.TryGetValue(PythonHelper.ReturnTypeAttribute, out returnType)) + { + return (Type)returnType; + } + + return typeof(object); + } + + internal static Type GetOutputType(ScriptScope scope, string functionName) + { + object returnType; + var function = scope.GetVariable(functionName); + if (function.func_dict.TryGetValue(PythonHelper.ReturnTypeAttribute, out returnType)) + { + return (Type)returnType; + } + else + { + Func getOutputType; + if (scope.TryGetVariable>("getOutputType", out getOutputType)) + { + return getOutputType(); + } + else + { + return typeof(object); + } + } + } + } +} diff --git a/Bonsai.Scripting/PythonProcessor.cs b/Bonsai.Scripting.Python/PythonProcessor.cs similarity index 95% rename from Bonsai.Scripting/PythonProcessor.cs rename to Bonsai.Scripting.Python/PythonProcessor.cs index 3293079ca..00e6a6808 100644 --- a/Bonsai.Scripting/PythonProcessor.cs +++ b/Bonsai.Scripting.Python/PythonProcessor.cs @@ -1,38 +1,38 @@ -using Microsoft.Scripting.Hosting; -using System; - -namespace Bonsai.Scripting -{ - class PythonProcessor - { - internal PythonProcessor(ScriptScope scope) - { - Action load, unload; - scope.TryGetVariable(PythonHelper.LoadFunction, out load); - scope.TryGetVariable(PythonHelper.UnloadFunction, out unload); - Process = scope.GetVariable>(PythonHelper.ProcessFunction); - Load = load; - Unload = unload; - } - - internal PythonProcessor(ObjectOperations op, object processorClass) - { - var processor = (object)op.CreateInstance(processorClass); - Process = op.GetMember>(processor, PythonHelper.ProcessFunction); - if (op.ContainsMember(processor, PythonHelper.UnloadFunction)) - { - Unload = op.GetMember(processor, PythonHelper.UnloadFunction); - } - if (op.ContainsMember(processor, PythonHelper.LoadFunction)) - { - Load = op.GetMember(processor, PythonHelper.LoadFunction); - } - } - - internal Action Load { get; private set; } - - internal Action Unload { get; private set; } - - internal Func Process { get; private set; } - } -} +using Microsoft.Scripting.Hosting; +using System; + +namespace Bonsai.Scripting.Python +{ + class PythonProcessor + { + internal PythonProcessor(ScriptScope scope) + { + Action load, unload; + scope.TryGetVariable(PythonHelper.LoadFunction, out load); + scope.TryGetVariable(PythonHelper.UnloadFunction, out unload); + Process = scope.GetVariable>(PythonHelper.ProcessFunction); + Load = load; + Unload = unload; + } + + internal PythonProcessor(ObjectOperations op, object processorClass) + { + var processor = (object)op.CreateInstance(processorClass); + Process = op.GetMember>(processor, PythonHelper.ProcessFunction); + if (op.ContainsMember(processor, PythonHelper.UnloadFunction)) + { + Unload = op.GetMember(processor, PythonHelper.UnloadFunction); + } + if (op.ContainsMember(processor, PythonHelper.LoadFunction)) + { + Load = op.GetMember(processor, PythonHelper.LoadFunction); + } + } + + internal Action Load { get; private set; } + + internal Action Unload { get; private set; } + + internal Func Process { get; private set; } + } +} diff --git a/Bonsai.Scripting/PythonSelectMany.cs b/Bonsai.Scripting.Python/PythonSelectMany.cs similarity index 95% rename from Bonsai.Scripting/PythonSelectMany.cs rename to Bonsai.Scripting.Python/PythonSelectMany.cs index d578a18fa..8e51c2152 100644 --- a/Bonsai.Scripting/PythonSelectMany.cs +++ b/Bonsai.Scripting.Python/PythonSelectMany.cs @@ -1,140 +1,140 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.ComponentModel; -using Microsoft.Scripting.Hosting; -using Bonsai.Expressions; -using System.Reactive.Linq; -using System.Linq.Expressions; -using IronPython.Runtime; - -namespace Bonsai.Scripting -{ - [DefaultProperty("Script")] - [WorkflowElementCategory(ElementCategory.Combinator)] - [TypeDescriptionProvider(typeof(PythonSelectManyTypeDescriptionProvider))] - [Description("A Python script used to project each value of the input into an observable sequence.")] - public class PythonSelectMany : SingleArgumentExpressionBuilder, IScriptingElement - { - public PythonSelectMany() - { - Script = "@returns(bool)\ndef process(value):\n yield True"; - } - - /// - /// Gets or sets the name of the python script. - /// - [Category("Design")] - [Externalizable(false)] - [Description("The name of the python script.")] - public string Name { get; set; } - - /// - /// Gets or sets a description for the python script. - /// - [Category("Design")] - [Externalizable(false)] - [Description("A description for the python script.")] - [Editor(DesignTypes.MultilineStringEditor, DesignTypes.UITypeEditor)] - public string Description { get; set; } - - [Editor("Bonsai.Scripting.PythonScriptEditor, Bonsai.Scripting", DesignTypes.UITypeEditor)] - [Description("The script that determines how each element is projected into a sequence.")] - public string Script { get; set; } - - public override Expression Build(IEnumerable arguments) - { - var engine = PythonEngine.Create(); - var scope = engine.CreateScope(); - var script = PythonHelper.ReturnsDecorator + Script; - var scriptSource = engine.CreateScriptSourceFromString(script); - scriptSource.Execute(scope); - - object selectMany; - var source = arguments.Single(); - var observableType = source.Type.GetGenericArguments()[0]; - if (PythonHelper.TryGetClass(scope, "SelectMany", out selectMany)) - { - var classExpression = Expression.Constant(selectMany); - var opExpression = Expression.Constant(engine.Operations); - var outputType = PythonHelper.GetOutputType(engine.Operations, selectMany, PythonHelper.ProcessFunction); - return Expression.Call( - typeof(PythonTransform), - "Process", - new[] { observableType, outputType }, - source, - opExpression, - classExpression); - } - else - { - var outputType = PythonHelper.GetOutputType(scope, PythonHelper.ProcessFunction); - var scopeExpression = Expression.Constant(scope); - return Expression.Call( - typeof(PythonSelectMany), - "Process", - new[] { observableType, outputType }, - source, - scopeExpression); - } - } - - static IObservable Process( - IObservable source, - ObjectOperations op, - object processorClass) - { - return Observable.Defer(() => - { - var processor = new PythonProcessor(op, processorClass); - var result = source.SelectMany(input => processor.Process(input).Cast()); - if (processor.Load != null) processor.Load(); - if (processor.Unload != null) - { - return result.Finally(processor.Unload); - } - else return result; - }); - } - - static IObservable Process( - IObservable source, - ScriptScope scope) - { - var processor = new PythonProcessor(scope); - var result = source.SelectMany(input => processor.Process(input).Cast()); - if (processor.Unload != null) - { - result = result.Finally(processor.Unload); - } - - if (processor.Load != null) - { - var observable = result; - result = Observable.Defer(() => - { - processor.Load(); - return observable; - }); - } - - return result; - } - - class PythonSelectManyTypeDescriptionProvider : TypeDescriptionProvider - { - static readonly TypeDescriptionProvider parentProvider = TypeDescriptor.GetProvider(typeof(PythonSelectMany)); - - public PythonSelectManyTypeDescriptionProvider() - : base(parentProvider) - { - } - - public override ICustomTypeDescriptor GetExtendedTypeDescriptor(object instance) - { - return new ScriptingElementTypeDescriptor(instance, - "A Python script used to project each element of the input sequence into an enumerable sequence."); - } - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.ComponentModel; +using Microsoft.Scripting.Hosting; +using Bonsai.Expressions; +using System.Reactive.Linq; +using System.Linq.Expressions; +using IronPython.Runtime; + +namespace Bonsai.Scripting.Python +{ + [DefaultProperty("Script")] + [WorkflowElementCategory(ElementCategory.Combinator)] + [TypeDescriptionProvider(typeof(PythonSelectManyTypeDescriptionProvider))] + [Description("A Python script used to project each value of the input into an observable sequence.")] + public class PythonSelectMany : SingleArgumentExpressionBuilder, IScriptingElement + { + public PythonSelectMany() + { + Script = "@returns(bool)\ndef process(value):\n yield True"; + } + + /// + /// Gets or sets the name of the python script. + /// + [Category("Design")] + [Externalizable(false)] + [Description("The name of the python script.")] + public string Name { get; set; } + + /// + /// Gets or sets a description for the python script. + /// + [Category("Design")] + [Externalizable(false)] + [Description("A description for the python script.")] + [Editor(DesignTypes.MultilineStringEditor, DesignTypes.UITypeEditor)] + public string Description { get; set; } + + [Editor("Bonsai.Scripting.Python.Design.PythonScriptEditor, Bonsai.Scripting.Python.Design", DesignTypes.UITypeEditor)] + [Description("The script that determines how each element is projected into a sequence.")] + public string Script { get; set; } + + public override Expression Build(IEnumerable arguments) + { + var engine = PythonEngine.Create(); + var scope = engine.CreateScope(); + var script = PythonHelper.ReturnsDecorator + Script; + var scriptSource = engine.CreateScriptSourceFromString(script); + scriptSource.Execute(scope); + + object selectMany; + var source = arguments.Single(); + var observableType = source.Type.GetGenericArguments()[0]; + if (PythonHelper.TryGetClass(scope, "SelectMany", out selectMany)) + { + var classExpression = Expression.Constant(selectMany); + var opExpression = Expression.Constant(engine.Operations); + var outputType = PythonHelper.GetOutputType(engine.Operations, selectMany, PythonHelper.ProcessFunction); + return Expression.Call( + typeof(PythonTransform), + "Process", + new[] { observableType, outputType }, + source, + opExpression, + classExpression); + } + else + { + var outputType = PythonHelper.GetOutputType(scope, PythonHelper.ProcessFunction); + var scopeExpression = Expression.Constant(scope); + return Expression.Call( + typeof(PythonSelectMany), + "Process", + new[] { observableType, outputType }, + source, + scopeExpression); + } + } + + static IObservable Process( + IObservable source, + ObjectOperations op, + object processorClass) + { + return Observable.Defer(() => + { + var processor = new PythonProcessor(op, processorClass); + var result = source.SelectMany(input => processor.Process(input).Cast()); + if (processor.Load != null) processor.Load(); + if (processor.Unload != null) + { + return result.Finally(processor.Unload); + } + else return result; + }); + } + + static IObservable Process( + IObservable source, + ScriptScope scope) + { + var processor = new PythonProcessor(scope); + var result = source.SelectMany(input => processor.Process(input).Cast()); + if (processor.Unload != null) + { + result = result.Finally(processor.Unload); + } + + if (processor.Load != null) + { + var observable = result; + result = Observable.Defer(() => + { + processor.Load(); + return observable; + }); + } + + return result; + } + + class PythonSelectManyTypeDescriptionProvider : TypeDescriptionProvider + { + static readonly TypeDescriptionProvider parentProvider = TypeDescriptor.GetProvider(typeof(PythonSelectMany)); + + public PythonSelectManyTypeDescriptionProvider() + : base(parentProvider) + { + } + + public override ICustomTypeDescriptor GetExtendedTypeDescriptor(object instance) + { + return new ScriptingElementTypeDescriptor(instance, + "A Python script used to project each element of the input sequence into an enumerable sequence."); + } + } + } +} diff --git a/Bonsai.Scripting/PythonSink.cs b/Bonsai.Scripting.Python/PythonSink.cs similarity index 91% rename from Bonsai.Scripting/PythonSink.cs rename to Bonsai.Scripting.Python/PythonSink.cs index b7e5f877c..5698fcf11 100644 --- a/Bonsai.Scripting/PythonSink.cs +++ b/Bonsai.Scripting.Python/PythonSink.cs @@ -1,72 +1,72 @@ -using System; -using System.ComponentModel; -using Microsoft.Scripting.Hosting; -using System.Reactive.Linq; -using System.Threading.Tasks; - -namespace Bonsai.Scripting -{ - [Obsolete] - [DefaultProperty("Script")] - [Description("A Python script used to operate on individual elements of the input sequence.")] - public class PythonSink : Sink - { - public PythonSink() - { - Script = "def process(value):\n return"; - } - - [Editor("Bonsai.Scripting.PythonScriptEditor, Bonsai.Scripting", DesignTypes.UITypeEditor)] - [Description("The script that determines the operation of the sink.")] - public string Script { get; set; } - - protected virtual ScriptEngine CreateEngine() - { - return PythonEngine.Create(); - } - - public override IObservable Process(IObservable source) - { - return Observable.Defer(() => - { - var scriptTask = new Task(() => { }); - scriptTask.Start(); - - var engine = CreateEngine(); - var scope = engine.CreateScope(); - engine.Execute(Script, scope); - - object sink; - PythonProcessor processor; - if (PythonHelper.TryGetClass(scope, "Sink", out sink)) - { - processor = new PythonProcessor(engine.Operations, sink); - } - else processor = new PythonProcessor(scope); - - if (processor.Load != null) - { - processor.Load(); - } - - return source.Do(input => - { - scriptTask = scriptTask.ContinueWith(task => - { - processor.Process(input); - }); - }).Finally(() => - { - var unloadAction = processor.Unload; - if (unloadAction != null) - { - scriptTask = scriptTask.ContinueWith(task => unloadAction()); - } - - engine.Runtime.IO.OutputWriter.Close(); - scriptTask.Wait(); - }); - }); - } - } -} +using System; +using System.ComponentModel; +using Microsoft.Scripting.Hosting; +using System.Reactive.Linq; +using System.Threading.Tasks; + +namespace Bonsai.Scripting.Python +{ + [Obsolete] + [DefaultProperty("Script")] + [Description("A Python script used to operate on individual elements of the input sequence.")] + public class PythonSink : Sink + { + public PythonSink() + { + Script = "def process(value):\n return"; + } + + [Editor("Bonsai.Scripting.Python.Design.PythonScriptEditor, Bonsai.Scripting.Python.Design", DesignTypes.UITypeEditor)] + [Description("The script that determines the operation of the sink.")] + public string Script { get; set; } + + protected virtual ScriptEngine CreateEngine() + { + return PythonEngine.Create(); + } + + public override IObservable Process(IObservable source) + { + return Observable.Defer(() => + { + var scriptTask = new Task(() => { }); + scriptTask.Start(); + + var engine = CreateEngine(); + var scope = engine.CreateScope(); + engine.Execute(Script, scope); + + object sink; + PythonProcessor processor; + if (PythonHelper.TryGetClass(scope, "Sink", out sink)) + { + processor = new PythonProcessor(engine.Operations, sink); + } + else processor = new PythonProcessor(scope); + + if (processor.Load != null) + { + processor.Load(); + } + + return source.Do(input => + { + scriptTask = scriptTask.ContinueWith(task => + { + processor.Process(input); + }); + }).Finally(() => + { + var unloadAction = processor.Unload; + if (unloadAction != null) + { + scriptTask = scriptTask.ContinueWith(task => unloadAction()); + } + + engine.Runtime.IO.OutputWriter.Close(); + scriptTask.Wait(); + }); + }); + } + } +} diff --git a/Bonsai.Scripting/PythonSource.cs b/Bonsai.Scripting.Python/PythonSource.cs similarity index 94% rename from Bonsai.Scripting/PythonSource.cs rename to Bonsai.Scripting.Python/PythonSource.cs index b4be91c67..8dae0c003 100644 --- a/Bonsai.Scripting/PythonSource.cs +++ b/Bonsai.Scripting.Python/PythonSource.cs @@ -1,110 +1,110 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.ComponentModel; -using Bonsai.Expressions; -using System.Reactive.Linq; -using System.Linq.Expressions; -using IronPython.Runtime; -using System.Threading.Tasks; - -namespace Bonsai.Scripting -{ - [DefaultProperty("Script")] - [WorkflowElementCategory(ElementCategory.Source)] - [TypeDescriptionProvider(typeof(PythonSourceTypeDescriptionProvider))] - [Description("A Python script used to generate an observable sequence of values.")] - public class PythonSource : ZeroArgumentExpressionBuilder, IScriptingElement - { - public PythonSource() - { - Script = "@returns(int)\ndef generate():\n yield 0"; - } - - /// - /// Gets or sets the name of the python source. - /// - [Category("Design")] - [Externalizable(false)] - [Description("The name of the python source.")] - public string Name { get; set; } - - /// - /// Gets or sets a description for the python source. - /// - [Category("Design")] - [Externalizable(false)] - [Description("A description for the python source.")] - [Editor(DesignTypes.MultilineStringEditor, DesignTypes.UITypeEditor)] - public string Description { get; set; } - - [Editor("Bonsai.Scripting.PythonScriptEditor, Bonsai.Scripting", DesignTypes.UITypeEditor)] - [Description("The script that determines the operation of the source.")] - public string Script { get; set; } - - public override Expression Build(IEnumerable arguments) - { - var engine = PythonEngine.Create(); - var scope = engine.CreateScope(); - var script = PythonHelper.ReturnsDecorator + Script; - var scriptSource = engine.CreateScriptSourceFromString(script); - scriptSource.Execute(scope); - - var scopeExpression = Expression.Constant(scope); - var outputType = PythonHelper.GetOutputType(scope, PythonHelper.GenerateFunction); - var generatorType = Expression.GetFuncType(typeof(PythonGenerator)); - var generateExpression = Expression.Call( - scopeExpression, - "GetVariable", - new[] { generatorType }, - Expression.Constant(PythonHelper.GenerateFunction)); - - var combinatorExpression = Expression.Constant(this); - return Expression.Call(combinatorExpression, "Generate", new[] { outputType }, generateExpression); - } - - IObservable Generate(Func generate) - { - return Observable.Create(async (observer, token) => - { - await Task.Run(() => - { - try - { - var generator = generate(); - try - { - foreach (var value in generator.Cast()) - { - if (token.IsCancellationRequested) break; - observer.OnNext(value); - } - } - finally { generator.close(); } - observer.OnCompleted(); - } - catch (Exception ex) - { - observer.OnError(ex); - } - }); - }); - } - - class PythonSourceTypeDescriptionProvider : TypeDescriptionProvider - { - static readonly TypeDescriptionProvider parentProvider = TypeDescriptor.GetProvider(typeof(PythonSource)); - - public PythonSourceTypeDescriptionProvider() - : base(parentProvider) - { - } - - public override ICustomTypeDescriptor GetExtendedTypeDescriptor(object instance) - { - return new ScriptingElementTypeDescriptor(instance, - "A Python script used to generate individual elements of an observable sequence."); - } - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.ComponentModel; +using Bonsai.Expressions; +using System.Reactive.Linq; +using System.Linq.Expressions; +using IronPython.Runtime; +using System.Threading.Tasks; + +namespace Bonsai.Scripting.Python +{ + [DefaultProperty("Script")] + [WorkflowElementCategory(ElementCategory.Source)] + [TypeDescriptionProvider(typeof(PythonSourceTypeDescriptionProvider))] + [Description("A Python script used to generate an observable sequence of values.")] + public class PythonSource : ZeroArgumentExpressionBuilder, IScriptingElement + { + public PythonSource() + { + Script = "@returns(int)\ndef generate():\n yield 0"; + } + + /// + /// Gets or sets the name of the python source. + /// + [Category("Design")] + [Externalizable(false)] + [Description("The name of the python source.")] + public string Name { get; set; } + + /// + /// Gets or sets a description for the python source. + /// + [Category("Design")] + [Externalizable(false)] + [Description("A description for the python source.")] + [Editor(DesignTypes.MultilineStringEditor, DesignTypes.UITypeEditor)] + public string Description { get; set; } + + [Editor("Bonsai.Scripting.Python.Design.PythonScriptEditor, Bonsai.Scripting.Python.Design", DesignTypes.UITypeEditor)] + [Description("The script that determines the operation of the source.")] + public string Script { get; set; } + + public override Expression Build(IEnumerable arguments) + { + var engine = PythonEngine.Create(); + var scope = engine.CreateScope(); + var script = PythonHelper.ReturnsDecorator + Script; + var scriptSource = engine.CreateScriptSourceFromString(script); + scriptSource.Execute(scope); + + var scopeExpression = Expression.Constant(scope); + var outputType = PythonHelper.GetOutputType(scope, PythonHelper.GenerateFunction); + var generatorType = Expression.GetFuncType(typeof(PythonGenerator)); + var generateExpression = Expression.Call( + scopeExpression, + "GetVariable", + new[] { generatorType }, + Expression.Constant(PythonHelper.GenerateFunction)); + + var combinatorExpression = Expression.Constant(this); + return Expression.Call(combinatorExpression, "Generate", new[] { outputType }, generateExpression); + } + + IObservable Generate(Func generate) + { + return Observable.Create(async (observer, token) => + { + await Task.Run(() => + { + try + { + var generator = generate(); + try + { + foreach (var value in generator.Cast()) + { + if (token.IsCancellationRequested) break; + observer.OnNext(value); + } + } + finally { generator.close(); } + observer.OnCompleted(); + } + catch (Exception ex) + { + observer.OnError(ex); + } + }); + }); + } + + class PythonSourceTypeDescriptionProvider : TypeDescriptionProvider + { + static readonly TypeDescriptionProvider parentProvider = TypeDescriptor.GetProvider(typeof(PythonSource)); + + public PythonSourceTypeDescriptionProvider() + : base(parentProvider) + { + } + + public override ICustomTypeDescriptor GetExtendedTypeDescriptor(object instance) + { + return new ScriptingElementTypeDescriptor(instance, + "A Python script used to generate individual elements of an observable sequence."); + } + } + } +} diff --git a/Bonsai.Scripting/PythonTextWriter.cs b/Bonsai.Scripting.Python/PythonTextWriter.cs similarity index 95% rename from Bonsai.Scripting/PythonTextWriter.cs rename to Bonsai.Scripting.Python/PythonTextWriter.cs index b3c120246..5731468bd 100644 --- a/Bonsai.Scripting/PythonTextWriter.cs +++ b/Bonsai.Scripting.Python/PythonTextWriter.cs @@ -1,38 +1,38 @@ -using System; -using System.Text; -using System.ComponentModel; -using Microsoft.Scripting.Hosting; -using System.IO; -using Bonsai.IO; - -namespace Bonsai.Scripting -{ - [Obsolete] - [Description("A Python script used to write individual elements of the input sequence to a text file.")] - public class PythonTextWriter : PythonSink - { - StreamWriter writer; - - [Description("The name of the output file.")] - [Editor("Bonsai.Design.SaveFileNameEditor, Bonsai.Design", DesignTypes.UITypeEditor)] - public string FileName { get; set; } - - [Description("Indicates whether to append or overwrite the specified file.")] - public bool Append { get; set; } - - [Description("The optional suffix used to generate file names.")] - public PathSuffix Suffix { get; set; } - - protected override ScriptEngine CreateEngine() - { - var engine = base.CreateEngine(); - if (!string.IsNullOrEmpty(FileName)) - { - var fileName = PathHelper.AppendSuffix(FileName, Suffix); - writer = new StreamWriter(fileName, Append, Encoding.ASCII); - engine.Runtime.IO.SetOutput(writer.BaseStream, writer); - } - return engine; - } - } -} +using System; +using System.Text; +using System.ComponentModel; +using Microsoft.Scripting.Hosting; +using System.IO; +using Bonsai.IO; + +namespace Bonsai.Scripting.Python +{ + [Obsolete] + [Description("A Python script used to write individual elements of the input sequence to a text file.")] + public class PythonTextWriter : PythonSink + { + StreamWriter writer; + + [Description("The name of the output file.")] + [Editor("Bonsai.Design.SaveFileNameEditor, Bonsai.Design", DesignTypes.UITypeEditor)] + public string FileName { get; set; } + + [Description("Indicates whether to append or overwrite the specified file.")] + public bool Append { get; set; } + + [Description("The optional suffix used to generate file names.")] + public PathSuffix Suffix { get; set; } + + protected override ScriptEngine CreateEngine() + { + var engine = base.CreateEngine(); + if (!string.IsNullOrEmpty(FileName)) + { + var fileName = PathHelper.AppendSuffix(FileName, Suffix); + writer = new StreamWriter(fileName, Append, Encoding.ASCII); + engine.Runtime.IO.SetOutput(writer.BaseStream, writer); + } + return engine; + } + } +} diff --git a/Bonsai.Scripting/PythonTransform.cs b/Bonsai.Scripting.Python/PythonTransform.cs similarity index 95% rename from Bonsai.Scripting/PythonTransform.cs rename to Bonsai.Scripting.Python/PythonTransform.cs index 5069b47c5..5a67e1bb0 100644 --- a/Bonsai.Scripting/PythonTransform.cs +++ b/Bonsai.Scripting.Python/PythonTransform.cs @@ -1,139 +1,139 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.ComponentModel; -using Microsoft.Scripting.Hosting; -using Bonsai.Expressions; -using System.Reactive.Linq; -using System.Linq.Expressions; - -namespace Bonsai.Scripting -{ - [DefaultProperty("Script")] - [WorkflowElementCategory(ElementCategory.Transform)] - [TypeDescriptionProvider(typeof(PythonTransformTypeDescriptionProvider))] - [Description("A Python script used to transform individual values of the input sequence.")] - public class PythonTransform : SingleArgumentExpressionBuilder, IScriptingElement - { - public PythonTransform() - { - Script = "@returns(bool)\ndef process(value):\n return True"; - } - - /// - /// Gets or sets the name of the python transform. - /// - [Category("Design")] - [Externalizable(false)] - [Description("The name of the python transform.")] - public string Name { get; set; } - - /// - /// Gets or sets a description for the python transform. - /// - [Category("Design")] - [Externalizable(false)] - [Description("A description for the python transform.")] - [Editor(DesignTypes.MultilineStringEditor, DesignTypes.UITypeEditor)] - public string Description { get; set; } - - [Editor("Bonsai.Scripting.PythonScriptEditor, Bonsai.Scripting", DesignTypes.UITypeEditor)] - [Description("The script that determines the operation of the transform.")] - public string Script { get; set; } - - public override Expression Build(IEnumerable arguments) - { - var engine = PythonEngine.Create(); - var scope = engine.CreateScope(); - var script = PythonHelper.ReturnsDecorator + Script; - var scriptSource = engine.CreateScriptSourceFromString(script); - scriptSource.Execute(scope); - - object transform; - var source = arguments.Single(); - var observableType = source.Type.GetGenericArguments()[0]; - if (PythonHelper.TryGetClass(scope, "Transform", out transform)) - { - var classExpression = Expression.Constant(transform); - var opExpression = Expression.Constant(engine.Operations); - var outputType = PythonHelper.GetOutputType(engine.Operations, transform, PythonHelper.ProcessFunction); - return Expression.Call( - typeof(PythonTransform), - "Process", - new[] { observableType, outputType }, - source, - opExpression, - classExpression); - } - else - { - var outputType = PythonHelper.GetOutputType(scope, PythonHelper.ProcessFunction); - var scopeExpression = Expression.Constant(scope); - return Expression.Call( - typeof(PythonTransform), - "Process", - new[] { observableType, outputType }, - source, - scopeExpression); - } - } - - static IObservable Process( - IObservable source, - ObjectOperations op, - object processorClass) - { - return Observable.Defer(() => - { - var processor = new PythonProcessor(op, processorClass); - var result = source.Select(processor.Process); - if (processor.Load != null) processor.Load(); - if (processor.Unload != null) - { - return result.Finally(processor.Unload); - } - else return result; - }); - } - - static IObservable Process( - IObservable source, - ScriptScope scope) - { - var processor = new PythonProcessor(scope); - var result = source.Select(processor.Process); - if (processor.Unload != null) - { - result = result.Finally(processor.Unload); - } - - if (processor.Load != null) - { - var observable = result; - result = Observable.Defer(() => - { - processor.Load(); - return observable; - }); - } - - return result; - } - - class PythonTransformTypeDescriptionProvider : TypeDescriptionProvider - { - static readonly TypeDescriptionProvider parentProvider = TypeDescriptor.GetProvider(typeof(PythonTransform)); - - public PythonTransformTypeDescriptionProvider() - : base(parentProvider) - { - } - - public override ICustomTypeDescriptor GetExtendedTypeDescriptor(object instance) - { - return new ScriptingElementTypeDescriptor(instance, - "A Python script used to process and convert individual elements of the input sequence."); - } - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.ComponentModel; +using Microsoft.Scripting.Hosting; +using Bonsai.Expressions; +using System.Reactive.Linq; +using System.Linq.Expressions; + +namespace Bonsai.Scripting.Python +{ + [DefaultProperty("Script")] + [WorkflowElementCategory(ElementCategory.Transform)] + [TypeDescriptionProvider(typeof(PythonTransformTypeDescriptionProvider))] + [Description("A Python script used to transform individual values of the input sequence.")] + public class PythonTransform : SingleArgumentExpressionBuilder, IScriptingElement + { + public PythonTransform() + { + Script = "@returns(bool)\ndef process(value):\n return True"; + } + + /// + /// Gets or sets the name of the python transform. + /// + [Category("Design")] + [Externalizable(false)] + [Description("The name of the python transform.")] + public string Name { get; set; } + + /// + /// Gets or sets a description for the python transform. + /// + [Category("Design")] + [Externalizable(false)] + [Description("A description for the python transform.")] + [Editor(DesignTypes.MultilineStringEditor, DesignTypes.UITypeEditor)] + public string Description { get; set; } + + [Editor("Bonsai.Scripting.Python.Design.PythonScriptEditor, Bonsai.Scripting.Python.Design", DesignTypes.UITypeEditor)] + [Description("The script that determines the operation of the transform.")] + public string Script { get; set; } + + public override Expression Build(IEnumerable arguments) + { + var engine = PythonEngine.Create(); + var scope = engine.CreateScope(); + var script = PythonHelper.ReturnsDecorator + Script; + var scriptSource = engine.CreateScriptSourceFromString(script); + scriptSource.Execute(scope); + + object transform; + var source = arguments.Single(); + var observableType = source.Type.GetGenericArguments()[0]; + if (PythonHelper.TryGetClass(scope, "Transform", out transform)) + { + var classExpression = Expression.Constant(transform); + var opExpression = Expression.Constant(engine.Operations); + var outputType = PythonHelper.GetOutputType(engine.Operations, transform, PythonHelper.ProcessFunction); + return Expression.Call( + typeof(PythonTransform), + "Process", + new[] { observableType, outputType }, + source, + opExpression, + classExpression); + } + else + { + var outputType = PythonHelper.GetOutputType(scope, PythonHelper.ProcessFunction); + var scopeExpression = Expression.Constant(scope); + return Expression.Call( + typeof(PythonTransform), + "Process", + new[] { observableType, outputType }, + source, + scopeExpression); + } + } + + static IObservable Process( + IObservable source, + ObjectOperations op, + object processorClass) + { + return Observable.Defer(() => + { + var processor = new PythonProcessor(op, processorClass); + var result = source.Select(processor.Process); + if (processor.Load != null) processor.Load(); + if (processor.Unload != null) + { + return result.Finally(processor.Unload); + } + else return result; + }); + } + + static IObservable Process( + IObservable source, + ScriptScope scope) + { + var processor = new PythonProcessor(scope); + var result = source.Select(processor.Process); + if (processor.Unload != null) + { + result = result.Finally(processor.Unload); + } + + if (processor.Load != null) + { + var observable = result; + result = Observable.Defer(() => + { + processor.Load(); + return observable; + }); + } + + return result; + } + + class PythonTransformTypeDescriptionProvider : TypeDescriptionProvider + { + static readonly TypeDescriptionProvider parentProvider = TypeDescriptor.GetProvider(typeof(PythonTransform)); + + public PythonTransformTypeDescriptionProvider() + : base(parentProvider) + { + } + + public override ICustomTypeDescriptor GetExtendedTypeDescriptor(object instance) + { + return new ScriptingElementTypeDescriptor(instance, + "A Python script used to process and convert individual elements of the input sequence."); + } + } + } +} diff --git a/Bonsai.Scripting.Python/ScriptingElementTypeDescriptor.cs b/Bonsai.Scripting.Python/ScriptingElementTypeDescriptor.cs new file mode 100644 index 000000000..3f535ef3d --- /dev/null +++ b/Bonsai.Scripting.Python/ScriptingElementTypeDescriptor.cs @@ -0,0 +1,33 @@ +using System; +using System.ComponentModel; + +namespace Bonsai.Scripting.Python +{ + class ScriptingElementTypeDescriptor : CustomTypeDescriptor + { + readonly IScriptingElement element; + readonly string defaultDescription; + + public ScriptingElementTypeDescriptor(object instance, string description) + { + if (instance == null) + { + throw new ArgumentNullException(nameof(instance)); + } + + element = instance as IScriptingElement; + defaultDescription = description; + } + + public override AttributeCollection GetAttributes() + { + var description = defaultDescription; + if (element != null && !string.IsNullOrEmpty(element.Description)) + { + description = element.Description; + } + + return new AttributeCollection(new DescriptionAttribute(description)); + } + } +} diff --git a/Bonsai.Scripting/Bonsai.Scripting.csproj b/Bonsai.Scripting/Bonsai.Scripting.csproj deleted file mode 100644 index d9b663af4..000000000 --- a/Bonsai.Scripting/Bonsai.Scripting.csproj +++ /dev/null @@ -1,20 +0,0 @@ - - - - Bonsai - Scripting Library - Bonsai Scripting Library containing scripting infrastructure for Bonsai. - Bonsai Rx Scripting - true - net472 - 3.0.0 - - - - - - - - - - - \ No newline at end of file diff --git a/Bonsai.StarterPack/Bonsai.StarterPack.csproj b/Bonsai.StarterPack/Bonsai.StarterPack.csproj index b5ef67a59..37c40295c 100644 --- a/Bonsai.StarterPack/Bonsai.StarterPack.csproj +++ b/Bonsai.StarterPack/Bonsai.StarterPack.csproj @@ -14,7 +14,7 @@ - + diff --git a/Bonsai.sln b/Bonsai.sln index 4f93d427d..af9845709 100644 --- a/Bonsai.sln +++ b/Bonsai.sln @@ -1,4 +1,4 @@ - + Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.29806.167 @@ -19,7 +19,13 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bonsai.System", "Bonsai.Sys EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bonsai.Arduino", "Bonsai.Arduino\Bonsai.Arduino.csproj", "{CFED93E8-2AE9-471D-81D6-C89BB0356EF3}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bonsai.Scripting", "Bonsai.Scripting\Bonsai.Scripting.csproj", "{A341A5A1-45A6-4B35-9AB1-FE42C622F738}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bonsai.Scripting.Expressions", "Bonsai.Scripting.Expressions\Bonsai.Scripting.Expressions.csproj", "{11F0B82C-7400-4BBC-B3CB-8B714AB4F9A1}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bonsai.Scripting.Expressions.Design", "Bonsai.Scripting.Expressions.Design\Bonsai.Scripting.Expressions.Design.csproj", "{2144779A-344C-4D90-B26D-A70761490A11}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bonsai.Scripting.Python", "Bonsai.Scripting.Python\Bonsai.Scripting.Python.csproj", "{D17B5E47-8D04-4BC6-AFFA-8F7ABE962676}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bonsai.Scripting.Python.Design", "Bonsai.Scripting.Python.Design\Bonsai.Scripting.Python.Design.csproj", "{C21CD090-BEA1-48E6-828A-0D3CF6EB427E}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bonsai.Player", "Bonsai.Player\Bonsai.Player.csproj", "{156452CC-3847-4706-AC65-4FA3BECA88EF}" EndProject @@ -154,16 +160,6 @@ Global {CFED93E8-2AE9-471D-81D6-C89BB0356EF3}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {CFED93E8-2AE9-471D-81D6-C89BB0356EF3}.Release|Mixed Platforms.Build.0 = Release|Any CPU {CFED93E8-2AE9-471D-81D6-C89BB0356EF3}.Release|x64.ActiveCfg = Release|Any CPU - {A341A5A1-45A6-4B35-9AB1-FE42C622F738}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A341A5A1-45A6-4B35-9AB1-FE42C622F738}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A341A5A1-45A6-4B35-9AB1-FE42C622F738}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {A341A5A1-45A6-4B35-9AB1-FE42C622F738}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {A341A5A1-45A6-4B35-9AB1-FE42C622F738}.Debug|x64.ActiveCfg = Debug|Any CPU - {A341A5A1-45A6-4B35-9AB1-FE42C622F738}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A341A5A1-45A6-4B35-9AB1-FE42C622F738}.Release|Any CPU.Build.0 = Release|Any CPU - {A341A5A1-45A6-4B35-9AB1-FE42C622F738}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {A341A5A1-45A6-4B35-9AB1-FE42C622F738}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {A341A5A1-45A6-4B35-9AB1-FE42C622F738}.Release|x64.ActiveCfg = Release|Any CPU {156452CC-3847-4706-AC65-4FA3BECA88EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {156452CC-3847-4706-AC65-4FA3BECA88EF}.Debug|Any CPU.Build.0 = Debug|Any CPU {156452CC-3847-4706-AC65-4FA3BECA88EF}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU @@ -326,16 +322,6 @@ Global {851B4D3C-D8BA-433E-B4E8-CBA186F10C0C}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {851B4D3C-D8BA-433E-B4E8-CBA186F10C0C}.Release|Mixed Platforms.Build.0 = Release|Any CPU {851B4D3C-D8BA-433E-B4E8-CBA186F10C0C}.Release|x64.ActiveCfg = Release|Any CPU - {FC54C35D-120F-49DC-9430-63447E550E39}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FC54C35D-120F-49DC-9430-63447E550E39}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FC54C35D-120F-49DC-9430-63447E550E39}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {FC54C35D-120F-49DC-9430-63447E550E39}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {FC54C35D-120F-49DC-9430-63447E550E39}.Debug|x64.ActiveCfg = Debug|Any CPU - {FC54C35D-120F-49DC-9430-63447E550E39}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FC54C35D-120F-49DC-9430-63447E550E39}.Release|Any CPU.Build.0 = Release|Any CPU - {FC54C35D-120F-49DC-9430-63447E550E39}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {FC54C35D-120F-49DC-9430-63447E550E39}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {FC54C35D-120F-49DC-9430-63447E550E39}.Release|x64.ActiveCfg = Release|Any CPU {EB2EC6F2-C34B-4C7B-A761-E12C853DABF9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EB2EC6F2-C34B-4C7B-A761-E12C853DABF9}.Debug|Any CPU.Build.0 = Debug|Any CPU {EB2EC6F2-C34B-4C7B-A761-E12C853DABF9}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU @@ -348,6 +334,64 @@ Global {EB2EC6F2-C34B-4C7B-A761-E12C853DABF9}.Release|Mixed Platforms.Build.0 = Release|Any CPU {EB2EC6F2-C34B-4C7B-A761-E12C853DABF9}.Release|x64.ActiveCfg = Release|Any CPU {EB2EC6F2-C34B-4C7B-A761-E12C853DABF9}.Release|x64.Build.0 = Release|Any CPU + {FC54C35D-120F-49DC-9430-63447E550E39}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FC54C35D-120F-49DC-9430-63447E550E39}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FC54C35D-120F-49DC-9430-63447E550E39}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {FC54C35D-120F-49DC-9430-63447E550E39}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {FC54C35D-120F-49DC-9430-63447E550E39}.Debug|x64.ActiveCfg = Debug|Any CPU + {FC54C35D-120F-49DC-9430-63447E550E39}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FC54C35D-120F-49DC-9430-63447E550E39}.Release|Any CPU.Build.0 = Release|Any CPU + {FC54C35D-120F-49DC-9430-63447E550E39}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {FC54C35D-120F-49DC-9430-63447E550E39}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {FC54C35D-120F-49DC-9430-63447E550E39}.Release|x64.ActiveCfg = Release|Any CPU + {11F0B82C-7400-4BBC-B3CB-8B714AB4F9A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {11F0B82C-7400-4BBC-B3CB-8B714AB4F9A1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {11F0B82C-7400-4BBC-B3CB-8B714AB4F9A1}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {11F0B82C-7400-4BBC-B3CB-8B714AB4F9A1}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {11F0B82C-7400-4BBC-B3CB-8B714AB4F9A1}.Debug|x64.ActiveCfg = Debug|Any CPU + {11F0B82C-7400-4BBC-B3CB-8B714AB4F9A1}.Debug|x64.Build.0 = Debug|Any CPU + {11F0B82C-7400-4BBC-B3CB-8B714AB4F9A1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {11F0B82C-7400-4BBC-B3CB-8B714AB4F9A1}.Release|Any CPU.Build.0 = Release|Any CPU + {11F0B82C-7400-4BBC-B3CB-8B714AB4F9A1}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {11F0B82C-7400-4BBC-B3CB-8B714AB4F9A1}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {11F0B82C-7400-4BBC-B3CB-8B714AB4F9A1}.Release|x64.ActiveCfg = Release|Any CPU + {11F0B82C-7400-4BBC-B3CB-8B714AB4F9A1}.Release|x64.Build.0 = Release|Any CPU + {2144779A-344C-4D90-B26D-A70761490A11}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2144779A-344C-4D90-B26D-A70761490A11}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2144779A-344C-4D90-B26D-A70761490A11}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {2144779A-344C-4D90-B26D-A70761490A11}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {2144779A-344C-4D90-B26D-A70761490A11}.Debug|x64.ActiveCfg = Debug|Any CPU + {2144779A-344C-4D90-B26D-A70761490A11}.Debug|x64.Build.0 = Debug|Any CPU + {2144779A-344C-4D90-B26D-A70761490A11}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2144779A-344C-4D90-B26D-A70761490A11}.Release|Any CPU.Build.0 = Release|Any CPU + {2144779A-344C-4D90-B26D-A70761490A11}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {2144779A-344C-4D90-B26D-A70761490A11}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {2144779A-344C-4D90-B26D-A70761490A11}.Release|x64.ActiveCfg = Release|Any CPU + {2144779A-344C-4D90-B26D-A70761490A11}.Release|x64.Build.0 = Release|Any CPU + {D17B5E47-8D04-4BC6-AFFA-8F7ABE962676}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D17B5E47-8D04-4BC6-AFFA-8F7ABE962676}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D17B5E47-8D04-4BC6-AFFA-8F7ABE962676}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {D17B5E47-8D04-4BC6-AFFA-8F7ABE962676}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {D17B5E47-8D04-4BC6-AFFA-8F7ABE962676}.Debug|x64.ActiveCfg = Debug|Any CPU + {D17B5E47-8D04-4BC6-AFFA-8F7ABE962676}.Debug|x64.Build.0 = Debug|Any CPU + {D17B5E47-8D04-4BC6-AFFA-8F7ABE962676}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D17B5E47-8D04-4BC6-AFFA-8F7ABE962676}.Release|Any CPU.Build.0 = Release|Any CPU + {D17B5E47-8D04-4BC6-AFFA-8F7ABE962676}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {D17B5E47-8D04-4BC6-AFFA-8F7ABE962676}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {D17B5E47-8D04-4BC6-AFFA-8F7ABE962676}.Release|x64.ActiveCfg = Release|Any CPU + {D17B5E47-8D04-4BC6-AFFA-8F7ABE962676}.Release|x64.Build.0 = Release|Any CPU + {C21CD090-BEA1-48E6-828A-0D3CF6EB427E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C21CD090-BEA1-48E6-828A-0D3CF6EB427E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C21CD090-BEA1-48E6-828A-0D3CF6EB427E}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {C21CD090-BEA1-48E6-828A-0D3CF6EB427E}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {C21CD090-BEA1-48E6-828A-0D3CF6EB427E}.Debug|x64.ActiveCfg = Debug|Any CPU + {C21CD090-BEA1-48E6-828A-0D3CF6EB427E}.Debug|x64.Build.0 = Debug|Any CPU + {C21CD090-BEA1-48E6-828A-0D3CF6EB427E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C21CD090-BEA1-48E6-828A-0D3CF6EB427E}.Release|Any CPU.Build.0 = Release|Any CPU + {C21CD090-BEA1-48E6-828A-0D3CF6EB427E}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {C21CD090-BEA1-48E6-828A-0D3CF6EB427E}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {C21CD090-BEA1-48E6-828A-0D3CF6EB427E}.Release|x64.ActiveCfg = Release|Any CPU + {C21CD090-BEA1-48E6-828A-0D3CF6EB427E}.Release|x64.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE