import console;
import py3;
//导入 Python 模块
var math = py3.import("math");
//调用 Python 函数,
var num = math.floor(22.3);
//在控制台输出数值。
console.log(num);
console.pause();
import console.int;
import py3;
// Python 代码,注意 Python 对空格缩进有严格要求。
var pyCode = /**
def getList(a,b):
return a + b
**/
//Python 代码
py3.exec( pyCode )
//从 py3.main 模块调用 Python 代码定义的函数
var ret = py3.main.getList(12,23);
console.log(ret);
py3.exec 的参数可以指定 aardio 工程目录下的 *.py 或 *.aardio 文件路径。 如果指定 *.aardio 文件则可使用 aardio 模块语法 生成 Python 代码。
兼容的纯值类型:
Python 返回的浮点数值、不大于 53 位的整型数值、布尔值、字符串、字节数组会自动转换为对应的纯 aardio 值,同样反过来 aardio 这些类型的值也可以自动转换为对应的 Python 类型。
pyObject 对象:
除了纯值以外的其他 Python 对象在 aardio 中存为 py3.object 对象( 简称 pyObject )。如果是 py2 扩展库就是 py.object ,原理与用法都相同。
pyObject 也可以在 aardio 中也可以像普通对象一样使用。 可以调用 pyObject 的成员函数、读写其属性、通过下标读写索引项、并支持各种常用运算符。
可通过 pyObject.parseValue() 函数转换为纯 aardio 值(通过 JSON 自动转换)。
Python 对象( pyObject )在 aardio 中与类型转换有关的函数:
pyObject.parseValue()
pyObject 转换为 aardio 表对象table.parseValue(pyObject)
pyObject 转换为 aardio 表对象tostring(pyObject)
pyObject 转换为 aardio 字符串tonumber(pyObject)
pyObject 转换为 aardio 数值 pyObject.toString()
pyObject 转换为 aardio 字符串pyObject.toDict()
pyObject 转换为字典,返回 py3.dict 字典对象pyObject.toList()
pyObject 转换为字典,返回 py3.list 列表对象pyObject.toTuple()
pyObject 转换为字典,返回 py3.tuple 元组对象pyObject.type()
pyObject 返回 Pythton 类型名aardio 中的 py3.dict,py3.list,py3.tuple 都继承自 py3.object ,本质都是 pyObject 对象(指 py3.object )。
最常用的函数 pyObject.parseValue,示例:
import console;
import py3;
var pyCode = /**
def getList(a,b):
return a,b,123 # Python 多返回值实际是返回一个 tuple
**/
py3.exec( pyCode )
//从 py3.main 模块调用 Python 代码定义的函数
var pyList = py3.main.getList(12,23);
//pyObject 转换为纯 aardio 值
var list = pyList.parseValue()
console.dump(list);
/*
list 或 tuple 可用下标访问,
注意 Python 对象起始索引为 0,aardio 数组起始索引为 1。
*/
var num = pyList[0];
//可以如下遍历 pyObject 对象。
for( pyItem in pyList.each() ){
console.log(pyItem) //基础类型已转换为纯 aardio 值,其他为 py2.object
}
console.pause();
在 aardio 中函数名前加一个 $
符号启用命名参数支持,示例:
import py3;
//导入 Python 模块
var requests = py3.import("requests");
//创建 Python 对象
var ses = requests.Session();
//在函数名前加 $ 以启用命名参数。
var res = ses.$get(verify=false,"https://www.aardio.com");
当我们将 Python 函数名 ses.get
改为 ses.$get
以后,aardio 就会将调用参数 1 指定的表对象转换为 Python 命名参数。
在 aardio 中函数调用参数有且只有一个使用 {}
构造器构建的 table 参数(并且表不是结构体)时,并且第一个出现的成员是用 =
分隔的键值对(不能用冒号替代),就可以省略外层的 {}
。 使用这个语法特性,可以用一个表参数模拟类似 Python 命名参数的效果。
aardio 与 Python 都支持多返回值,而 Python 的多返回值实际上是返回一个 tuple 对象。
import py3;
var pyCode = /**
def getTuple():
return 12,3
**/
//执行 Python 代码
py3.exec( pyCode )
//调用 Python 函数
var pyObj = py3.main.getTuple();
//取 Python 的多返回值并转换为数组
var arr = pyObj.parseValue()
//输出返回值,aardio 起始下标为 1 。
print(arr[1],arr[2])
//Python 对象也可以通过下标访问其成员,但 Python 起始下标为 0 。
print(pyObj[0],arr[1])
进程内安装 Python 模块:
// 如果程序中用的是 py3.10,这一句也要改为 py3.10.pip,Python 3.7 以下版本不支持 pip 工具
import py3.pip;
//可选指定 pip 镜像服务器
py3.pip.setIndexUrl("aliyun");
//安装指定的模块
py3.pip("install","安装模块名称");
有些模块必须将 py3.pip 改为 py3.pip.process 以通过 Python.exe 在进程外安装模块,示例:
import py3.pip;
py3.pip.setIndexUrl("aliyun");
//安装指定的模块
py3.pip.process("install","安装模块名称");
需要进程外安装的模块如果用 py3.pip("install","安装模块名称")
安装则会导致安装时误将当前 EXE( 发布前则为 aardio.exe )作为 Python.exe 重复启动,并且安装模块会失败。
aardio 默认在以下两个目录搜索 Python 模块:
"\py"
文件路径开始为单个斜杠(或反斜杠)表示应用程序根目录, 应用程序根目录在开发时指工程根目录(工程外单独启动代码则为文件所在目录), 发布后应用程序根目录就是指 EXE 所在的目录。
"~\lib\py3.res\DLLs"
aardio 规定文件路径开始为波浪线表示 EXE 所在的目录(开发时为 aardio.exe 所在目录)。
也可以自己添加模块搜索路径,示例:
py3.appendPath("\py\site-packages\");
aardio 在与 Python 交互时, 默认除了布尔值、浮点数值、小于53位的整数值,布尔值转换为纯 aardio 值以外。 在 aardio 这些对象存为 pyObject,并保留对原始 Python 对象的引用。
而 aardio 对象在自动转换为 Python 对象时, 默认是传值而非传址,Python 中不保留对 aardio 对象的引用。
py3.export 的特别之处在于可以导出 aardio 模块到 Python, 在 Python 中引用与操作原始的 aardio 对象。
我们可以使用 py3.export 将 aardio 对象转换为 Python 模块,示例:
py3.export.aardio = {
exportFunction = function(){
}
}
然后就可以在 Python 代码中执行 import aardio
导入上面的 aardio 模块。
我们也可以调用 py3.export( aardioObject ) 将参数中指定的 aardio 对象导出为 Python 对象,示例:
import winex;
import console.int;
import py3;
var pyCode = /**
def testPy(console,winex):
# 还能支持 aardio 创建的迭代器
for(hwnd,title,threadId,processId) in winex.each():
console.log(title)
**/
//执行 Python 代码
py3.exec( pyCode );
//调用 Python 函数
py3.main.testPy(
py3.export(console),//将 aardio 对象转换为 Python 对象
py3.export(winex)
);
py3.export( aardioObject ) 可用于导入参数 aardioObject 指定的 aardio 对象为 Python 对象,这种「代理对象」在 Python 中将保持对原始 aardio 对象的引用,传址而非传值。
aardioObject 参数可指定 table,cdata,class,function 等 类型的 aardio 对象。
注意 py3.export 只能在 Python 启动线程中使用,这是 Python 本身的限制。 在非 Python 主线程下,py3.export 为 null 值。
使用 py3.mainThread 也可以检测是否 Python 主线程,
参考文档: 多线程入门
Python 写多线程比较麻烦,我们一般会用单线程运行 Python 。但在有图形界面的线程中运行 Python 时,如果 Python 有耗时操作会阻塞界面导致卡顿。
这时候我们可以创建后台线程来运行 Python。要注意的是一个进程中应当只启动一个 Python 线程,避免复杂的 GIL 管理。
import win.ui;
/*DSG{{*/
var winform = win.form(text="aardio - 后台线程运行 Python";right=1163;bottom=753)
winform.add(
button={cls="button";text="调用python线程";left=876;top=663;right=1102;bottom=723;z=2};
edit={cls="edit";left=11;top=16;right=1140;bottom=625;edge=1;multiline=1;z=1}
)
/*}}*/
//Python 后台线程
pyServerThread = function(winform){
import win.ui;
import py3;
//创建 messageOnly 窗体作为命令监听器
var frmMsg = win.form().messageOnly();
//响应事件
frmMsg.pyHash = function(){
//可以直接访问界面控件
winform.edit.print("子线程正在执行 pyHash 函数",tostring(time()))
var hashlib = py3.import("hashlib");
var md5 = hashlib.md5()
md5.update( raw.buffer("注意这个函数的参数不是字符串而是字节数组(相当于 aardio 中的 buffer)") );
sleep(1000)
//调用界面线的函数
winform.pyHashEnd( tostring(md5.hexdigest()) );
return tostring(md5.hexdigest());
}
//退出线程
frmMsg.pyExit = function(){
win.quitMessage();
}
//将 frmMsg 传入界面线程
winform.pyCommand = frmMsg;
//在工作线程需要启用消息循环,才能响应事件
win.loopMessage();
}
//启动 Python 后台线程
thread.create( pyServerThread,winform )
//增加工作线程可以调用的函数
winform.pyHashEnd = function(str){
winform.edit.print("主线程函数被调用,参数:",str)
}
winform.button.oncommand = function(id,event){
//禁用按钮,避免重复提交
winform.button.disabledText = "正在调用python线程";
//异步调用python线程的函数,等待返回值
var str = winform.pyCommand.pyHash()
winform.edit.print("主线程收到返回值:",str)
//取消按钮禁用状态
winform.button.disabledText = null;
}
winform.onClose = function(hwnd,message,wParam,lParam){
//退出 Python 线程
winform.pyCommand.pyExit()
}
winform.show();
win.loopMessage();
在 aardio 中窗口对象可以跨线程直接调用。
我们将界面线程的 winform
对象作为线程参数传入 Python 后台线程,然后又在 Python 后台线程中调用 winform.pyCommand = frmMsg
将后台线程的 frmMsg
回传到界面线程,就可以在界面线程中通过 winform.pyCommand
访问后台线程 frmMsg
对象了。
注意后台线程要调用 win.loopMessage()
以启动窗口消息循环,使用 win.quitMessage()
退出消息循环。
py3 扩展库是进程内直接调用 Python 接口,仅支持 py3 扩展库自带的指定版本 Python,并且这是 32 位 Python。
可运行如下 aardio 代码查看 py3 扩展库自带的 Python 版本信息:
import console.int;
import py3;
console.log( py3.sysObject("version") );
aardio 提供 process.python 库可通过进程外调用任意任意版本 Python ,兼容 32 位与 64 位 Python 。
示例:
import console.int;
import process.python;
//运行 Python 代码
var python = process.python.exec(`
?>import json
str = json.dumps(['foo', "<?=
time() //可使用模板语法嵌入 aardio 代码
?>",{'bar': ('baz', None, 1.0, 2)}])
# print 写到进程标准输出,在 aardio 中可以读取
print( str )
`);
/*
这里的 python 对象就是进程管道( process.popen )对象。
下面的 python.json 读取进程输出的下一行 JSON 并转换为 aardio 对象,忽略非 JSON 输出。
读取其他数据可以改为 readAll() 或 read() 函数。
*/
var info,err = python.json();
console.dump(info || err);
调用 process.python.exec 返回的是一个 process.popen 对象。
process.python 默认会调用绿色嵌入版 Python ,如果没有找到 Python 会自动下载。
可以用 process.python.path 自定义 python.exe 的路径。如果执行 process.python.path = "python.exe"
则会调用系统安装的 Python ( 如果系统没有安装 Python 则会自动安装 )。
process.python.exec 的参数参数可以是*.py, *.aardio 文件路径( 支持内存加载 aardio 工程资源目录的文件 )。如果指定的 Python 代码开始为 aardio 模板标记,则启用 aardio 模板语法。
process.python.exec 可添加一个或多个启动参数,也可以用一个字符串包含多个参数(空格分隔)。示例:
import console.int;
import process.python;
var python = process.python.exec(`
import sys
print( sys.argv )
`,"参数1","参数2");
console.log( python.readAll(), )