aardio 文档

aardio 调用 Python 入门指南

aardio 调用 Pyhon 模块与函数

import console;  
import py3;

//导入 Python 模块
var math = py3.import("math");

//调用 Python 函数,  
var num = math.floor(22.3);

//在控制台输出数值。 
console.log(num);

console.pause();

aardio 执行 Python 代码

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 代码。

aardio 与 Python 数据类型转换规则

使用 pyObject

pyObject 也可以在 aardio 中也可以像普通对象一样使用。 可以调用 pyObject 的成员函数、读写其属性、通过下标读写索引项、并支持各种常用运算符。

可通过 pyObject.parseValue() 函数转换为纯 aardio 值(通过 JSON 自动转换)。

Python 对象( pyObject )在 aardio 中与类型转换有关的函数:

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 调用 Python 函数如何指定命名参数

在 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 的多返回值

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 模块

进程内安装 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 模块的目录

aardio 默认在以下两个目录搜索 Python 模块:

也可以自己添加模块搜索路径,示例:

py3.appendPath("\py\site-packages\");

在 Python 中调用 aardio 对象

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。要注意的是一个进程中应当只启动一个 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() 退出消息循环。

进程外调用任意版本 Python ,兼容 32 位与 64 位 Python 。

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(), )

Markdown 格式