Go 有一個(gè)開(kāi)源的 Excelize 很強(qiáng)大,可用于操作 Excel 表格。
看著 Excelize 范例里絲滑的 Go 代碼,我就想著怎么能弄到 aardio 里用。先嘗試把 Go 對(duì)象封包到 IDispatch 接口,然后又用 Go 反射整了幾個(gè)小時(shí),最后都放棄了。
后面繼續(xù)調(diào)整思路,改為調(diào)用在運(yùn)行時(shí)用 Go 語(yǔ)法寫腳本的 Yaegi (不需要安裝 Go 編譯環(huán)境,體積小速度快,內(nèi)存加載沒(méi)有外部依賴 ),一頓封裝實(shí)現(xiàn)了在 aardio 里運(yùn)行時(shí)執(zhí)行 Go 代碼、讀寫 Go 變量、調(diào)用 Go 函數(shù)。
一、 aardio 運(yùn)行 Yaegi(Go 腳本)
aardio 代碼示例:
//創(chuàng)建 Yaegi(Go 腳本)解釋器
import golang.yaegi;
var go = golang.yaegi();
//直接用 Go 語(yǔ)法寫腳本。
go.eval(`
package main
//導(dǎo)入內(nèi)建模塊
import 'fmt'
//編寫函數(shù)
func Hello(name string) string {
return name;
}
//全局變量
var GlobaVal = '這是全局變量';
`)
//在 aardio 中直接調(diào)用 Go 函數(shù)
var ret = go.Hello( 'Hello' );
//在 aardio 中修改 Go 全局常量的值
go.GlobaVal = '新的 Go 全局常量值';
import console.int;
//獲取 Go 全局常量的值
console.log( go.GlobaVal );
二、調(diào)用 Excelize 操作 Excel 表格
然后調(diào)用 Excelize 就變簡(jiǎn)單了。
基于上面的原理,我又寫了一個(gè) golang.excelize 擴(kuò)展庫(kù)。然后只要直接復(fù)制粘貼 Excelize 官方的 Go 代碼范例就可以在 aardio 里用了。
直接看 aardio 代碼:
import console.int;
import golang.excelize;
var go = golang.excelize();
//直接用 Go 語(yǔ)法寫腳本。
go.eval(`
package main
import (
'fmt'
//golang.excelize 已經(jīng)提前導(dǎo)入,不用再下載安裝外部模塊
'github.com/xuri/excelize/v2'
)
func CreateExcel(path string) string {
//下面是 Excelize 官網(wǎng)范例
f := excelize.NewFile();
defer func() {
if err := f.Close(); err != nil {
fmt.Println(err)
}
}()
// 創(chuàng)建新表格.
index, err := f.NewSheet('Sheet2')
if err != nil {
fmt.Println(err)
return ''
}
// 設(shè)置單元格的值.
f.SetCellValue('Sheet2', 'A2', 'Hello world.')
f.SetCellValue('Sheet1', 'B2', 100)
// 設(shè)置工作簿的當(dāng)前激活表格
f.SetActiveSheet(index)
// 保存表格到指定的路徑
if err := f.SaveAs(path); err != nil {
fmt.Println(err)
}
//讀單元格的值
cell, err := f.GetCellValue('Sheet1', 'B2')
if err != nil {
fmt.Println(err)
return ''
}
return cell;
}`)
//在 aardio 中直接調(diào)用 Go 函數(shù)
var ret = go.CreateExcel( io.fullpath('/hello.xlsx') );
//查看 Go 函數(shù)的返回值
console.log( ret );
三、Yaegi 導(dǎo)入第三方模塊
這里說(shuō)一下 Yaegi 導(dǎo)入第三方模塊的步驟。
以 aardio 中的 golang.excelize 擴(kuò)展庫(kù)為例。打開(kāi)生成 DLL 的源文件:
'~\lib\golang\excelize\.dll\編譯.aardio'
然后在 Go 源碼里可以看到這個(gè)全局常量:
var Symbols = map[string]map[string]reflect.Value{}
這個(gè) Symbols 就是符號(hào)表,第三方模塊都放到這里面。
例如我們要添加 aardio 模塊?,在創(chuàng)建解釋器以后就這么寫:
//創(chuàng)建 Yaegi 解釋器
yaegiInterpreter = interp.New(interp.Options{})
yaegiInterpreter.Use(stdlib.Symbols)
//添加第三方模塊
Symbols['aardio/aardio'] = map[string]reflect.Value{
'Call': reflect.ValueOf(aardio.Call),
'CallPtr': reflect.ValueOf(aardio.CallPtr),
'CallJson': reflect.ValueOf(aardio.CallJson),
'JsonParam': reflect.ValueOf(aardio.JsonParam),
}
//注冊(cè)到解釋器
yaegiInterpreter.Use(Symbols);
Symbols 可以理解為 aardio 里的表對(duì)象,每個(gè)鍵值對(duì)注冊(cè)一個(gè)?第三方模塊。比較坑的地方是這個(gè)鍵名 'aardio/aardio' 要寫兩次。
如果是非常大的模塊,手動(dòng)寫?就不現(xiàn)實(shí)了。這時(shí)候可以用 Yaegi 提供的工具?自動(dòng)生成符號(hào)表。為了避免被坑,可以直接運(yùn)行我提供的 aardio 代碼生成,以 Excelize 為例:
//提取符號(hào)表
import console.int;
import golang;
var go = golang('/','1.22.3')
go.mod('init excelize')
go.get('github.com/xuri/excelize/v2')
go.yaegiExtract('github.com/xuri/excelize/v2')
沒(méi)有任何顯示是正常的,耐心等到完成?。打開(kāi)自動(dòng)生成的
github_com-xuri-excelize-v2.go
?有了前面的知識(shí),看生成的代碼你就知道怎么做了。
四、Yaegi(Go 腳本)回調(diào) aardio 函數(shù)
?直接看代碼:
//aardio 回調(diào)函數(shù)
import golang.yaegi;
var go = golang.yaegi();
go.eval(`
package main
import 'aardio'
func TestCallBack(fnCallback float64) int{
var s = '字符串'
var r,_ = aardio.CallJson( fnCallback ,s,123,map[string]int{'id': 1, 'id2': 2} )
return r
}
`)
import raw.jsonCall
//創(chuàng)建回調(diào)函數(shù)指針, 在 Go 中必須用 aardio.CallJson 調(diào)用。
var callback = raw.jsonCall(
function(a,b,c){
console.log('回調(diào)參數(shù):',a,b)
console.dumpJson(c);
return 123;
} );
import console.int
var ret = go.TestCallBack( callback )
console.log(ret);
回調(diào)可以支持任意個(gè)數(shù) JSON 兼容的參數(shù),傳參與返回值會(huì)自動(dòng)通過(guò) JSON 轉(zhuǎn)換。
要注意在 aardio 中調(diào)用 Yaegi 函數(shù)時(shí),數(shù)值默認(rèn)是 float64 類型( JSON 只能描述一種數(shù)值類型 ),可以在 Go 函數(shù)?里強(qiáng)制轉(zhuǎn)換一下就可以了。
五、Yaegi(Go 腳本)調(diào)用 aardio 窗口函數(shù)
直接看代碼:
import golang.yaegi;
var go = golang.yaegi();
go.eval(`
package main
import 'aardio'
func TestCallBack(hwnd float64){
aardio.Call(hwnd,'onCallback','參數(shù)1','參數(shù)2');
}
`)
import win.ui;
var winform = win.form(text='Yaegi')
winform.add(
edit={cls='edit';left=43;top=27;right=717;bottom=400;multiline=1;z=1}
)
winform.onCallback = function(param1,param2){
winform.edit.print('Go 調(diào)用了 aardio 函數(shù),參數(shù):',param1,param2)
}
go.TestCallBack( winform.hwnd );
winform.show();
win.loopMessage();?