教程三 在Go中使用Energy创建跨平台应用 - 状态控制

教程-示例-文档

本文介绍在Energy中如何像浏览器一样控制状态, 页面的加载、前进、后退、刷新、暂停刷新

前提-需要安装好开发环境参考: 教程一 环境安装

创建应用

  • 开发环境中 MacOSX平台必须在"GlobalCEFInit"之前设置CEF
  • 设置使用CEF 和 CEF框架目录,生成开发执行应用程序包
    • macapp.MacApp.IsCEF(common.IsDarwin())
  • 全局初始化 每个应用都必须调用的
    • cef.GlobalCEFInit(&libs, &resources)
  • 可选的应用配置
    • cfg := cef.NewApplicationConfig()
  • 指定chromium的二进制包框架根目录, 不指定为当前程序执行目录
    • 环境变量ENERGY_HOME="/app/cefframework" 配置框架所在目录

  if common.IsWindows() {
      //SetFrameworkDirPath 或 配置环境变量 ENERGY_HOME
      //cfg.SetFrameworkDirPath("D:\\app.exe\\energy\\chromium64")
  } else if common.IsLinux() {
      //cfg.SetFrameworkDirPath("/home/sxm/app/swt/energy/chromium")
  }
  • 创建应用
    • cef.NewApplication(cfg)
  • 主进程窗口初始化函数
    • src.MainBrowserWindow()
package main

import (
    "embed"
    "github.com/energye/energy/cef"
    "github.com/energye/energy/common"
    "github.com/energye/energy/example/browser-control/src"
    "github.com/energye/golcl/pkgs/macapp"
)

//go:embed resources
var resources embed.FS

//go:embed libs
var libs embed.FS

func main() {
    //开发环境中 MacOSX平台必须在"GlobalCEFInit"之前设置CEF
    //设置使用CEF 和 CEF框架目录,生成开发执行应用程序包
    macapp.MacApp.IsCEF(common.IsDarwin())
    //环境变量 ENERGY_HOME="/app/cefframework" 配置框架所在目录
    //全局初始化 每个应用都必须调用的
    cef.GlobalCEFInit(&libs, &resources)
    //可选的应用配置
    cfg := cef.NewApplicationConfig()
    //指定chromium的二进制包框架根目录,
    //不指定为当前程序执行目录
    if common.IsWindows() {
        //SetFrameworkDirPath 或 配置环境变量 ENERGY_HOME
        //cfg.SetFrameworkDirPath("D:\\app.exe\\energy\\chromium64")
    } else if common.IsLinux() {
        //cfg.SetFrameworkDirPath("/home/sxm/app/swt/energy/chromium")
    }
    //创建应用
    cefApp := cef.NewApplication(cfg)
    //主进程窗口
    src.MainBrowserWindow()

    //运行应用
    cef.Run(cefApp)
}

系统UI和Energy

使用系统组件 golcl 创建控制相关按钮

初始主进程和窗口

  • 在非主进程中有些代码是不必执行的。通过IsMain函数判断是否为主进程

    • commons.Args.IsMain()
  • 使用主浏览器的配置属性对浏览器进行了简单的配置

    • cef.BrowserWindow.Config
  • 使用ChromiumConfig配置开启右键菜单和开发者工具

    • config.SetEnableMenu(true)
    • config.SetEnableDevTools(true)
  • 创建窗口时的回调函数 对浏览器事件设置,和窗口属性组件等创建和修改

  • cef.BrowserWindow.SetBrowserInit 窗口初始函数

  • 在窗口初始函数中

    • 设置更多的窗口属性
    • 窗口的事件
    • 浏览器的事件
  • <pre>cef.QueueAsyncCall 函数,在Linux中和操作UI相关的功能中,需要使用该函数包裹住,该函数会将被执行的函数移至主线程中执行。否则操作UI相关或Linux系统中会导致UI线程锁死</pre>

  • <pre>func controlUI 函数,在SetBrowserInit 函数中调用,主要创建主窗口内的系统UI组件</pre>

package src

import (
    "fmt"
    "github.com/energye/energy/cef"
    "github.com/energye/energy/common"
    "github.com/energye/golcl/lcl"
    "github.com/energye/golcl/lcl/types"
)

//主浏览器窗口
func MainBrowserWindow() {
    //只有启动主进程才会继续执行
    if !common.Args.IsMain() {
        return
    }
    //主窗口的配置
    //指定一个URL地址,或本地html文件目录
    cef.BrowserWindow.Config.DefaultUrl = "https://energy.yanghy.cn/"
    //窗口的标题
    cef.BrowserWindow.Config.Title = "Energy - 浏览器控制"
    //窗口宽高
    cef.BrowserWindow.Config.Width = 1024
    cef.BrowserWindow.Config.Height = 768
    //chromium配置
    config := cef.NewChromiumConfig()
    config.SetEnableMenu(true)
    config.SetEnableDevTools(true)
    cef.BrowserWindow.Config.SetChromiumConfig(config)
    //创建窗口时的回调函数 对浏览器事件设置,和窗口属性组件等创建和修改
    cef.BrowserWindow.SetBrowserInit(func(event *cef.BrowserEvent, browserWindow *cef.TCefWindowInfo) {
        //设置应用图标 这里加载的图标是内置到执行程序里的资源文件
        lcl.Application.Icon().LoadFromFSFile("resources/icon.ico")
        //在窗体初始化时创建窗口内的组件
        back, forward, stop, refresh, progressLabel, addr := controlUI(browserWindow)
        //页面加载处理进度
        event.SetOnLoadingProgressChange(func(sender lcl.IObject, browser *cef.ICefBrowser, progress float64) {
            //linux 更新UI组件必须使用 QueueAsyncCall 主线程异步同步
            cef.QueueAsyncCall(func(id int) {
                //参数-进度
                progressLabel.SetCaption(fmt.Sprintf("%v", progress*100))
            })
        })
        //页面加载状态,根据状态判断是否加载完成,和是否可前进后退
        event.SetOnLoadingStateChange(func(sender lcl.IObject, browser *cef.ICefBrowser, isLoading, canGoBack, canGoForward bool) {
            //linux 更新UI组件必须使用 QueueAsyncCall 主线程异步同步
            cef.QueueAsyncCall(func(id int) {
                //控制按钮状态
                stop.SetEnabled(isLoading)
                refresh.SetEnabled(!isLoading)
                back.SetEnabled(canGoBack)
                forward.SetEnabled(canGoForward)
            })
        })
        event.SetOnAddressChange(func(sender lcl.IObject, browser *cef.ICefBrowser, frame *cef.ICefFrame, url string) {
            cef.QueueAsyncCall(func(id int) {
                addr.SetText(url)
            })
        })
    })
    //创建窗口之后对对主窗口的属性、组件或子窗口的创建
    cef.BrowserWindow.SetBrowserInitAfter(func(browserWindow *cef.TCefWindowInfo) {
        fmt.Println("SetBrowserInitAfter")
    })
}

//控制组件UI
//地址栏和控制按钮创建
func controlUI(browserWindow *cef.TCefWindowInfo) (goBack *lcl.TButton, goForward *lcl.TButton, stop *lcl.TButton, refresh *lcl.TButton, progressLabel *lcl.TLabel, addrBox *lcl.TComboBox) {
    window := browserWindow.Window
    //这里使用系统UI组件
    //创建panel做为地址栏的父组件
    addrPanel := lcl.NewPanel(window) //设置父组件
    addrPanel.SetParent(window)
    addrPanel.SetAnchors(types.NewSet(types.AkLeft, types.AkTop, types.AkRight)) //设置锚点定位,让宽高自动根据窗口调整大小
    addrPanel.SetHeight(25)
    addrPanel.SetWidth(window.Width())
    //创建 按钮-后退
    goBack = lcl.NewButton(addrPanel) //设置父组件
    goBack.SetParent(addrPanel)
    goBack.SetCaption("后退")
    goBack.SetBounds(0, 0, 35, 25)
    goForward = lcl.NewButton(addrPanel) //设置父组件
    goForward.SetParent(addrPanel)
    goForward.SetCaption("前进")
    goForward.SetBounds(35, 0, 35, 25)
    stop = lcl.NewButton(addrPanel) //设置父组件
    stop.SetParent(addrPanel)
    stop.SetCaption("停止")
    stop.SetBounds(35+35, 0, 35, 25)
    refresh = lcl.NewButton(addrPanel) //设置父组件
    refresh.SetParent(addrPanel)
    refresh.SetCaption("刷新")
    refresh.SetBounds(35+35+35, 0, 35, 25)

    //创建下拉框
    addrBox = lcl.NewComboBox(addrPanel)
    addrBox.SetParent(addrPanel)
    addrBox.SetLeft(35 + 35 + 35 + 35)                                         //这里是设置左边距 上面按钮的宽度
    addrBox.SetWidth(window.Width() - (35 + 35 + 35 + 35 + 35 + 35))           //宽度 减按钮的宽度
    addrBox.SetAnchors(types.NewSet(types.AkLeft, types.AkTop, types.AkRight)) //设置锚点定位,让宽高自动根据窗口调整大小
    addrBox.Items().Add("https://energy.yanghy.cn")
    addrBox.Items().Add("https://www.baidu.com")

    //显示加载进度
    progressLabel = lcl.NewLabel(addrPanel) //设置父组件
    progressLabel.SetParent(addrPanel)
    progressLabel.SetCaption("0")
    progressLabel.SetBounds(window.Width()-(35+35)-10, 3, 35, 25)
    progressLabel.SetAnchors(types.NewSet(types.AkTop, types.AkRight)) //设置锚点定位,让宽高自动根据窗口调整大小

    goUrl := lcl.NewButton(addrPanel) //设置父组件
    goUrl.SetParent(addrPanel)
    goUrl.SetCaption("GO")
    goUrl.SetBounds(window.Width()-35, 0, 35, 25)
    goUrl.SetAnchors(types.NewSet(types.AkTop, types.AkRight)) //设置锚点定位,让宽高自动根据窗口调整大小

    //重新调整browser窗口的Parent属性
    //重新设置了上边距,宽,高
    window.WindowParent().SetAlign(types.AlNone) //重置对齐,默认是整个客户端
    window.WindowParent().SetTop(25)
    window.WindowParent().SetHeight(window.Height() - 25)
    window.WindowParent().SetWidth(window.Width())
    //设置锚点定位,让宽高自动根据窗口调整大小
    //因为窗口大小已调整,这里不能使用 SetAlign 了
    window.WindowParent().SetAnchors(types.NewSet(types.AkTop, types.AkLeft, types.AkRight, types.AkBottom))

    //给按钮增加事件
    goBack.SetOnClick(func(sender lcl.IObject) {
        browserWindow.Chromium().GoBack()
    })
    goForward.SetOnClick(func(sender lcl.IObject) {
        browserWindow.Chromium().GoForward()
    })
    stop.SetOnClick(func(sender lcl.IObject) {
        browserWindow.Chromium().StopLoad()
    })
    refresh.SetOnClick(func(sender lcl.IObject) {
        browserWindow.Chromium().Reload()
    })
    goUrl.SetOnClick(func(sender lcl.IObject) {
        var url = addrBox.Text()
        if url != "" {
            browserWindow.Chromium().LoadUrl(url)
        }
    })
    return
}

go run xx.go

go build

go build -ldflags “-H windowsgui -s -w”

说明

需要你自己下载编译好的框架二进制包,或使用energy命令行工具安装环境,参考教程CEF和Enregy使用
这里面用到了系统UI组件

  1. 首先创建了应用,应用创建属于不管主进程还是渲染进程(子进程)都要执行的代码, 通过NewApplicationConfig配置应用参数,需要你使用(SetFrameworkDirPath)指定框架目录,如果执行文件在框架目录中,则不需要指定.
  2. 创建主窗口,在CEF中主窗口是主进程,UI的主进程,也是brower的主进程,大多数业务逻辑都要在该进程中实现
  3. 在主进程中创建了窗口组件,按钮控制组件,事件监听,窗口属性设置等等.

控制事件

在回调函数BrowserWindow.SetBrowserInit设置事件监听

cef.BrowserWindow.SetBrowserInit(func(event *cef.BrowserEvent, browserWindow *cef.TCefWindowInfo)

设置加载进度改变事件

event.SetOnLoadingProgressChange(func(sender lcl.IObject, browser *cef.ICefBrowser, progress float64)

设置页面加载状态事件

event.SetOnLoadingStateChange(func(sender lcl.IObject, browser *cef.ICefBrowser, isLoading, canGoBack, canGoForward bool)

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,504评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,434评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,089评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,378评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,472评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,506评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,519评论 3 413
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,292评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,738评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,022评论 2 329
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,194评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,873评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,536评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,162评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,413评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,075评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,080评论 2 352

推荐阅读更多精彩内容