65.9K
CodeProject 正在变化。 阅读更多。
Home

将 IronPython 集成到您的应用程序中

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.05/5 (8投票s)

2008年3月16日

CPOL

6分钟阅读

viewsIcon

55943

downloadIcon

1253

本文介绍了脚本的实用性,并解释了如何将 IronPython 作为脚本语言集成到您的应用程序中。

引言

IronPython 是 .NET 的 Python 实现。它通过 Python 编程语言提供对 .NET 库的完全访问,同时也提供对标准 Python 库的访问。虽然它可以用于编写独立应用程序,但本文将重点介绍如何将其用作应用程序的脚本语言。

为什么要使用脚本语言?

  1. 在运行时更改功能

    这有很多原因 - 也许你的每个客户都希望你的应用程序表现不同,而编写和发布插件太过麻烦。或者你的客户是开发人员,他们希望能够更改应用程序的行为并立即反馈进行自定义,而无需重新编译应用程序或插件。或者,你正在测试某些功能,并且希望应用程序在不同时间表现不同,而无需中断调试器。

  2. 提供“额外”功能

    你正在运行你的应用程序,并注意到它占用了大量内存。你决定使用内存分析器对其进行分析,但在那之前,你想运行一个完整的 GC,以便分析器只捕获活动对象。如果有一种方法可以调用 `GC.Collect`,那不是很棒吗?或者,假设你有一个复杂的 UI,需要大量的导航,你想让开发人员省去每次都要浏览 UI 的麻烦。如果有一种东西可以自动完成所有必要的操作,并帮助开发人员快速进入有趣的部分,那不是很棒吗?

  3. 实时测试

    单元测试非常适合单独测试各个组件,但如果你能编写可以在实时系统上运行的测试,那不是很棒吗?例如,当用户单击按钮 X 并且软件处于状态 Y 时,运行测试 Z?或者进行 UI 测试,例如自动填写文本框和单击按钮?

  4. 深入了解实时系统

    当你被困住而没有调试器,并且想检查系统状态(例如,类成员的值)时,如果你能直接在命令窗口中输入它并立即看到结果,那不是很棒吗?

集成 IronPython

集成 IronPython 非常简单。你需要

  1. 从你的应用程序中添加对 IronPython.dll 的引用
  2. 实例化一个 `PythonEngine` 对象
  3. 将你想通过脚本公开的对象添加到 `pythonEngine.Globals`
  4. 重定向标准输出和错误流,以便你可以显示错误和脚本输出

下面的代码片段展示了如何在附加的示例应用程序中完成这些操作

   public Form1()
   {
      InitializeComponent();
      InitializeIronPython();
   }

   private void InitializeIronPython()
   {
      Options.PrivateBinding = true;
      pythonEngine = new PythonEngine();
            
      MessageBoxStream s = new MessageBoxStream();

      pythonEngine.SetStandardError(s);
      pythonEngine.SetStandardOutput(s);
            
      pythonEngine.AddToPath(AppDomain.CurrentDomain.BaseDirectory);

      pythonEngine.Globals.Add("form", this);
      pythonEngine.Globals.Add("bl", bl);
   }

`MessageBoxStream` 是一个派生自 `System.IO.Stream` 的类,它使用 `MessageBox.Show` 显示写入流的任何内容。`pythonEngine.Globals.Add` 方法将你的对象作为全局变量公开给脚本。第一个参数是 `string`,它对应于脚本中的变量名,第二个参数是与该变量对应的对象。示例应用程序将 `Form` 对象和业务逻辑对象公开给脚本。

示例应用程序底部还有一个“命令提示符”,它实际上是一个 `textbox`,用于执行实时 Python 代码。代码如下所示

   private void commandPrompt_KeyPress(object sender, KeyPressEventArgs e)
   {
      if (e.KeyChar == '\r')
      {
         try
         {
            pythonEngine.ExecuteToConsole(commandPrompt.Text);
            commandPrompt.Clear();
         }
         catch(Exception e)
         {
            MessageBox.Show(e.ToString());
         }
      }
   }

它只是将文本转发到 `pythonEngine` 对象上的 `ExecuteToConsole` 方法。

使用 IronPython

附加的项目演示了如何在上一节中提到的内容。这是一个相当牵强的应用程序 - 它有两个输入 `textbox` 和一个输出 `textbox`。有三个按钮:“Add”、“Subtract”和“Custom”。正如你所期望的,Add 按钮会添加输入 `textbox` 中的数字并将其写入输出,Subtract 会减去它们,而 Custom 允许人们编写自定义操作。让我们回顾一下上一节中提到的几点,看看如何使用 IronPython 完成它们。

运行时更改功能

“Custom”按钮执行应用程序目录中的一个名为 custom.py 的脚本。该脚本获取输入 `textbox` 和 `Form` 实例的引用,它可以使用这些来执行任何自定义操作。IronPython 允许你控制脚本可以引用的对象。

private void customButton_Click(object sender, EventArgs e)
{
   Dictionaryobject /> locals = new Dictionaryobject />()
   {
      {"input1", input1},
      {"input2", input2},
      };

      pythonEngine.ExecuteFile("custom.py", pythonEngine.DefaultModule, locals);
   }

custom.py 脚本会自动获取添加到 `pythonEngine` 对象中的全局对象。除此之外,你还可以传递一个名称/对象对的字典作为该脚本的局部变量。然后,脚本文件可以像这样访问它们:

def append(x, y):
    return ''.join([x,y])

form.ShowResult(int(append(input1.Text, input2.Text)))

此脚本只需连接输入 `textbox` 中的文本,将连接结果转换为数字,然后调用 `Form` 对象的 `ShowResult`。

再举一个例子,输入以下命令:

form.TransformResult = lambda x : '-'.join(str(x))

然后执行 Add 或 Subtract - 你会注意到结果的数字之间有连字符。

如果你查看 `ShowResult` 的代码,你会发现它会在设置 `resultTextBox` 的 `Text` 属性之前调用 `TransformResult` 委托(如果可用)。上面的代码片段创建了一个匿名函数,该函数将传递的数字转换为 `string`,然后在每个字符之间插入一个连字符。然后,该匿名函数被分配给 `Form` 对象的 `TransformResult` 委托,然后在单击 Add 按钮时调用它。

提供“额外”功能

你只需在命令提示符 `textbox` 中输入 `GC.Collect` 并按 Enter,就可以立即让 CLR 执行 GC。

为了更有趣的例子,假设你作为开发人员想了解当前会话中生成的结果。假设这是你不想写进应用程序中的一部分。使用 IronPython,你可以这样做:

 resultTextBox = form.Controls.Find('result', True)[0]
 results = []
 def fn(arg1, arg2) : results.append(resultTextBox.Text) 

 resultTextBox.TextChanged += fn

然后你可以在命令提示符中的任何点输入……

results

……以获取到目前为止计算出的结果。该脚本基本上订阅了 `resultTextBox` 的 `TextChanged` 事件,并将内容附加到一个列表中。

实时测试

使用上一节中显示的 `Controls.Find` 方法,你可以获取对任何 UI 对象的引用,然后对其进行操作。

input1TextBox = form.Controls.Find('input1', True)[0]
input2TextBox = form.Controls.Find('input2', True)[0]
resultTextBox = form.Controls.Find('result', True)[0]

input1TextBox.Text = '1'
input2TextBox.Text = '2'

addButton = form.Controls.Find('addButton', True)[0]
addButton.PerformClick()

print int(resultTextBox.Text) == 3
print int(resultTextBox.Text) == 4

如你所见,这段代码获取了 UI 对象的引用,设置了值,执行了一个操作,然后验证了结果。你也可以选择通过将 UI 对象添加到 `PythonEngine.Globals` 来公开它们,而不是使用 `Control.Find` 来获取它们。

深入了解实时系统

你已经看到你可以访问 UI 对象并对其进行操作。你可以做……

form.Text = 'IronPython'

……例如,更改 `form` 的标题。你也可以访问业务逻辑组件,如果你将它们添加到 PythonEngine。示例应用程序公开了它持有的 `BusinessLogic` 对象,名称为 `bl`,因此你现在也可以访问其状态。它将最后一个操作的类型作为成员,所以你可以做……

bl.LastOperation

……以查看上一次执行的操作。

结论

我希望这篇文章让你对 IronPython 和将脚本集成到你的应用程序中产生了兴趣。IronPython 项目托管在 CodePlex 上,所以那里是你下载和了解更多信息的地方。示例应用程序中的命令提示符非常基础,还有像 IronTextBox 这样的替代方案,它们做得更好。你也可以向你的提示符添加“meta”命令,允许你执行任意 Python 文件。请记住,附加的应用程序是一个**示例**应用程序,旨在演示 IronPython 集成,所以如果代码缺少错误检查,你就知道为什么了。

历史

  • 2008年3月16日 15:39: 初始发布
© . All rights reserved.