主页 > 开源代码  > 

基于energy(lclv3.0)跨平台GUI框架实现的XTASDKGUI源码实现

基于energy(lclv3.0)跨平台GUI框架实现的XTASDKGUI源码实现
项目开源地址 energy v2.0 系列, LCL 原生控件, CEFlcl v3.0 v3.0 系列为, LCL 原生控件, CEF, Webview2, Webkit2XTA SDK 源码分析

本文主要介绍 xtaui.go 是一个基于 LCL 框架的 Go 语言 GUI 应用程序的主文件。 它通过嵌入资源、初始化框架、创建主窗体并运行应用,实现了完整的程序生命周期管理。 本文从资源嵌入、初始化、异常处理、主窗体创建等方面对源码进行了详细分析。

1. 文件结构和包声明

源码文件为 xtaui.go,包声明为 main,表示这是一个可执行的 Go 程序。

2. 导入的包 embed: 用于嵌入静态资源文件。fmt: 标准输出包,用于打印日志。github /energye/lcl/inits 和 github /energye/lcl/lcl: 这是 LCL(Light Controls Library)框架的包,用于创建跨平台的 GUI 应用。xta/xtaui/window: 本地包,用于定义主窗口逻辑。 3. 嵌入资源 使用 go:embed 指令嵌入了两个目录: assets: 用于存放静态资源(如图片、配置文件等)。libs: 用于存放动态链接库(如平台依赖的二进制文件)。 4. 主函数 (main()) 初始化资源: 调用 inits.Init(lib, resources),将嵌入的资源和库文件传递给 LCL 框架进行初始化。初始化应用: 调用 lcl.Application.Initialize(),初始化 LCL 框架。设置异常处理: 通过 SetOnException 方法设置全局异常处理函数,捕获并打印错误信息。设置主窗体: 调用 SetMainFormOnTaskBar(true),确保主窗体显示在任务栏中。创建和运行主窗体: 通过 CreateForm 创建主窗体(MainWindow),并调用 Run() 方法启动应用。
源码概述

xtaui.go 是一个基于 LCL 框架的 Go 语言 GUI 应用程序的主文件。它通过嵌入静态资源和动态链接库,初始化 LCL 框架,并创建和运行主窗口。


资源嵌入与初始化

资源嵌入: 使用 go:embed 指令将 assets 和 libs 目录嵌入到可执行文件中。这种方式可以避免在运行时依赖外部文件,使程序更加 portable( portable,便于移植)。

初始化流程:

调用 inits.Init(lib, resources),将嵌入的资源传递给 LCL 框架。调用 lcl.Application.Initialize(),完成 LCL 框架的初始化。
异常处理机制

通过 SetOnException 方法,程序全局捕获异常并打印错误信息。这种机制可以有效避免程序因未捕获的异常而崩溃,同时为调试提供日志支持。


主窗体的创建与运行

创建主窗体: 调用 lcl.Application.CreateForm(&window.MainWindow) 创建主窗体。MainWindow 是从本地包 xta/xtaui/window 中导入的结构体,用于定义窗体的外观和行为。

运行应用: 调用 lcl.Application.Run() 启动应用程序,进入消息循环,处理用户交互。


LCL 框架简介

LCL是一个跨平台的 GUI 开发框架,支持 Windows、Linux 和 macOS 操作系统。通过 LCL,开发者可以使用 Go 语言快速构建功能丰富的桌面应用。


示例截图 程序文件大小

执行文件: 15M 压缩后: 5M

截图

程序部分源码 main 入口 package main import ( "embed" "fmt" "github /energye/lcl/inits" "github /energye/lcl/lcl" "xta/xtaui/window" ) //go:embed assets var resources embed.FS //go:embed libs var lib embed.FS func main() { inits.Init(lib, resources) lcl.Application.Initialize() lcl.Application.SetOnException(func(sender lcl.IObject, e lcl.IException) { fmt.Println("Exception:", e.ToString()) }) lcl.Application.SetMainFormOnTaskBar(true) lcl.Application.CreateForm(&window.MainWindow) lcl.Application.Run() } 程序主窗口 package window import ( "bufio" "errors" "fmt" "github /energye/lcl/lcl" "github /energye/lcl/rtl" "github /energye/lcl/types" "github /energye/lcl/types/colors" "github /energye/xta/chat" "io/fs" "os" "time" _ "xta/xtaui/syso" ) type TMainWindow struct { lcl.TForm message lcl.IMemo chat lcl.IMemo ai chat.IGiteeAI chatBtn lcl.IButton selFileBtn lcl.IButton selDirDlg lcl.ISelectDirectoryDialog saveChatBtn lcl.IButton saveDirDlg lcl.ISaveDialog savePathInp lcl.IMemo saveFileBuf *bufio.Writer fileWindow []*FileWindow title string } var MainWindow TMainWindow func (m *TMainWindow) FormCreate(sender lcl.IObject) { m.title = "ENERGY - XTA Chat UI" m.SetCaption(m.title) m.SetPosition(types.PoScreenCenter) m.SetWidth(1024) m.SetHeight(768) m.SetShowHint(true) m.Constraints().SetMinWidth(types.TConstraintSize(m.Width())) m.Constraints().SetMinHeight(types.TConstraintSize(m.Height())) png := lcl.NewPngImage() png.LoadFromFSFile("assets/icon.png") lcl.Application.Icon().Assign(png) png.Free() m.initMainBox() } func (m *TMainWindow) initMainBox() { go m.initXTASDK() openURL := lcl.NewLinkLabel(m) openURL.SetParent(m) openURL.SetCaption(`<a href=" ai.gitee /models"> [Gitee AI API 获取] </a>`) openURL.SetAlign(types.AlRight) openURL.SetTop(5) openURL.Font().SetSize(12) openURL.SetOnLinkClick(func(sender lcl.IObject, link string, linktype types.TSysLinkType) { rtl.SysOpen(link) }) open1URL := lcl.NewLinkLabel(m) open1URL.SetParent(m) open1URL.SetCaption(`<a href=" github /energye/xta"> [XTA AI SDK] </a>`) open1URL.SetAlign(types.AlRight) open1URL.SetTop(5) open1URL.Font().SetSize(12) open1URL.SetOnLinkClick(func(sender lcl.IObject, link string, linktype types.TSysLinkType) { rtl.SysOpen(link) }) modules := lcl.NewComboBox(m) modules.SetParent(m) modules.SetLeft(150) modules.Items().AddStrings2(chat.GiteeAIModels()) modules.SetItemIndex(17) modules.SetHeight(35) modules.SetWidth(300) modules.Font().SetSize(12) modules.SetOnChange(func(sender lcl.IObject) { module := chat.GiteeAIModelNameEnum(modules.Items().Strings(modules.ItemIndex())) m.ai.SetModel(module) m.message.Lines().Add("模型: " + m.ai.Model()) m.SetCaption(m.title + " " + m.ai.Model()) }) apiKey := lcl.NewEditButton(m) apiKey.SetParent(m) apiKey.SetLeft(modules.Left() + modules.Width() + 5) apiKey.SetPasswordChar(uint16('*')) apiKey.SetHeight(35) apiKey.SetWidth(200) apiKey.Font().SetSize(12) apiKey.Button().SetCaption("API KEY") //apiKey.Button().SetLeft(100) apiKey.Button().SetWidth(80) apiKey.SetOnClick(func(sender lcl.IObject) { m.ai.Options().APIKey = apiKey.Text() }) // 消息 m.message = lcl.NewMemo(m) m.message.SetParent(m) m.message.SetTop(40) m.message.SetLeft(150) m.message.SetWidth(m.Width() - 150) m.message.SetHeight(m.Height() - 190) m.message.SetBorderStyle(types.BsNone) m.message.SetReadOnly(true) m.message.SetScrollBars(types.SsAutoBoth) m.message.SetWordWrap(true) m.message.SetAnchors(types.NewSet(types.AkLeft, types.AkTop, types.AkRight, types.AkBottom)) // 聊天 m.chat = lcl.NewMemo(m) m.chat.SetParent(m) m.chat.SetBorderStyle(types.BsNone) m.chat.SetScrollBars(types.SsAutoBoth) m.chat.SetTop(m.message.Top() + m.message.Height() + 3) m.chat.SetLeft(150) m.chat.SetWidth(m.Width()) m.chat.SetHeight(100) m.chat.SetAnchors(types.NewSet(types.AkLeft, types.AkRight, types.AkBottom)) chatLabel := lcl.NewLabel(m) chatLabel.SetParent(m) chatLabel.SetCaption("发送消息") chatLabel.SetTop(m.chat.Top() + 40) chatLabel.SetLeft(20) chatLabel.Font().SetSize(18) chatLabel.Font().SetColor(colors.ClGray) chatLabel.SetAnchors(types.NewSet(types.AkLeft, types.AkBottom)) // 发送消息 m.chatBtn = lcl.NewButton(m) m.chatBtn.SetParent(m) m.chatBtn.SetTop(m.chat.Top() + m.chat.Height() + 3) m.chatBtn.SetWidth(100) m.chatBtn.SetHeight(45) m.chatBtn.SetCaption("发 送") m.chatBtn.Font().SetSize(16) m.chatBtn.SetLeft(m.Width() - 100) m.chatBtn.SetAnchors(types.NewSet(types.AkRight, types.AkBottom)) m.chatBtn.SetOnClick(m.SendMessage) // 选择文件 m.selDirDlg = lcl.NewOpenDialog(m) m.selDirDlg.SetOptions(m.selDirDlg.Options().Include(types.OfShowHelp, types.OfAllowMultiSelect)) m.selDirDlg.SetTitle("XTA - AI SDK 打开文件 多选") m.selFileBtn = lcl.NewButton(m) m.selFileBtn.SetParent(m) m.selFileBtn.SetTop(m.chat.Top() + m.chat.Height() + 3) m.selFileBtn.SetWidth(150) m.selFileBtn.SetHeight(40) m.selFileBtn.SetCaption("选择文件/多选") m.selFileBtn.Font().SetSize(12) m.selFileBtn.SetLeft(150) m.selFileBtn.SetOnClick(m.selectFileOrDir) m.selFileBtn.SetAnchors(types.NewSet(types.AkLeft, types.AkBottom)) // 保存消息 m.saveDirDlg = lcl.NewSaveDialog(m) m.saveDirDlg.SetFilter("文本文件(*.txt)|*.txt|所有文件(*.*)|*.*") m.saveDirDlg.SetTitle("XTA - AI SDK 消息保存") m.saveChatBtn = lcl.NewButton(m) m.saveChatBtn.SetParent(m) m.saveChatBtn.SetTop(m.chat.Top() + m.chat.Height() + 3) m.saveChatBtn.SetLeft(m.selFileBtn.Left() + m.selFileBtn.Width() + 3) m.saveChatBtn.SetCaption("保存消息") m.saveChatBtn.SetWidth(100) m.saveChatBtn.SetHeight(40) m.saveChatBtn.Font().SetSize(12) m.saveChatBtn.SetAnchors(types.NewSet(types.AkLeft, types.AkBottom)) m.saveChatBtn.SetOnClick(func(sender lcl.IObject) { if m.saveDirDlg.Execute() { m.savePathInp.SetText(m.saveDirDlg.FileName()) } }) m.savePathInp = lcl.NewMemo(m) m.savePathInp.SetParent(m) m.savePathInp.SetTop(m.chat.Top() + m.chat.Height() + 3) m.savePathInp.SetLeft(m.saveChatBtn.Left() + m.saveChatBtn.Width() + 3) m.savePathInp.SetHeight(40) m.savePathInp.SetWidth(300) m.savePathInp.Font().SetSize(15) m.savePathInp.SetWordWrap(false) m.savePathInp.SetAnchors(types.NewSet(types.AkLeft, types.AkBottom)) var savefile *os.File m.savePathInp.SetOnChange(func(sender lcl.IObject) { if savefile != nil { savefile.Close() savefile = nil m.saveFileBuf = nil } path := m.savePathInp.Text() fe, err := os.Open(path) if err == nil { defer fe.Close() st, err := fe.Stat() if err == nil { if !st.IsDir() { savefile, err = os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if err == nil { m.saveFileBuf = bufio.NewWriter(savefile) } } } } else if errors.Is(err, fs.ErrNotExist) { savefile, err = os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if err == nil { m.saveFileBuf = bufio.NewWriter(savefile) } } }) // 窗口显示 m.SetOnShow(func(sender lcl.IObject) { m.chat.SetFocus() apiKey.SetText(m.ai.APIKey()) }) //clearHistory := lcl.NewButton(m) //clearHistory.SetParent(m) //clearHistory.SetOnClick(func(sender lcl.IObject) { // m.ai.History() //}) } // 主窗口左侧创建文件项 func (m *TMainWindow) createFileItem(filewindow *FileWindow) { btn := lcl.NewButton(m) btn.SetParent(m) caption := filewindow.fileDesc if caption == "" { caption = filewindow.filenames } btn.SetCaption(caption) btn.SetOnClick(func(sender lcl.IObject) { m.removeFileBtn(filewindow.id) }) btn.SetHint("点击删除该文件项") filewindow.fileBtn = btn m.fileWindow = append(m.fileWindow, filewindow) m.resortFileBtns() } func (m *TMainWindow) removeFileBtn(id string) { var newwindows []*FileWindow for _, fw := range m.fileWindow { if fw.id == id { lcl.RunOnMainThreadAsync(func(id uint32) { fw.fileBtn.Free() fw.Close() }) } else { newwindows = append(newwindows, fw) } } m.fileWindow = newwindows m.resortFileBtns() } func (m *TMainWindow) resortFileBtns() { for i, fw := range m.fileWindow { fw.fileBtn.SetLeft(5) fw.fileBtn.SetWidth(130) fw.fileBtn.SetHeight(30) fw.fileBtn.SetTop(int32(i*30) + 5) } } func (m *TMainWindow) selectFileOrDir(sender lcl.IObject) { if m.selDirDlg.Execute() { files := m.selDirDlg.Files() var file []string for i := 0; i < int(files.Count()); i++ { fmt.Println(files.ValueFromIndex(int32(i))) file = append(file, files.ValueFromIndex(int32(i))) } win := createWindow(file, func(window *FileWindow) { m.createFileItem(window) }) win.Show() } } func nowDatetime() string { return time.Now().Format("2006-01-02 15:04:05") } XTA - AI SDK package window import ( "bytes" "fmt" "github /energye/lcl/lcl" "github /energye/xta/chat" "os" "strings" ) func (m *TMainWindow) initXTASDK() { options := chat.DefaultGiteeAIOptions options.APIKey = os.Getenv(chat.ENV_AI_API_KEY) m.ai = chat.NewGiteeAI(options, false) m.ai.System("【系统角色】你具备跨领域知识整合与结构化推理能力的智能助手。始终遵循:事实准确性 > 响应速度 > 表达流畅度的优先级原则。【响应规范】1. 解析阶段:识别问题类型(事实/观点/方法需求),标注关键信息置信度2. 处理阶段:- 事实类:提供最新权威信源+时间戳- 观点类:多视角分析+概率评估 - 方法类:分步实施框架+风险预案3. 输出阶段:采用「结论-依据-延伸」结构,技术概念附带白话解释【安全协议】- 对潜在争议内容自动附加免责声明- 医疗/法律建议必须提示咨询专业人士- 实时监测对话情感倾向,对焦虑/紧急表达启动安抚话术") isFirstRec := false m.ai.SetOnReceive(func(message *chat.TResponse) { if !isFirstRec { m.message.Lines().Add("回复: " + nowDatetime()) isFirstRec = true } // 在异步UI线程里操作 lcl.RunOnMainThreadAsync(func(id uint32) { if message != nil { if message.Error != "" { s := fmt.Sprintf("错误: %v %v", message.Error, message.ErrorType) m.message.Lines().Add(s) if m.saveFileBuf != nil { m.saveFileBuf.WriteString(s) m.saveFileBuf.Flush() } } choices := message.Choices for _, choice := range choices { if strings.Contains(choice.Delta.Content, "\n") { m.message.Lines().Add(choice.Delta.Content) } else { m.message.SetSelStart(int32(len(m.message.Lines().Text()))) m.message.SetSelText(choice.Delta.Content) } if m.saveFileBuf != nil { m.saveFileBuf.WriteString(choice.Delta.Content) m.saveFileBuf.Flush() } } } else { fmt.Println("结束") m.message.Lines().Add("") m.chatBtn.SetEnabled(true) isFirstRec = false } }) }) m.ai.SetOnFail(func(message *chat.TResponseError) { lcl.RunOnMainThreadSync(func() { s := fmt.Sprintf(" 错误: %v %v %v", message.Code, message.Message, message.Type) m.message.Lines().Add(s) m.chatBtn.SetEnabled(true) isFirstRec = false }) }) lcl.RunOnMainThreadSync(func() { m.message.Lines().Add("XTA - AI SDK 初始化完成") m.message.Lines().Add("模型: " + m.ai.Model()) //m.message.Lines().Add("APIKEY: ........." + m.ai.APIKey()[5:10] + "............") m.SetCaption(m.title + " " + m.ai.Model()) }) } func (m *TMainWindow) SendMessage(sender lcl.IObject) { msg := m.chat.Lines().Text() if msg != "" { m.message.Lines().Add("我: " + nowDatetime()) m.message.Lines().Add(" " + msg) buf := bytes.Buffer{} buf.WriteString(msg + "\n") for _, fw := range m.fileWindow { if fw.isSend { continue } fw.isSend = true buf.WriteString(fw.text.Text() + "\n") buf.WriteString(strings.Join(fw.fileContent, "\n")) } m.sendMessage(buf.String()) m.chat.SetText("") } } func (m *TMainWindow) sendMessage(content string) { // 在协程里操作 go m.ai.ChatStream(content) m.chatBtn.SetEnabled(false) }
标签:

基于energy(lclv3.0)跨平台GUI框架实现的XTASDKGUI源码实现由讯客互联开源代码栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“基于energy(lclv3.0)跨平台GUI框架实现的XTASDKGUI源码实现