IronPython 作为 C# WPF VS 2015 的脚本语言

IronPython as Scripting language to C# WPF VS 2015

我遇到了一些麻烦,希望你能帮忙解决!我一直在尝试在 while 循环中的 sleep(500) 步骤中更新此 grid.Width 参数。但是,当我在我的程序中点击 运行 脚本时,整个 GUI 停止了。我已经尝试 运行 在不同的线程上使用脚本并使用 BackgroundWorker,但在脚本完成之前,它们仍然以两种方式阻止我的应用程序 GUI。请看一下下面的代码好吗?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

using System.IO;
using System.Threading;
using IronPython.Hosting;
using Microsoft.Scripting;
using Microsoft.Scripting.Hosting;

namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    string script;
    ScriptEngine engine;
    ScriptScope scope;
    Thread threadScript;

    public MainWindow()
    {
        InitializeComponent();

        engine = Python.CreateEngine();
        scope = engine.CreateScope();
        string variableName = "isto";
        object gridMier = gridScript;
        scope.SetVariable(variableName, gridMier);
    }

    public void rodarScript()
    {
        this.Dispatcher.Invoke((Action)(() =>
        {
            try
            {
                //PARTE PARA ADICIONAR BIBLIOTECAS BASICAS PARA DESENVOLVIMENTO COM OS SCRIPTS
                script = @"#Reference the WPF assemblies
import clr
clr.AddReferenceByName(""PresentationFramework, Version = 3.0.0.0, Culture =       neutral, PublicKeyToken = 31bf3856ad364e35"")
clr.AddReferenceByName(""PresentationCore, Version=3.0.0.0, Culture=neutral,     PublicKeyToken=31bf3856ad364e35"")
import System.Windows
def getMyObject():
    return isto

objeto = getMyObject()

#Atalhos de referencias para adicionar
Thickness = System.Windows.Thickness
from System.Threading.Thread import Sleep
Debug = System.Diagnostics.Debug";

                script = script + "\n" + textBoxScript.Text;
                var source = engine.CreateScriptSourceFromString(script, SourceCodeKind.Statements);
                //var compiled = source.Compile();
                //var result = compiled.Execute(scope);
                source.Execute(scope);
            }
            catch (Exception qualquerExcecaoEncontrada)
            {
                MessageBox.Show(qualquerExcecaoEncontrada.ToString(), "Scripting Test do Mier", MessageBoxButton.OK);
            }
        }));
    }

    private void buttonScript_Click(object sender, RoutedEventArgs e)
    {
        threadScript = new Thread(rodarScript);
        threadScript.Start();
    }
}
}

IronPython 中的代码示例 (textBoxScript.Text)

for num in range(1,100):
    objeto.Width = objeto.Width + 1
    Sleep(500)

这个简单的代码,运行在线程上运行,阻塞了我的整个 GUI 50 秒。 如有任何帮助,我们将不胜感激!

谢谢, 卢卡斯

创建一个单独的线程,然后将完整的内容放在 Dispatcher.Invoke 中是不合理的。因为您随后又与 ui 线程同步(咆哮时间)。您应该只调用那些需要(UI 访问)的东西。首先将其从 rodarScript 中删除并仅将其用于 script = script + "\n" + textBoxScript.Text;:

    public void rodarScript()
    {
        try
        {
            //PARTE PARA ADICIONAR BIBLIOTECAS BASICAS PARA DESENVOLVIMENTO COM OS SCRIPTS
            script = @"#...";

            this.Dispatcher.Invoke((Action)(() =>
            {
                script = script + "\n" + textBoxScript.Text;
            }));

            var source = engine.CreateScriptSourceFromString(script, SourceCodeKind.Statements);
            //var compiled = source.Compile();
            //var result = compiled.Execute(scope);
            source.Execute(scope);
        }
        catch (Exception qualquerExcecaoEncontrada)
        {
            MessageBox.Show(qualquerExcecaoEncontrada.ToString(), "Scripting Test do Mier", MessageBoxButton.OK);
        }
    }

(已删除 IP-Code)。

然后添加一些简单的方法,它接受 PythonFunction 的实例并将其添加为变量,如下所示:

public void ExecuteInUI(object obj)
{
    this.Dispatcher.BeginInvoke((Action)(() =>
    {
        var op = engine.CreateOperations(scope);
        op.Invoke(obj);
    }));
}

添加为变量:

scope.SetVariable("execute_in_ui", new Action<object>(ExecuteInUI));

然后你必须稍微修改你的 Python 代码,因为你只想在访问 ui:

时使用 BeginInvoke
def inc_width():
    objeto.Width = objeto.Width + 1

for num in range(1,100):
    execute_in_ui(inc_width)
    Sleep(500)

所以我们将 inc_width 的函数信息传递给 c# 并从那里在 ExecuteInUI 中执行它。那么完整的代码将如下所示:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

using System.IO;
using System.Threading;
using IronPython.Hosting;
using Microsoft.Scripting;
using Microsoft.Scripting.Hosting;

namespace AsyncIronPython
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        string script;
        ScriptEngine engine;
        ScriptScope scope;
        Thread threadScript;

        public MainWindow()
        {
            InitializeComponent();

            engine = Python.CreateEngine();
            scope = engine.CreateScope();
            string variableName = "isto";
            object gridMier = gridScript;
            scope.SetVariable(variableName, gridMier);
            scope.SetVariable("execute_in_ui", new Action<object>(ExecuteInUI));
        }

        public void ExecuteInUI(object obj)
        {
            this.Dispatcher.BeginInvoke((Action)(() =>
            {
                var op = engine.CreateOperations(scope);
                op.Invoke(obj);
            }));
        }

        public void rodarScript()
        {
            try
            {
                //PARTE PARA ADICIONAR BIBLIOTECAS BASICAS PARA DESENVOLVIMENTO COM OS SCRIPTS
                script = @"#Reference the WPF assemblies
import clr
clr.AddReferenceByName(""PresentationFramework, Version = 3.0.0.0, Culture =       neutral, PublicKeyToken = 31bf3856ad364e35"")
clr.AddReferenceByName(""PresentationCore, Version=3.0.0.0, Culture=neutral,     PublicKeyToken=31bf3856ad364e35"")
import System.Windows
def getMyObject():
    return isto

objeto = getMyObject()

#Atalhos de referencias para adicionar
Thickness = System.Windows.Thickness
from System.Threading.Thread import Sleep
Debug = System.Diagnostics.Debug";

                this.Dispatcher.Invoke((Action)(() =>
                {
                    script = script + "\n" + textBoxScript.Text;
                }));

                var source = engine.CreateScriptSourceFromString(script, SourceCodeKind.Statements);
                //var compiled = source.Compile();
                //var result = compiled.Execute(scope);
                source.Execute(scope);
            }
            catch (Exception qualquerExcecaoEncontrada)
            {
                MessageBox.Show(qualquerExcecaoEncontrada.ToString(), "Scripting Test do Mier", MessageBoxButton.OK);
            }
        }

        private void buttonScript_Click(object sender, RoutedEventArgs e)
        {
            threadScript = new Thread(rodarScript);
            threadScript.Start();
        }
    }
}

XAML:

<Window x:Class="AsyncIronPython.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:AsyncIronPython"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid x:Name="gridScript">
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="30" />
        </Grid.RowDefinitions>

        <TextBox x:Name="textBoxScript" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Margin="3" AcceptsReturn="True" AcceptsTab="True" />
        <Button x:Name="buttonScript" Click="buttonScript_Click" VerticalAlignment="Center" HorizontalAlignment="Stretch" Content="Execute" Grid.Row="1" Margin="3" />

    </Grid>
</Window>

希望这对您有所帮助。

谢谢@BengEg 和大家,这正是我想要的。我试图在 C# WPF 中使用 IronPython 脚本在文本框、网格、用户控件中创建动画。所以,我很难找到这样做的方法,因为用户控件正在被我程序中的另一个线程使用。所以,这是最后的 "Testing Scripting code".

CSharp

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;

    using System.IO;
    using System.Threading;
    using IronPython.Hosting;
    using Microsoft.Scripting;
    using Microsoft.Scripting.Hosting;

    namespace AsyncIronPython
    {
        /// <summary>
        /// Interaction logic for MainWindow.xaml
        /// </summary>
        public partial class MainWindow : Window
        {
            string script;
            ScriptEngine engine;
            ScriptScope scope;
            Thread threadScript;

            public MainWindow()
            {
                InitializeComponent();

                engine = Python.CreateEngine();
                scope = engine.CreateScope();
                scope.SetVariable("objetoEditavel", gridScript);
                scope.SetVariable("execute_in_ui", new Action<object>(ExecuteInUI));
            }

            public void ExecuteInUI(object obj)
            {
                this.Dispatcher.BeginInvoke((Action)(() =>
                {
                    var op = engine.CreateOperations(scope);
                    op.Invoke(obj);
                }));
            }

            public void rodarScript()
            {
                try
                {
                    //PARTE PARA ADICIONAR BIBLIOTECAS BASICAS PARA DESENVOLVIMENTO COM OS SCRIPTS
                    script = @"#Reference the WPF assemblies
    import clr
    clr.AddReferenceByName(""PresentationFramework, Version = 3.0.0.0, Culture =       neutral, PublicKeyToken = 31bf3856ad364e35"")
    clr.AddReferenceByName(""PresentationCore, Version=3.0.0.0, Culture=neutral,     PublicKeyToken=31bf3856ad364e35"")
    import System.Windows
    def getMyObject():
        return objetoEditavel

    objeto = getMyObject()

    #Atalhos de referencias para adicionar
    Thickness = System.Windows.Thickness
    from System.Threading.Thread import Sleep
    Debug = System.Diagnostics.Debug";

                    this.Dispatcher.Invoke((Action)(() =>{script = script + "\n" + textBoxScript.Text;}));

                    var source = engine.CreateScriptSourceFromString(script, SourceCodeKind.Statements);
                    source.Execute(scope);
                }
                catch (Exception qualquerExcecaoEncontrada)
                {
                    MessageBox.Show(qualquerExcecaoEncontrada.ToString(), "Scripting Test do Mier", MessageBoxButton.OK);
                }
            }

            private void buttonScript_Click(object sender, RoutedEventArgs e)
            {
                threadScript = new Thread(rodarScript);
                threadScript.Start();
            }
        }
    }

XAML

    <Window x:Class="AsyncIronPython.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:AsyncIronPython"
            mc:Ignorable="d"
            Title="MainWindow" Height="350" Width="525">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="*" />
                <RowDefinition Height="30" />
            </Grid.RowDefinitions>

            <TextBox x:Name="textBoxScript" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Margin="261,3,3,3" AcceptsReturn="True" AcceptsTab="True" Background="#FFFFD6D6" />
            <Button x:Name="buttonScript" Click="buttonScript_Click" VerticalAlignment="Center" HorizontalAlignment="Stretch" Content="Execute" Grid.Row="1" Margin="3" />
            <Grid x:Name="gridScript" HorizontalAlignment="Left" Height="50" Margin="10,10,0,0" VerticalAlignment="Top" Width="50" Background="Black"/>

        </Grid>
    </Window>

gridScript 中的 IronPython 脚本(基本上会创建此黑框网格的动画,以 50 毫秒的间隔增长。

    def inc_width():
        objeto.Width = objeto.Width + 1

    for num in range(1,100):
        execute_in_ui(inc_width)
        Sleep(50)