〇、寫在前面
你想的沒錯,Python 和 C# 其實都可以單獨實現我們要實現的功能,這裡筆者只是抱著實驗及學習的態度去解決問題。
我是一個 C# 程式設計師,目前在學習 Python,對於 Python 得天獨厚的膠水語言特性自然是必須要領略一番,於是就有了本文。
學會了 Python 呼叫 C# 的話,就能做很多想到和想不到的東西,這很有趣,不是嗎?
一、這個功能是怎麼樣的
我很熟悉用 C# & WinForm 的方式開發介面,現在剛好學習了 Python 的網路程式設計的基礎庫 socket,於是我就想到寫一個程式,思路如下:
程式執行時會開啟一個 WinForm 窗體,窗體上有: 輸入檔案下載地址的位址列 選擇檔案儲存位置的檔案開窗按鈕 當前下載狀態的狀態區域 下載按鈕
輸入下載地址,選擇一個檔案儲存位置
點選下載按鈕下載檔案,狀態區域顯示檔案下載狀態,最好能顯示下載進度
介面放到 WinForm,下載功能放到 Python
二、WinForm 端功能實現
WinForm 分為幾部分功能
介面設計
提供下載地址的公共屬性
提供檔案儲存地址公共屬性
提供用於委託下載事件的委託定義
提供記錄狀態資訊的公共方法
提供更新進度資訊的公共方法
1。 介面設計
首先我們使用 VS 建立一個類庫專案
至於為什麼沒有使用 。NET 5 或者 。net core,是因為:Python 呼叫 C# 動態連結庫
建立專案後新建窗體
本例中設計介面設計如下:
2。 方法定義
///
///
開啟選擇儲存檔案路徑時候由於會報錯
在可以呼叫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();}///
三、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()複製程式碼
四、執行效果
五、存在問題
功能實現了,但是存在一個無法解決的問題,就是當檔案開始下載後 WinForm 的介面會卡住,疑似是沒有用現執行緒開啟主窗體的原因,但是不能解釋為什麼下載開始的時候沒有卡頓,有哪位大白指導一下呢?不勝感激!
作者:程式園丁
連結:https://juejin。cn/post/7075518911852052494
著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。