4.5
EricZhou
1.简介
zserge/lorca一个非常小的库,用于在Go中构建现代HTML5桌面应用程序. 它使用Chrome浏览器作为UI层. 与Electron不同,它不会将Chrome捆绑到应用程序包中,而是重用已安装的那个. Lorca建立了与浏览器窗口的连接,允许从UI调用Go代码并以无缝方式从Go操作UI.
1.1原理
Lorca使用Chrome DevTools协议来检测Chrome实例. 首先,Lorca尝试找到已安装的Chrome,启动绑定到临时端口的远程调试实例,并从stderr读取实际的WebSocket端点. 然后Lorca打开与WebSocket服务器的新客户端连接,并通过WebSocket发送Chrome DevTools协议方法的JSON消息来监控Chrome. JavaScript函数在Chrome中进行评估,而Go函数实际上在Go运行时运行,返回的值将发送到Chrome.
2.代码实例
这是一个简单的计数器demo
2.1Examples/counter代码文件结构功能说明
./icons./www./assets.gogen.gobuild_linux.shbuild_macos.shbuild_windows.batcounter.gifgen.goassets.gomain.go
main.go
//go:generate go run -tags generate gen.go
package main
import (
"fmt"
"log"
"net"
"net/http"
"os"
"os/signal"
"sync"
"github.com/zserge/lorca"
)
// Go types that are bound to the UI must be thread-safe, because each binding
// is executed in its own goroutine. In this simple case we may use atomic
// operations, but for more complex cases one should use proper synchronization.
type counter struct {
sync.Mutex
count int
}
func (c *counter) Add(n int) {
c.Lock()
defer c.Unlock()
c.count = c.count + n
}
func (c *counter) Value() int {
c.Lock()
defer c.Unlock()
return c.count
}
func main() {
ui, err := lorca.New("", "", 480, 320)
if err != nil {
log.Fatal(err)
}
defer ui.Close()
// A simple way to know when UI is ready (uses body.onload event in JS)
ui.Bind("start", func() {
log.Println("UI is ready")
})
// Create and bind Go object to the UI
c := &counter{}
ui.Bind("counterAdd", c.Add)
ui.Bind("counterValue", c.Value)
// Load HTML.
// You may also use `data:text/html,<base64>` approach to load initial HTML,
// e.g: ui.Load("data:text/html," + url.PathEscape(html))
ln, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
log.Fatal(err)
}
defer ln.Close()
go http.Serve(ln, http.FileServer(FS))
ui.Load(fmt.Sprintf("http://%s", ln.Addr()))
// You may use console.log to debug your JS code, it will be printed via
// log.Println(). Also exceptions are printed in a similar manner.
ui.Eval(`
console.log("Hello, world!");
console.log('Multiple values:', [1, false, {"x":5}]);
`)
// Wait until the interrupt signal arrives or browser window is closed
sigc := make(chan os.Signal)
signal.Notify(sigc, os.Interrupt)
select {
case <-sigc:
case <-ui.Done():
}
log.Println("exiting...")
}
go:generate go run -tags generate gen.gotype counter structsync.MutexAddValuelocar.Newui.Bindln, err := net.Listen("tcp", "127.0.0.1:0")go http.Serve(ln, http.FileServer(FS))ui.Evalsignal.Notify(sigc, os.Interrupt)
3.实例教程
接下来我们就使用vuejs+elementUI来创建一个简单的GUI程序
3.1创建vuejs-spa
Home.vue
ginbro/gui/ginbro-spa/src/views/Home.vue
<template>
<el-form ref="form" :model="form" label-width="160px">
<el-form-item label="address">
<el-input v-model="form.mysqlAddr" placeholder="1270.0.0.1:3306"></el-input>
</el-form-item>
<el-form-item label="user">
<el-input v-model="form.mysqlUser"></el-input>
</el-form-item>
<el-form-item label="password">
<el-input v-model="form.mysqlPassword"></el-input>
</el-form-item>
<el-form-item label="database">
<el-input v-model="form.mysqlDatabase" placeholder="the database to create RESTful app"></el-input>
</el-form-item>
<el-form-item label="database">
<el-radio v-model="form.mysqlCharset" label="utf8">utf8</el-radio>
<el-radio v-model="form.mysqlCharset" label="utf8mb4">utf8mb4</el-radio>
</el-form-item>
<el-form-item label="login table">
<el-input v-model="form.authTable" placeholder="the table for login auth"></el-input>
</el-form-item>
<el-form-item label="password column">
<el-input v-model="form.authPassword" placeholder="the column for password verification"></el-input>
</el-form-item>
<el-form-item label="app listen">
<el-input v-model="form.appListen" placeholder="app listening address"></el-input>
</el-form-item>
<el-form-item label="package">
<el-input v-model="form.outPackage" placeholder="the path relative to $GOPATH/src"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit">Generate</el-button>
<el-button>Cancel</el-button>
</el-form-item>
</el-form>
</template>
<script>
// @ is an alias to /src
import HelloWorld from '@/components/HelloWorld.vue'
export default {
data() {
return {
form: {
mysqlAddr: '127.0.0.1:3306',
mysqlUser: 'root',
mysqlPassword: 'password',
mysqlDatabase: 'dbname',
mysqlCharset: 'utf8',
appListen: '127.0.0.1:5555',
authTable: 'users',
authPassword: 'password',
outPackage: 'ginbro-demo'
},
msg:""
}
},
methods: {
onSubmit() {
mysqlGen(this.form).then(rsp => {
console.log(rsp)
this.$message({
message:rsp,
type: 'success'
});
}).catch( err =>{
console.log(rsp)
this.$message({
message: err,
type: 'error'
});
})
}
}
}
</script>
mysqlGenui.Bind("mysqlGen", c.MysqlGen)
mysqlGen 发送json参数到go服务,go通过unmarshal 得到放回的struct 参数执行逻辑 通过通过promise得到go绑定方法的返回结果
function.go
package main
import (
"github.com/libragen/ginbro/parser"
"sync"
)
// Go types that are bound to the UI must be thread-safe, because each binding
// is executed in its own goroutine. In this simple case we may use atomic
// operations, but for more complex cases one should use proper synchronization.
type guiFunction struct {
sync.Mutex
result string
}
type args struct {
MysqlUser string `json:"mysqlUser"`
MysqlPassword string `json:"mysqlPassword"`
MysqlAddr string `json:"mysqlAddr"`
MysqlDatabase string `json:"mysqlDatabase"`
MysqlCharset string `json:"mysqlCharset"`
OutPackage string `json:"outPackage"`
AppListen string `json:"appListen"`
AuthTable string `json:"authTable"`
AuthPassword string `json:"authPassword"`
}
package main
import (
"github.com/libragen/ginbro/parser"
"sync"
)
// Go types that are bound to the UI must be thread-safe, because each binding
// is executed in its own goroutine. In this simple case we may use atomic
// operations, but for more complex cases one should use proper synchronization.
type guiFunction struct {
sync.Mutex
result string
}
type args struct {
MysqlUser string `json:"mysqlUser"`
MysqlPassword string `json:"mysqlPassword"`
MysqlAddr string `json:"mysqlAddr"`
MysqlDatabase string `json:"mysqlDatabase"`
MysqlCharset string `json:"mysqlCharset"`
OutPackage string `json:"outPackage"`
AppListen string `json:"appListen"`
AuthTable string `json:"authTable"`
AuthPassword string `json:"authPassword"`
}
func (c *guiFunction) MysqlGen(arg args) string {
c.Lock()
defer c.Unlock()
ng, err := parser.NewGuiParseEngine(arg.MysqlUser, arg.MysqlPassword, arg.MysqlAddr, arg.MysqlDatabase, arg.MysqlCharset, arg.OutPackage, arg.AppListen, arg.AuthTable, arg.AuthPassword)
if err != nil {
return err.Error()
}
if err := ng.ParseDatabaseSchema(); err != nil {
return err.Error()
}
ng.GenerateProjectCode()
ng.GoFmt()
return "your ginbro project is created at " + ng.OutPath
} {
c.Lock()
defer c.Unlock()
ng, err := parser.NewGuiParseEngine(arg.MysqlUser, arg.MysqlPassword, arg.MysqlAddr, arg.MysqlDatabase, arg.MysqlCharset, arg.OutPackage, arg.AppListen, arg.AuthTable, arg.AuthPassword)
if err != nil {
return err.Error()
}
if err := ng.ParseDatabaseSchema(); err != nil {
return err.Error()
}
ng.GenerateProjectCode()
ng.GoFmt()
return "your ginbro project is created at " + ng.OutPath
}
type args struct
func (c *guiFunction) MysqlGen(arg args) string
type guiFunction struct sync.Mutex
main.go
//go:generate go run -tags generate gen.go
package main
import (
"fmt"
"github.com/zserge/lorca"
"log"
"net"
"net/http"
"os"
"os/signal"
)
func main() {
ui, err := lorca.New("", "", 480, 600)
if err != nil {
log.Fatal(err)
}
defer ui.Close()
// A simple way to know when UI is ready (uses body.onload event in JS)
ui.Bind("start", func() {
log.Println("UI is ready")
})
// Create and bind Go object to the UI
c := &guiFunction{}
ui.Bind("mysqlGen", c.MysqlGen)
// Load HTML.
// You may also use `data:text/html,<base64>` approach to load initial HTML,
// e.g: ui.Load("data:text/html," + url.PathEscape(html))
ln, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
log.Fatal(err)
}
defer ln.Close()
go http.Serve(ln, http.FileServer(FS))
ui.Load(fmt.Sprintf("http://%s", ln.Addr()))
// You may use console.log to debug your JS code, it will be printed via
// log.Println(). Also exceptions are printed in a similar manner.
ui.Eval(`
`)
// Wait until the interrupt signal arrives or browser window is closed
sigc := make(chan os.Signal)
signal.Notify(sigc, os.Interrupt)
select {
case <-sigc:
case <-ui.Done():
}
log.Println("exiting...")
}
ui.Bind("mysqlGen", c.MysqlGen)
运行效果图
总结
一个非常小的库,用于在Go中构建现代HTML5桌面应用程序. 它使用Chrome浏览器作为UI层. 与Electron不同,它不会将Chrome捆绑到应用程序包中,而是重用已安装的那个. Lorca建立了与浏览器窗口的连接,允许从UI调用Go代码并以无缝方式从Go操作UI.
The End
ssh $用户@mojotv.cn
ssh mojotv.cn hn