Python 整合 C# 實現介面操作下載檔案功能

〇、寫在前面

你想的沒錯,Python 和 C# 其實都可以單獨實現我們要實現的功能,這裡筆者只是抱著實驗及學習的態度去解決問題。

我是一個 C# 程式設計師,目前在學習 Python,對於 Python 得天獨厚的膠水語言特性自然是必須要領略一番,於是就有了本文。

學會了 Python 呼叫 C# 的話,就能做很多想到和想不到的東西,這很有趣,不是嗎?

一、這個功能是怎麼樣的

我很熟悉用 C# & WinForm 的方式開發介面,現在剛好學習了 Python 的網路程式設計的基礎庫 socket,於是我就想到寫一個程式,思路如下:

程式執行時會開啟一個 WinForm 窗體,窗體上有: 輸入檔案下載地址的位址列 選擇檔案儲存位置的檔案開窗按鈕 當前下載狀態的狀態區域 下載按鈕

輸入下載地址,選擇一個檔案儲存位置

點選下載按鈕下載檔案,狀態區域顯示檔案下載狀態,最好能顯示下載進度

介面放到 WinForm,下載功能放到 Python

二、WinForm 端功能實現

WinForm 分為幾部分功能

介面設計

提供下載地址的公共屬性

提供檔案儲存地址公共屬性

提供用於委託下載事件的委託定義

提供記錄狀態資訊的公共方法

提供更新進度資訊的公共方法

1。 介面設計

首先我們使用 VS 建立一個類庫專案

Python 整合 C# 實現介面操作下載檔案功能

至於為什麼沒有使用 。NET 5 或者 。net core,是因為:Python 呼叫 C# 動態連結庫

Python 整合 C# 實現介面操作下載檔案功能

建立專案後新建窗體

Python 整合 C# 實現介面操作下載檔案功能

本例中設計介面設計如下:

Python 整合 C# 實現介面操作下載檔案功能

2。 方法定義

///

/// 當前地址/// public string ThisUrl{ get { return textUrl。Text; }}/// /// 當前儲存路徑/// public string ThisSavePath{ get { return textSavePath。Text; }}複製程式碼

///

/// 下載事件委託/// public event EventHandler DownloadEvent;/// /// 下載按鈕事件/// /// /// private void buttonDownload_Click(object sender, EventArgs e){ if (string。IsNullOrEmpty(this。textUrl。Text)) { MessageBox。Show(“請先輸入要下載的檔案地址!”); this。textUrl。Focus(); return; } if (string。IsNullOrEmpty(this。textSavePath。Text)) { MessageBox。Show(“請先選擇檔案要儲存的地址!”); this。textSavePath。Focus(); return; } // 呼叫委託事件 if(this。DownloadEvent != null) { this。DownloadEvent。Invoke(this, e); }}複製程式碼

開啟選擇儲存檔案路徑時候由於會報錯

在可以呼叫OLE之前,必須將當前執行緒設定為單執行緒單元(STA)模式,請確保您的Main函式帶有STAThreadAttribute標記

很無奈,因為我們的呼叫方並不是 C# 的 Main 函式,而我目前並不知道 Python 呼叫 C# 如何實現的,所以只能另外想方法,就是把選擇儲存檔案路徑的開窗單獨啟一個執行緒開發,在子執行緒上再標記 STA

/// 選擇按鈕事件/// /// /// private void buttonSelect_Click(object sender, EventArgs e){ if (string。IsNullOrEmpty(this。textUrl。Text)) { MessageBox。Show(“請先輸入要下載的檔案地址!”); this。textUrl。Focus(); return; } var index = this。textUrl。Text。LastIndexOf(“/”); var fileName = this。textUrl。Text。Substring(index + 1); Thread importThread = new Thread(() => { var text = OpenDialog(fileName); MessageEvent(text); }); importThread。SetApartmentState(ApartmentState。STA); //重點 importThread。Start();}///

/// 開啟對話方塊/// private string OpenDialog(string fileName){ var saveFileDialog = new SaveFileDialog(); saveFileDialog。Filter = “所有檔案 (*。*)|*。*”; saveFileDialog。FilterIndex = 0; if (!string。IsNullOrEmpty(fileName)) { saveFileDialog。FileName = Path。Combine(saveFileDialog。FileName, fileName); } DialogResult dialogResult = saveFileDialog。ShowDialog(); if (dialogResult == DialogResult。OK) { return saveFileDialog。FileName; } return String。Empty;}複製程式碼

三、Python 端功能實現

Python 中分幾部分功能

程式呼叫 。NET 類庫開啟窗體

程式中存在下載指定 URL 檔案儲存到指定路徑的函式定義

程式結束的函式定義

把當前程式封裝成可執行程式(如:Windows 中為封裝成 exe)

import socketimport timeimport remainapp = None# 呼叫動態連結庫的更新狀態資訊def LogInfo(text): # print(text) mainapp。LogInfo(text) # 呼叫動態連結庫的更新下載進度def downloadInfo(c, all): mainapp。SetProcess(c, all) if c == all: # LogInfo(“下載進度 {:。2f}”。format(c / all * 100)) LogInfo(“下載完成。”) # else: # LogInfo(“下載進度 {:。2f}%”。format(c / all * 100))# 監聽下載委託事件def Download(source, args): thisurl = source。ThisUrl。lower() thispath = source。ThisSavePath LogInfo(“下載地址是: {}”。format(thisurl)) LogInfo(“儲存路徑為: {}”。format(thispath)) reobj = re。compile(r“”“(?xi)\A [a-z][a-z0-9+\-。]*:// # Scheme ([a-z0-9\-。_~%!$&‘()*+,;=]+@)? # User ([a-z0-9\-。_~%]+ # Named or IPv4 host |\[[a-z0-9\-。_~%!$&’()*+,;=:]+\]) # IPv6+ host ”“”) match = reobj。search(thisurl) if match: HOST = match。group(2) PORT = 443 if thisurl。startswith(‘https’) else 80 mysock = socket。socket(socket。AF_INET, socket。SOCK_STREAM) mysock。connect((HOST, PORT)) urlend = ‘GET {} HTTP/1。0\r\n\r\n’。format(thisurl)。encode() # LogInfo(“傳遞引數: {}”。format(urlend)) LogInfo(“開始下載……”) mysock。sendall(urlend) count = 0 picture = b“” hearlength = 0 filelength = 0 hearc = b“” while True: data = mysock。recv(5120) if (len(data) < 1): break time。sleep(0。1) count = count + len(data) picture = picture + data # print(len(data), count) if hearlength == 0: hearlength = picture。find(b“\r\n\r\n”) if hearlength > 0: hearc = picture[:hearlength]。decode() # print(hearc) sear = re。search(‘Content-Length: ([0-9]+)’, hearc) if sear: filelength = int(sear。groups()[0]) downloadInfo(count - 4 - hearlength, filelength) else: downloadInfo(count - 4 - hearlength, filelength) mysock。close() # Skip past the header and save the picture data picture = picture[hearlength+4:] fhand = open(thispath, “wb”) fhand。write(picture) fhand。close() # Code: http://www。py4e。com/code3/urljpeg。py # Or select Download from this trinket‘s left-hand menu else: LogInfo(’下載失敗,地址格式存在問題!‘)# 使用 pythonnet 的方式引入動態連結庫import clr# 此處保證動態連結庫檔案放在當前資料夾中,如果不在應該使用這種方式# clr。AddReference(’D:\\Path\\DotNetWithPython‘)# clr。AddReference(’D:\\Path\\DotNetWithPython。dll‘)clr。AddReference(’DotNetWithPython‘)from DotNetWithPython import *mainapp = MainForm()mainapp。DownloadEvent += Downloadmainapp。ShowDialog()複製程式碼

四、執行效果

Python 整合 C# 實現介面操作下載檔案功能

Python 整合 C# 實現介面操作下載檔案功能

五、存在問題

功能實現了,但是存在一個無法解決的問題,就是當檔案開始下載後 WinForm 的介面會卡住,疑似是沒有用現執行緒開啟主窗體的原因,但是不能解釋為什麼下載開始的時候沒有卡頓,有哪位大白指導一下呢?不勝感激!

Python 整合 C# 實現介面操作下載檔案功能

作者:程式園丁

連結:https://juejin。cn/post/7075518911852052494

著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。