Python說明
# Python說明
TaskRunner目前已支援運行Python指令碼。Python是一種廣泛使用的解釋型、高級和通用的程式語言。最初被設計用於編寫自動化指令碼,隨著版本的不斷更新和語言新功能的新增,越來越多被用於獨立的、大型專案的開發。目前AI相關的專案大部分都以Python作為基礎語言使用,通過Python的引入,TaskRunner也能接入到Python生態中,從而實現更多的功能。接下來將詳細介紹如何在TaskRunner中使用Python相關的功能。
# 1. 安裝配置Python
TaskRunner為64位程式,需要使用者自行安裝Python環境,可以在https://www.python.org/ (opens new window)或者anaconda (opens new window)中獲取可使用的python工具進行安裝。安裝完成後,需要記錄Python環境安裝的目錄,在後續會使用。
# 2. 基本使用方法
# 2.1. 基本示例
一個基本的使用方法如下,使用控制元件的方式引入並執行Python指令碼。
{
var pe, ps;
try{
pe = new TPythonEngine(nil);
//要呼叫的python dll庫檔名稱,在python安裝目錄下可檢視
pe.DllName = "python311.dll";
//要呼叫的python dll庫檔案所在的目錄
pe.DllPath = "C:\\Users\\isoface\\anaconda3";
pe.AutoFinalize = false;
//不要設定為自動載入,在設定完成後,使用下方的LoadDll手動載入
pe.AutoLoad = false;
pe.UseLastKnownVersion = false;
pe.LoadDll;
//第一種用法
//直接使用字串執行python語句
pe.ExecString("print(1+1)","<strings>");
//第二種用法
//呼叫執行TaskRunner目錄下的python指令碼檔案
pe.ExecFile("test.py",nil.nil);
//第三種用法
//呼叫執行多行python語句
ps = new TStringList;
ps.Add("a=1");
ps.Add("b=2");
ps.Add("print(a+b)");
pe.ExecStrings(ps,"<strings>")
ps.Free;
}
finally{
pe.Free;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 2.2. 獲取python的計算結果
假設我們提供了以下python指令碼,儲存檔名稱為test.py
:
from pathlib import Path
# 獲取python 檔案所在的目錄
try:
current_directory = Path(__file__).resolve().parent
except NameError:
# 當 __file__ 不存在時,使用目前工作目錄
current_directory = Path.cwd()
# 建立日誌目錄
log_directory = current_directory / 'temp'
# 檢測目錄是否存在
if not log_directory.exists():
# 如果目錄不存在,則建立目錄
log_directory.mkdir()
# 由Var對映的變數
test.Value = str(log_directory)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
使用以下方式進行呼叫引用,可以獲取python運行過程中的變數結果。
{
var pe, pv;
try{
pe = new TPythonEngine(nil);
pv = new TPythonDelphiVar(nil);
pe.AutoFinalize = false;
//不要設定為自動載入,在設定完成後,使用下方的LoadDll手動載入
pe.AutoLoad = false;
pe.UseLastKnownVersion = false;
pe.DllPath = "C:\\Users\\isoface\\anaconda3";
pe.DllName = "python311.dll";
//pythondelphivar 要繫結pythonengine使用
pv.Engine = pe;
pv.Module = "__main__";
pv.VarName = "test";
pe.LoadDll;
pe.ExecFile("test.py",nil,nil);
UGMM.AddLog("test.Value = "+pv.ValueAsString);
}
finally{
pe.Free;
pv.Free;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 2.3. 從指令碼變數與python變數相互轉換
假設test.py
中的指令碼語句如下:
from pathlib import Path
# 獲取python 檔案所在的目錄
try:
current_directory = Path(__file__).resolve().parent
except NameError:
# 當 __file__ 不存在時,使用目前工作目錄
current_directory = Path.cwd()
# 建立日誌目錄
log_directory = current_directory / 'temp'
test_directory = Path(test.Value)
# 檢測目錄是否存在
if not log_directory.exists():
# 如果目錄不存在,則建立目錄
log_directory.mkdir()
# 檢測目錄是否存在
if not test_directory.exists():
# 如果目錄不存在,則建立目錄
test_directory.mkdir()
# 對映至DelphiVar的變數
test.Value = str(log_directory)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
使用方法如下:
//當python語句中出現引用`test.Value`時,觸發此事件
function pvOnGetData(Sender,Data)
{
Data = "F:\\Program Files (x86)\\IsoFace\\TaskRunner\\File";
}
//當python語句中出現給`test.Value`賦值的語句時,觸發此事件
function pvOnSetData(Sender,Data)
{
UGMM.AddLog("test.Value = "+Data);
}
{
var pe, pv;
try{
pe = new TPythonEngine(nil);
pv = new TPythonDelphiVar(nil);
pe.AutoFinalize = false;
//不要設定為自動載入,在設定完成後,使用下方的LoadDll手動載入
pe.AutoLoad = false;
pe.UseLastKnownVersion = false;
pe.DllPath = "C:\\Users\\deanj\\anaconda3";
pe.DllName = "python311.dll";
//pythondelphivar 要繫結pythonengine使用
pv.Engine = pe;
//設定運行的python模組名稱,主運行模組名稱通常為__main__
pv.Module = "__main__";
//需要注入至python模組中的變數名稱
pv.VarName = "test";
pv.OnGetData = &pvOnGetData;
pv.OnSetData = &pvOnSetData;
pe.LoadDll;
pe.ExecFile("1.py",nil,nil);
}
finally{
pe.Free;
pv.Free;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 2.4. 從預設資料獲取Python指令碼
使用者除了從Python指令碼檔案中獲取要執行的程式碼外,還可以從預設資料中獲取Python指令碼來運行,使用示例如下:
//當python語句中出現引用`test.Value`時,觸發此事件
function pvOnGetData(Sender,Data)
{
Data = "F:\\Program Files (x86)\\IsoFace\\TaskRunner\\File";
}
//當python語句中出現給`test.Value`賦值的語句時,觸發此事件
function pvOnSetData(Sender,Data)
{
UGMM.AddLog("test.Value = "+Data);
}
{
var sl,pv;
sl = new TStringList;
pv = new TPythonDelphiVar(nil);
try{
//從預設資料編號為`dcc_pid_train`的預設資料中獲取Python指令碼內容,其中包含了test的引用
sl.Text = UGMM.GetPython("dcc_pid_train");
//設定運行的python模組名稱,主運行模組名稱通常為__main__
pv.Module = "__main__";
//需要注入至python模組中的變數名稱
pv.VarName = "test";
pv.OnGetData = &pvOnGetData;
pv.OnSetData = &pvOnSetData;
//使用設定中設定的Python引擎運行上述指令碼
UGMM.PyEngineAndGIL_ExecStrings(sl,'',pv);
}
finally{
sl.Free;
pv.Free;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 3. 高級使用方法
Python在運行時,會收到全域性直譯器鎖(GIL)的限制。GIL 是python的全域性直譯器鎖,同一程序中假如有多個執行緒運行,一個執行緒在運行python程式的時候會霸佔python直譯器(加了一把鎖即GIL),使該程序內的其他執行緒無法運行,等該執行緒運行完后其他執行緒才能運行。基本使用方法中提供的方式在運行時會佔用Python直譯器,導致其他程式無法使用Python。故在實際使用中,需要時刻注意Python GIL的情況。
使用高級方法前,請先在TaskRunner設定中設定Python路徑,DLL名稱,勾選自動載入
選項。
TaskRunner提供了以下方法,在運行Python時不會長期佔用Python直譯器。點選下列方法檢視詳細的使用方法:
# 4. 使用執行緒運行Python
如果要運行的Python需要運行較長時間才能返回結果,爲了避免運行Python導致的UI界面堵塞,可以考慮使用執行緒的方式來運行Python。但是,由於GIL的限制,實際Python執行緒的運行仍是有先後順序的,並不是同時進行。
TaskRunner為此建立了一個Python任務佇列,當接收到Python運行請求時,會將運行的請求建立任務,壓入任務佇列中。
當任務佇列中存在任務,TaskRunner會取出最早建立的一個任務,開始使用Python運行。
當Python任務運行完成後,TaskRunner才會繼續取下一個任務運行,直至所有任務都運行完成。這樣就能夠避免併發請求訪問時,Python出現多執行緒運行被GIL阻塞的情況。
使用執行緒方法前,請先在TaskRunner設定中設定Python路徑,DLL名稱,勾選自動載入
選項。
具體的使用方法,請參考Python預設資料。