從兩個優秀的世界各取所需,更高效的複用程式碼。想想就醉了,.NET和python融合了。“懶惰”的程式設計師們,還等什麼?
Jesse Smith為您展示如何兩個語言來服務同一個.NET程式。你能集兩家所長:Python和.NET一起工作,提供可重用的程式碼功能而不需要你為了一個環境重寫程式碼庫。
通過使用IronPython 執行時庫,你可以讓Python指令碼執行在你的.NET程式中。本文向你展示如何使用一個.NET程式中的python指令碼獲取並展示使用者反饋。
如果你曾經有在一個.NET程式中執行Python指令碼的需求,整合兩者最好的辦法是使用IronPython。我有過這樣的需求。我曾經效力的一個組織需要擴充套件一個已經存在於一個.NET地圖程式中的工具集。我們的目的是利用另一個工具集中現有的Python程式碼來擴充套件這個已有的工具集,前者還不是後者的一部分。在做了一番研究之後,我決定使用IronPython,事情進展很順利。
本文基於上述專案的經驗,我會還原當時的場景,對你所處的情境也同樣適用。
對於這個我們所期望的解決方案,首先要面對的挑戰是從將要執行在.NET程式中的那個Python指令碼獲取輸出,然後使用一個.NET程式的對話方塊把這個輸出展示給使用者。讓我感到吃驚的是,這麼做比預想的還要簡單,你同樣也能學到如何去做同樣的事情。整個過程相對比較簡單,我們將在下面的章節中進行概述。
在.NET程式中嵌入Python指令碼
在這個例子中,我向你展示如何在一個Windows桌面程式中嵌入Python指令碼。目的是在一個已有的程式中新增一個新的工具按鈕。點選按鈕後,程式會彈出一個對話方塊,顯示一個嵌入在對話方塊程式碼中的Python指令碼的執行結果。
為了給這個例子鋪墊更多的前因後果,我們這個應用程式是一個桌面地圖軟體,可以讓使用者建立他們自己的地圖。這個新的工具允許使用者解析和標準化他們地圖上的地址點。
標準化一個地址,就意味著要確保這個地址有一個街道名,字首或字尾,房屋或建築編號,以及連線該街道的上一條街道和下一條街道。每一個地址段必須遵循US郵局郵編地址標準指導。
假設已有一個Python指令碼已經可以做到這些;它就是例子中執行解析的指令碼。Python指令碼將會通過當前對話方塊在螢幕上輸出或閃現這個正在解析的地址,我們可以通過選擇地址解析工具來觸發對話方塊(我不想說的太細,僅僅解釋到這裡。)
程式碼並不重要,重要的是知道如何嵌入指令碼和如何定向指令碼的輸出到對話方塊,作為程式的一部分顯示在螢幕上。這一過程像下面這樣進行:
- 使用者啟動地圖程式並開啟一個自定義地圖。
- 使用者從螢幕上方的已有的工具集中選擇一個新的地址標準化工具。
- 彈出一個帶有可以啟動地址標準化程式的啟動按鈕的地址標準化對話方塊
- 一個文字框顯示當前正在被解析的地址。這些文字框很快消失,地址在使用者眼前一閃而過,表示過程啟動並且正在解析。
- 出現一個表示整個過程結束的資訊,使用了第4步中同樣的文字框。
確定原始碼和專案
首先要做的事情是確定指令碼應該嵌入到應用程式原始碼的什麼位置。在我們的例子/情境下,這個位置應該是新增到包含工具集專案中的新的對話方塊。這個對話方塊會被一個已經存在的用於處理工具欄點選事件的方法所觸發
當我們確定了需要包含原始碼的專案後,就需要引用IronPython 庫。
如果你使用Visual Studio作為編輯器的話,最簡單的方法是使用Nuget Package Manager來新增IronPython庫到專案中。你可以搜尋”IronPython”,然後執行時庫就可以在包管理工具中選擇了。
嵌入指令碼
接下來做的才是真正嵌入指令碼。你首先要用指令碼引擎(scripting engine)來設定一個Python指令碼引擎例項。在新增指令碼前,你同樣可以設定任何你的指令碼所需的特殊的路徑。
1 2 3 4 5 6 |
; html-script: false ]ScriptEngine pyEngine = Python.CreateEngine(); pyEngine.Runtime.IO.RedirectToConsole(); var paths = pyEngine.GetSearchPaths(); paths.Add(@"C:Python27Lib"); paths.Add(@"C:Python27Libsite-packages"); pyEngine.SetSearchPaths(paths); |
第二行告訴.NET框架Python引擎的執行時庫會將輸出重定向到控制檯。然而,這並不是重定向到我們為這個應用新增的新的工具所需的對話方塊中。(下面的程式碼會做這個工作)
但是,首先我們需要通過一個簡單的字串變數來新增指令碼。你需要改變你的指令碼中的引號來配合字串的引號工作。
一個簡單的方法是把你指令碼中的所有雙引號變成單引號。嵌入指令碼的語法如下:
1 2 3 4 |
; html-script: false ]string thescript = @" (此處為實際指令碼內容) "; |
你可能需要處理一些格式問題,但是縮排必須一致。在指令碼字串解析一個有效的字串後,是時候新增輸出重定向程式碼讓指令碼的輸出顯示在工具的對話方塊視窗中了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
; html-script: false ]Console.SetOut(TextWriter.Synchronized(new TextBoxWriter(statusText))); pyEngine.Execute(thescript); this.AllDone(FINISHED); } catch (Exception ex) { this.AllDone(ex.InnerException.StackTrace); } } public void AllDone(string message) { buttonStart.Enabled = true; this.statusLabel.Text = message; } |
在上面這段程式碼中,我們設定了一個新的TextWriter,它接受一個TextBoxWriter型別的引數,這使得我們可以把指令碼的輸出重新寫到一個文字框中。TextBoxWriter型別的程式碼如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
; html-script: false ]public class TextBoxWriter : TextWriter { private TextBox _textBox; public TextBoxWriter(TextBox textbox) { _textBox = textbox; } public override void Write(char value) { base.Write(value); // When character data is written, append it to the text box. _textBox.AppendText(value.ToString()); } public override System.Text.Encoding Encoding { get { return System.Text.Encoding.UTF8; } } } } |
傳入TextBoxWriter型別的statusText屬性是我們的文字框,它會出現在對話方塊中,顯示指令碼的輸出。我們的Python指令碼中的每一個輸出語句都會被重定向到這個文字框。
結論
在本文中,你學會了如何把Python指令碼整合到一個.NET程式中,並且把Python的指令碼檔案輸出到一個.NET對話方塊。這種無縫銜接,使用者是不會感覺到的,他們並不知道實際上是Python在處理後臺的一些工作。
在很多場合下,整合兩種語言是很有用的。我分享了的這一情景,為我的處境提供了很好的解決方案。你可以按照類似的步驟,用同樣的方法把它應用在很多場合。
我建議你去建立一個自己的簡單範例,甚至使用Python指令碼檔案把Python程式碼直接加入到.NET應用中,你確實可以這樣做。當然你並不需要直接把指令碼嵌入在.NET原始碼中,但對我來說這樣做最方便。
打賞支援我翻譯更多好文章,謝謝!
打賞譯者
打賞支援我翻譯更多好文章,謝謝!
任選一種支付方式