Python集成C#實現(xiàn)界面操作下載文件功能的全過程
〇、寫在前面
你想的沒錯,Python 和 C# 其實都可以單獨實現(xiàn)我們要實現(xiàn)的功能,這里筆者只是抱著實驗及學習的態(tài)度去解決問題。
我是一個 C# 程序員,目前在學習 Python,對于 Python 得天獨厚的膠水語言特性自然是必須要領略一番,于是就有了本文。
學會了 Python 調用 C# 的話,就能做很多想到和想不到的東西,這很有趣,不是嗎?
一、這個功能是怎么樣的
我很熟悉用 C# & WinForm 的方式開發(fā)界面,現(xiàn)在剛好學習了 Python 的網(wǎng)絡編程的基礎庫 socket,于是我就想到寫一個程序,思路如下:
- 程序運行時會打開一個 WinForm 窗體,窗體上有:
- 輸入文件下載地址的地址欄
- 選擇文件保存位置的文件開窗按鈕
- 當前下載狀態(tài)的狀態(tài)區(qū)域
- 下載按鈕
- 輸入下載地址,選擇一個文件保存位置
- 點擊下載按鈕下載文件,狀態(tài)區(qū)域顯示文件下載狀態(tài),最好能顯示下載進度
- 界面放到 WinForm,下載功能放到 Python
二、WinForm 端功能實現(xiàn)
WinForm 分為幾部分功能
- 界面設計
- 提供下載地址的公共屬性
- 提供文件存儲地址公共屬性
- 提供用于委托下載事件的委托定義
- 提供記錄狀態(tài)信息的公共方法
- 提供更新進度信息的公共方法
1. 界面設計
首先我們使用 VS 創(chuàng)建一個類庫項目

至于為什么沒有使用 .NET 5 或者 .net core,是因為:Python 調用 C# 動態(tài)鏈接庫

創(chuàng)建項目后新建窗體

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

2. 方法定義
/// <summary>
/// 當前地址
/// </summary>
public string ThisUrl
{
get
{
return textUrl.Text;
}
}
/// <summary>
/// 當前保存路徑
/// </summary>
public string ThisSavePath
{
get
{
return textSavePath.Text;
}
}/// <summary>
/// 下載事件委托
/// </summary>
public event EventHandler DownloadEvent;
/// <summary>
/// 下載按鈕事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
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函數(shù)帶有STAThreadAttribute標記
很無奈,因為我們的調用方并不是 C# 的 Main 函數(shù),而我目前并不知道 Python 調用 C# 如何實現(xiàn)的,所以只能另外想方法,就是把選擇保存文件路徑的開窗單獨啟一個線程開發(fā),在子線程上再標記 STA
/// 選擇按鈕事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
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();
}
/// <summary>
/// 打開對話框
/// </summary>
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 端功能實現(xiàn)
Python 中分幾部分功能
- 程序調用 .NET 類庫打開窗體
- 程序中存在下載指定 URL 文件存儲到指定路徑的函數(shù)定義
- 程序結束的函數(shù)定義
- 把當前程序封裝成可運行程序(如:Windows 中為封裝成 exe)
import socket
import time
import re
mainapp = None
# 調用動態(tài)鏈接庫的更新狀態(tài)信息
def LogInfo(text):
# print(text)
mainapp.LogInfo(text)
# 調用動態(tài)鏈接庫的更新下載進度
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))
# 監(jiān)聽下載委托事件
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("傳遞參數(shù): {}".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 的方式引入動態(tài)鏈接庫
import clr
# 此處保證動態(tài)鏈接庫文件放在當前文件夾中,如果不在應該使用這種方式
# clr.AddReference('D:\\Path\\DotNetWithPython')
# clr.AddReference('D:\\Path\\DotNetWithPython.dll')
clr.AddReference('DotNetWithPython')
from DotNetWithPython import *
mainapp = MainForm()
mainapp.DownloadEvent += Download
mainapp.ShowDialog()四、運行效果


五、存在問題
功能實現(xiàn)了,但是存在一個無法解決的問題,就是當文件開始下載后 WinForm 的界面會卡住,疑似是沒有用現(xiàn)線程打開主窗體的原因,但是不能解釋為什么下載開始的時候沒有卡頓,有哪位大白指導一下呢?不勝感激!

總結
到此這篇關于Python集成C#實現(xiàn)界面操作下載文件功能的文章就介紹到這了,更多相關Python界面操作下載文件內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
django執(zhí)行數(shù)據(jù)庫查詢之后實現(xiàn)返回的結果集轉json
這篇文章主要介紹了django執(zhí)行數(shù)據(jù)庫查詢之后實現(xiàn)返回的結果集轉json,希望對大家有所幫助。一起跟隨小編過來看看吧2020-03-03
pycharm訪問mysql數(shù)據(jù)庫的方法步驟
這篇文章主要介紹了pycharm訪問mysql數(shù)據(jù)庫的方法步驟。文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-06-06
使用python將csv數(shù)據(jù)導入mysql數(shù)據(jù)庫
這篇文章主要為大家詳細介紹了如何使用python將csv數(shù)據(jù)導入mysql數(shù)據(jù)庫,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下2024-05-05
Nginx+Uwsgi+Django 項目部署到服務器的思路詳解
這篇文章主要介紹了Nginx+Uwsgi+Django 項目部署到服務器的思路,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-05-05
使用pickle存儲數(shù)據(jù)dump 和 load實例講解
今天小編就為大家分享一篇使用pickle存儲數(shù)據(jù)dump 和 load實例講解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-12-12
Python靜態(tài)類型檢查新工具之pyright 使用指南
這篇文章主要介紹了Python靜態(tài)類型檢查新工具之pyright 使用指南,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下2019-04-04

