go使用walk创建window界面,与ai对话

前面我们学过搭建ai服务器:https://blog.csdn.net/daily886/article/details/96118130

下面,我们在window系统中,使用walk创建一个window界面,与ai对话

walk github :https://github.com/lxn/walk

需要用到的第三方包有:

go get github.com/gin-gonic/gin
go get github.com/lxn/walk
go get github.com/spf13/viper
go get github.com/tidwall/gjson
go get github.com/akavel/rsrc
go get -u -v golang.org/x/crypto/...

目录结构如下: 
项目根目录是 aichatwindow


aichatwindow的目录结构
├── conf                         # 配置文件统一存放目录
│   ├── config.yaml              # 配置文件
├── config                       # 专门用来处理配置和配置文件的Go package
│   └── config.go                 
├── handler                      # 类似MVC架构中的C,用来读取输入,并将处理流程转发给实际的处理函数,最后返回结果
│   ├── handler.go
├── model                        #数据模型
│   ├── chater.go                  # 图灵参数构造体模型
│   ├── windowdialog.go                  # walk模型
├── pkg                          # 引用的包
│   ├── errno                    # 错误码存放位置
│   │   ├── code.go
│   │   └── errno.go
├── aichatwindow.manifest                      # walk需要使用的 manifest 文件
├── main.go                      # Go程序唯一入口

下面,我们根据目录结构,从上往下建立文件夹和文件
建立文件夹和文件 aichatwindow/conf/config.yaml ,config.yaml 内容如下:

common:
  #http://www.tuling123.com 图灵机器人接口
  tuling:
    apikey: 你申请的图灵apikey
    api: http://openapi.tuling123.com/openapi/api/v2

建立文件夹和文件 aichatwindow/config/config.go ,config.go 内容如下:

package config

import (
	"github.com/spf13/viper"
	"time"
	"os"
	"log"
)

// LogInfo 初始化日志配置
func LogInfo() {
	file := "./logs/" + time.Now().Format("2006-01-02") + ".log"
	logFile, _ := os.OpenFile(file,os.O_RDWR| os.O_CREATE| os.O_APPEND, 0755)
	log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
	log.SetOutput(logFile)
}

// Init 读取初始化配置文件
func Init() error {
	//初始化配置
	if err := Config();err != nil{
		return err
	}

	//初始化日志
	LogInfo()
	return nil
}

// Config viper解析配置文件
func Config() error{
	viper.AddConfigPath("conf")
	viper.SetConfigName("config")
	if err := viper.ReadInConfig();err != nil{
		return err
	}
	return nil
}

建立文件夹和文件 aichatwindow/handler/handler.go ,handler.go 内容如下:

package handler

import (
	"bytes"
	"net/http"
	"io/ioutil"

	"github.com/gin-gonic/gin"

	"aichatwindow/pkg/errno"
)

type Response struct {
	Code    int         `json:"code"`
	Message string      `json:"message"`
	Data    interface{} `json:"data"`
}

//返回json 格式
func SendResponse(c *gin.Context,err error,data interface{}){
	code,message := errno.DecodeErr(err)

	//总是返回http状态ok
	c.JSON(http.StatusOK,Response{
		Code: code,
		Message:message,
		Data: data,
	})

}

//返回html 格式
func SendResponseHtml(c *gin.Context,err error,data string){
	c.Header("Content-Type", "text/html; charset=utf-8")
	//总是返回http状态ok
	c.String(http.StatusOK,data)
}

//http请求
func HttpRequest(api string,json string,method string) (string, error) {
	jsonStr := []byte(json)
	req, err := http.NewRequest(method, api, bytes.NewBuffer(jsonStr))
	req.Header.Set("Content-Type", "application/json") //使用json格式传参

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return "", errno.ApiServerError
	}
	defer resp.Body.Close()

	body, _ := ioutil.ReadAll(resp.Body)

	if !(resp.StatusCode == 200) {
		return "",  errno.ApiServerError
	}
	return string(body), nil
}

建立文件夹和文件 aichatwindow/model/chater.go ,chater.go 内容如下:

package model

import (
	"encoding/json"

	"aichatwindow/pkg/errno"
)

/*
传入参数 json
{
	"reqType":0,
    "perception": {
        "inputText": {
            "text": "附近的酒店"
        },
        "inputImage": {
            "url": "imageUrl"
        },
        "selfInfo": {
            "location": {
                "city": "北京",
                "province": "北京",
                "street": "信息路"
            }
        }
    },
    "userInfo": {
        "apiKey": "",
        "userId": ""
    }
}
*/

type Chatting struct {
	ReqType    int        `json:"reqType"`
	Perception Perception `json:"perception"`
	UserInfo   UserInfo   `json:"userInfo"`
}

type InputText struct {
	Text string `json:"text"`
}

type Perception struct {
	InputText InputText `json:"inputText"`
}

type UserInfo struct {
	ApiKey string `json:"apiKey"`
	UserId string `json:"userId"`
}
//更新 图灵参数构造体 信息
func UpdateChatting(userId string, text string, chattingInfo Chatting) Chatting {
	chattingInfo.UserInfo.UserId = userId
	chattingInfo.Perception.InputText.Text = text
	return chattingInfo
}
//建立 图灵参数构造体
func BuildChatting(text string, userId string,appKey string) Chatting {
	chatting := Chatting{ReqType: 0}
	chatting.Perception = buildPerception(text)
	chatting.UserInfo = buildUserInfo(userId,appKey)
	return chatting
}
//建立 Perception
func buildPerception(text string) Perception {
	perception := Perception{buildInputText(text)}
	return perception
}
//建立 InputText
func buildInputText(text string) InputText {
	inputText := InputText{text}
	return inputText
}
//建立 UserInfo
func buildUserInfo(userId string,appKey string) UserInfo {
	return UserInfo{appKey, userId}
}

//构造体转换成字符串
func ConvertJson(chattingInfo Chatting) (string,error) {
	jsons, errs := json.Marshal(chattingInfo)
	if errs != nil {
		return "", errno.ModelError
	}
	return string(jsons),nil
}

建立文件夹和文件 aichatwindow/model/windowdialog.go ,windowdialog.go 内容如下:

package model

import (
	"fmt"
	"strconv"

	"github.com/tidwall/gjson"
	"github.com/spf13/viper"
	"github.com/lxn/walk"
	. "github.com/lxn/walk/declarative"

	. "aichatwindow/handler"
)

/*-------------------window界面-------------------*/
func OpenWindow() {
	mw := &MyMainWindow{}

	if _, err := (MainWindow{
		AssignTo: &mw.MainWindow,
		Title:    "AI对话",
		//Icon:     "test.ico",
		MinSize:  Size{300, 400},
		Layout:   VBox{},
		Children: []Widget{
			GroupBox{
				Layout: HBox{},
				Children: []Widget{
					LineEdit{
						AssignTo: &mw.searchBox,
					},
					PushButton{
						Text:      "对话",
						OnClicked: mw.clicked,
					},
				},
			},
			TextEdit{
				AssignTo: &mw.textArea,
			},
			ListBox{
				AssignTo: &mw.results,
				Row:      5,
			},
		},
	}.Run());
	err != nil {
		fmt.Printf("Run err: %+v\n",err)
	}
}

type MyMainWindow struct {
	*walk.MainWindow
	searchBox *walk.LineEdit
	textArea  *walk.TextEdit
	results   *walk.ListBox
}

func (mw *MyMainWindow) clicked() {
	word := mw.searchBox.Text()
	model := []string{}
	for _, value := range search( word) {
		model = append(model, fmt.Sprintf("对话回复:%v", value))
	}
	fmt.Println(model)
	mw.results.SetModel(model)
}

/*-------------------图灵接口-------------------*/
//获取tuling接口回复
func TulingAi(info string) (string,error) {
	api := viper.GetString("common.tuling.api")

	//发送http请求图灵api  , body是http响应
	var body, resultErrs = HttpRequest(api,info,"POST")
	if resultErrs != nil {
		fmt.Printf("HttpRequest err: %+v\n",resultErrs)
	}

	return body, nil
}


//图灵搜索
func search( word string) (res []string) {
	res = []string{}
	var userId = "1";
	//图灵接口参数构造体
	var chattingInfo = BuildChatting(word,userId, viper.GetString("common.tuling.apikey"))
	fmt.Printf("chattingInfo: %+v\n",chattingInfo)
	// 参数构造体 转换成 字符串
	chatstr,err := ConvertJson(chattingInfo)
	if err != nil{
		fmt.Printf("ConvertJson err: %+v\n",err)
		return
	}

	//调用图灵接口
	body,err := TulingAi(chatstr)
	if err != nil{
		fmt.Printf("TulingAi err: %+v\n",err)
		return
	}
	fmt.Printf("body err: %+v\n",body)
	var results string
	// 使用gjson 获取返回结果的 resultType
	result := gjson.Get(body, "results.#.resultType")
	for key, name := range result.Array() {
		//如果 resultType 是 text格式
		if name.String() == "text"{
			//获取对应 key 的 values里的text ,就是图灵回复的文字
			getstring := "results."+strconv.Itoa(key)+".values.text"
			fmt.Printf("getstring: %+v\n",getstring)
			result_text := gjson.Get(body,getstring)
			results = result_text.String()
			res = append(res, results)
		}
	}
	return
}

建立文件夹和文件 aichatwindow/pkg/errno/code.go ,code.go 内容如下:

package errno

var (
	// Common errors
	OK                  = &Errno{Code: 0, Message: "OK"}
	VALUEERROR        = &Errno{Code: -1, Message: "输入错误"}

	InternalServerError = &Errno{Code: 10001, Message: "服务器错误"}
	ApiServerError = &Errno{Code: 20001, Message: "接口服务器错误"}
	ModelError = &Errno{Code: 30001, Message: "聊天模型错误"}
)

建立文件夹和文件 aichatwindow/pkg/errno/errno.go ,errno.go 内容如下:

package errno

import "fmt"

type Errno struct {
	Code int
	Message string
}

//返回错误信息
func (err Errno) Error() string{
	return err.Message
}

//设置 Err 结构体
type Err struct {
	Code int
	Message string
	Err error
}

//声明构造体
func New(errno *Errno,err error) *Err{
	return &Err{Code:errno.Code,Message:errno.Message,Err:err}
}

//添加错误信息
func (err *Err) Add(message string) error{
	err.Message += " " + message
	return err
}

//添加指定格式的错误信息
func (err * Err) Addf(format string,args...interface{}) error{
	err.Message += " " + fmt.Sprintf(format,args...)
	return err
}

//拼接错误信息字符串
func (err *Err) Error() string{
	return fmt.Sprintf("Err - code: %d, message: %s, error: %s",err.Code,err.Message,err.Err)
}

// 解析 错误信息, 返回字符串
func DecodeErr(err error) (int,string){
	if err == nil{
		return OK.Code,OK.Message
	}
	switch typed := err.(type) {
	case *Err:
		return typed.Code,typed.Message
	case *Errno:
		return typed.Code,typed.Message
	default:
	}
	return InternalServerError.Code,err.Error()
}

建立文件夹和文件 aichatwindow/aichatwindow.manifest , aichatwindow.manifest内容如下:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
    <assemblyIdentity version="1.0.0.0" processorArchitecture="*" name="SomeFunkyNameHere" type="win32"/>
    <dependency>
        <dependentAssembly>
            <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"/>
        </dependentAssembly>
    </dependency>
    <application xmlns="urn:schemas-microsoft-com:asm.v3">
        <windowsSettings>
            <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor</dpiAwareness>
            <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">True</dpiAware>
        </windowsSettings>
    </application>
</assembly>

建立文件夹和文件 aichatwindow/main.go ,main.go 内容如下:

package main

import (
	"fmt"

	"aichatwindow/config"
	"aichatwindow/model"
)

func main() {
	if err := config.Init();err != nil{
		fmt.Println(err)
	}

	model.OpenWindow()
}

初始化包

[root@localhost aichatwindow]# go mod init aichatwindow
go: creating new go.mod: module aichatwindow

使用rsrc命令创建 rsrc.syso文件

[root@localhost aichatwindow]# find / -name rsrc
/root/go/bin/rsrc
/root/go/pkg/mod/cache/download/github.com/akavel/rsrc
/root/go/pkg/mod/github.com/akavel/rsrc@v0.8.0/rsrc


[root@localhost aichatwindow]# /root/go/bin/rsrc -manifest aichatwindow.manifest -o rsrc.syso

打包成window系统的 .exe 文件

[root@localhost aichatwindow]# CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build


[root@localhost aichatwindow]# ll
total 21180
-rwxr-xr-x. 1 root root 21651456 Jul 29 17:38 aichatwindow.exe
-rw-r--r--. 1 root root      889 Jul 29 17:14 aichatwindow.manifest
drwxr-xr-x. 2 root root       25 Jul 29 16:52 conf
drwxr-xr-x. 2 root root       23 Jul 29 16:52 config
-rw-------. 1 root root      614 Jul 29 17:24 go.mod
-rw-------. 1 root root    17061 Jul 29 17:24 go.sum
drwxr-xr-x. 2 root root       24 Jul 29 16:52 handler
-rw-r--r--. 1 root root      175 Jul 29 17:14 main.go
drwxr-xr-x. 2 root root       46 Jul 29 16:52 model
drwxr-xr-x. 3 root root       19 Jul 29 16:52 pkg
-rw-r--r--. 1 root root     1069 Jul 29 17:38 rsrc.syso

把aichatwindow.exe复制到window系统下,双击运行 aichatwindow.exe 即可运行,效果如下:

若提示错误,/root/go/pkg/mod/github.com/lxn/walk@v0.0.0-20190619151032-86d8802c197a/icon.go:121:19: undefined: "golang.org/x/sys/windows".GetSystemDirectory  ,解决方法如下:

[root@localhost aichatwindow]# go get -u -v golang.org/x/crypto/...

[root@localhost aichatwindow]# rsrc -manifest aichatwindow.manifest -o rsrc.syso

参考:https://www.oschina.net/code/snippet_1375875_33929

参考:https://blog.csdn.net/weixin_34245082/article/details/86279240

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值