|
|
#19
iswith昨晚 20:26
*==============================================================
* 文件: test_netpy_examples.prg
* 说明: NetPy 调用示例(多个常见用法)
* 说明点:
* - 假定同目录下有 NetPy.prg
* - 请确认 lcPyDllPath 指向你本机的 pythonNN.dll 并且位数与编译的 NetPy.dll 一致 及32位,python38用于开发调器,建议一样。
* - 发行Python38 所需文件请成为vip再咨询。文件整体压缩到不大只有几MB,但cPython库挺大的,最好建全商业应用的自动下载库发送指令给你的客户机;
让其自动下载所需应用的库,升级应用调用就可以完成任务。
* - 示例包含:ExecPy + _vfp.SetVar、oNetPy.CallFunc、从 Python 传数组回 VFP、numpy 处理、错误处理、清理
*==============================================================
* - 设置工作路径
Set Talk Off
Set Date YMD
Clear
On Shutdown Quit()
* - 工作目录
Local lcPath
If Type( "_vfp.ActiveProject" ) == "O"
m.lcPath = JustPath( _vfp.ActiveProject.Name )
Else
m.lcPath = JustPath( Sys(16) )
Endif
Set Default To ( m.lcPath )
If !File( "NetPy.prg" )
?"没找到 NetPy.prg,请先放到当前工作目录或指定路径"
Return
Endif
Set Procedure To NetPy.prg
* - 初始化Python环境
Public oNetPy
If Vartype(oNetPy) <> "O" Or IsNull(oNetPy)
oNetPy = CreateObject("oNetPy")
Local lcPyDllPath
* 修改为本机 Python dll 路径
lcPyDllPath = [C:\Users\Administrator\AppData\Local\Programs\Python\Python38-32\python38.dll]
If !oNetPy.InitPy(lcPyDllPath)
? "InitPy 失败:", oNetPy.cLastError
Release oNetPy
Return
Endif
Endif
? "NetPy 版本:", oNetPy.GetVer()
? Replicate("=", 30)
* 注入 _vfp(供 Python 脚本调用)
Local lcErrorMsg
lcErrorMsg= ""
If !oNetPy.AddHostObject("_vfp", _vfp, @lcErrorMsg)
? "AddHostObject 失败:", lcErrorMsg
Return
Endif
* ---------------------------
* 示例 1: 简单 ExecPy -> Python 里计算并通过 _vfp.SetVar 返回结果给 VFP
* ---------------------------
Local lcScript ;
, lcErrorMsg
m.lcErrorMsg= ""
Public result
Text To m.lcScript Noshow Pretext 2
import math
result = math.sqrt(25)
# 通过注入的 _vfp.SetVar 把结果设置回 VFP 变量 result
_vfp.SetVar( "result" , result )
# 在 Python 端也通过 DoCmd 打印结果(可在 VFP 输出看到)
_vfp.DoCmd('?"示例1:PY sqrt(25) -> " + str(result)')
Endtext
If !oNetPy.ExecPy( m.lcScript , @lcErrorMsg)
? "示例1:ExecPy 失败:", lcErrorMsg
_cliptext = lcErrorMsg
Else
?"示例1:[vfp] sqrt(25) -> " , result
Endif
? Replicate("=", 30)
* ---------------------------
* 示例 2: 直接在 VFP 调用 Python 标准库函数(CallFunc)
* 使用 oNetPy.CallFunc(module, func, taParams, @tcErrorMsg)
* ---------------------------
Local laParams[2] ;
, loRes
laParams[1] = 2
laParams[2] = 8
m.lcErrorMsg = ""
loRes = oNetPy.CallFunc( "math", "pow", @laParams, @lcErrorMsg )
If Empty( m.lcErrorMsg ) Then
? "示例2: math.pow(2,8) ->", loRes
Else
? "示例2 调用失败:", m.lcErrorMsg
_cliptext = m.lcErrorMsg
Endif
? Replicate("=", 30)
* ---------------------------
* 示例 3: 在 Python 中定义函数并让 Python 直接通过 _vfp.SetVar 返回结果,
* 然后在 VFP 使用 GetVariable 读取(适合复杂函数和闭包)
* ---------------------------
Public res_add
Text To m.lcScript Noshow
def my_add(a, b):
return a + b
res = my_add(7, 13)
_vfp.SetVar( "res_add" , res )
_vfp.DoCmd( f'?"[示例3:PY] my_add(7,13) -> " + str( {res} )' )
Endtext
lcErrorMsg = ""
If oNetPy.ExecPy( m.lcScript, @lcErrorMsg ) Then
? "示例3: res_add =", _vfp.Eval("res_add")
Else
? "示例3 ExecPy 失败:", lcErrorMsg
_cliptext = lcErrorMsg
Endif
? Replicate("=", 30)
* ---------------------------
* 示例 4:Python 生成图表并返回图片路径
* ---------------------------
* Python 生成图表并返回图片路径
Text To lcScript Noshow Pretext 2
import matplotlib.pyplot as plt
import os
# 创建数据
x = [1, 2, 3, 4]
y = [10, 15, 13, 17]
# 生成图表
plt.plot(x, y)
plt.title('VFP-Python 交互图表')
img_path = os.path.join(_vfp.GetVar("sys(5)"), 'chart.png')
plt.savefig(img_path)
plt.close()
# 返回图片路径
_vfp.SetVar( "cImagePath" , img_path )
Endtext
If oNetPy.ExecPy( m.lcScript ) Then
If File(cImagePath)
_Screen.AddObject("imgChart", "Image")
_Screen.imgChart.Picture = cImagePath
_Screen.imgChart.Visible = .T.
Endif
Endif
Return
* ---------------------------
* 示例 4: Python 把 list 传回 VFP(SetArray),并在 VFP 端读取数组内容
* ---------------------------
Text To m.lcScript Noshow
py_list = [1, 2, 3, 'he"llo', 5.5, True, None]
try:
_vfp.SetArray( "py_list" , py_list )
except Exception as e:
_vfp.DoCmd( f'?[PY] SetArray failed: str({e}) ')
Endtext
lcErrorMsg = ""
If !oNetPy.ExecPy(m.lcScript, @lcErrorMsg) Then
? "示例4 ExecPy 失败:", lcErrorMsg
_cliptext = lcErrorMsg
Else
* 检查 VFP 端是否存在 py_list,并打印元素
If Type("py_list") = "U"
? "示例4: py_list 未在 VFP 创建"
Else
? "示例4: py_list 类型:", Vartype(py_list)
If Vartype(py_list) = "A"
? "示例4: py_list 元素数:", Alen(py_list)
For lnI = 1 To Alen(py_list)
? "py_list[", lnI, "] =", py_list[lnI]
Endfor
Else
? "示例4: py_list 值:", py_list
Endif
Endif
Endif
? Replicate("=", 30)
* ---------------------------
* 示例 5: 使用 numpy 并把 ndarray 转为 list 再传给 VFP(更稳定)
* ---------------------------
Text To m.lcScript Noshow
try:
import numpy as np
py_nd = np.array([[10, 20, 30], [40, 50, 60]])
# 将 ndarray 转成嵌套 list,再用 SetArray 逐行/逐元素写回
_vfp.SetArray("py_nd", py_nd.tolist())
_vfp.DoCmd('?"[PY] SetArray(py_nd.tolist()) called"')
except Exception as e:
_vfp.DoCmd('?"[PY] numpy -> fallback list used or failed: ' + str(e) + '"')
py_nd = [[10,20,30],[40,50,60]]
_vfp.SetArray("py_nd", py_nd)
Endtext
lcErrorMsg = ""
If !oNetPy.ExecPy(m.lcScript, @lcErrorMsg)
? "示例5 ExecPy 失败:", lcErrorMsg
Else
If Type("py_nd") = "U"
? "示例5: py_nd 未在 VFP 创建"
Else
? "示例5: py_nd 类型:", Vartype(py_nd)
If Vartype(py_nd) = "A"
? "示例5: py_nd 尺寸:", Alen(py_nd,1), "x", Alen(py_nd,2)
For lnI = 1 To Alen(py_nd,1)
For lnJ = 1 To Alen(py_nd,2)
? "py_nd[", lnI, ",", lnJ, "] =", py_nd[lnI, lnJ]
Endfor
Endfor
Else
? "示例5: py_nd 值:", py_nd
Endif
Endif
Endif
* ---------------------------
* 示例 6: Error handling 演示(捕获 ExecPy/CallFunc 错误并读取 oNetPy.cLastError)
* ---------------------------
* 故意调用不存在的模块/函数
Local laP[1], lcErrorMsg2, loBad
laP[1] = 10
m.lcErrorMsg = ""
loBad = oNetPy.CallFunc("non_existing_module", "foo", @laP, @lcErrorMsg )
If Empty(lcErrorMsg)
? "示例6: unexpected success ->", loBad
Else
? "示例6: 调用失败,错误信息:", lcErrorMsg
? "(保存的最后错误)", oNetPy.cLastError
Endif
* ---------------------------
* 清理:关闭 Python(根据需要)
* ---------------------------
* 如果你希望在脚本末尾释放 Python 环境:
* oNetPy.Shutdown()
* Release oNetPy
? "示例运行结束"
Return
|