Gox语言中用代码绘图显示在图形界面中并让它实现自动旋转效果-GX39

结合之前文章中的例子,我们这次来用代码绘制一个图形,然后显示在用Sciter制作的GUI图形界面中,并且让它自动旋转。

下面的例子中的信息量很大,除了之前的使用gg包进行代码绘图,还演示了纯用Gox代码来操控HTML中的DOM元素(完全不使用Sciter中的TiScript),包括手动创建元素、选择元素和设置其属性等,并通过修改图片标签的transform样式属性,结合Go语言中的协程(类似线程)实现图形不断旋转的效果。

先来看看最终实现的效果:

准备要做的界面就是这样的,简简单单,最上面是一个div,其中一个span显示红字,下面就是一个img标签装载的图形。程序运行后,图形将以中心为轴每个1秒中顺时针旋转45度。

下面就是实现该效果的完整代码:

// 设置image/color包的简称
// github.com/fogleman/gg包已经默认简称gg
color = image_color

// 建立imagetk包的引用,注意该包的用法比较特殊
itk = imagetk.NewImageTK()

// 建立一个绘图环境,图片大小为 600×400 像素
contextT = gg.NewContext(600, 400)

// 用十六进制方式设置当前颜色
contextT.SetHexColor("FFFFFF")

// 用当前颜色来清空整个图片
contextT.Clear()

// 设置线宽为 2 个像素
contextT.SetLineWidth(2)

// 设置RGB颜色,每种颜色的数值范围是 0.0 - 1.0
contextT.SetRGB(0.0, 0.0, 1.0)

// 画一个圆,以坐标为 (200, 200) 的点为圆心,半径为 50像素
contextT.DrawCircle(250, 250, 60)

// 真正进行线条绘制,并且保留绘图路径以便后面填充颜色
contextT.StrokePreserve()

// 创建一个渐变色填充方案 gradientT
// 是两点之间的圆环式渐变色
gradientT = gg.NewRadialGradient(250, 250, 0, 250, 250, 80)

// 创建一个颜色的基本方法
colorT = new(color.RGBA)
colorT.R = uint8(0)
colorT.G = uint8(0)
colorT.B = uint8(0)
colorT.A = uint8(255)

gradientT.AddColorStop(0, colorT)

// 注意创建颜色的快捷方法
gradientT.AddColorStop(1, itk.NewRGBA(0, 0, 255, 255))

// 设置用渐变色 gradientT 填充
contextT.SetFillStyle(gradientT)

// 真正进行填充,并清除绘画路径,因为后面已经不需要了
contextT.Fill()

// 开始用填充的方式绘制一个矩形

// 用RGBA色彩体系设置当前颜色
// 用 0 - 255 的数值范围表示颜色和透明度
// 设置一定的透明度以便显示出被覆盖住的图形(圆形)
contextT.SetRGBA255(0, 255, 0, 180)

// 绘制一个左上角点坐标为 (300, 200),宽 100,高 100的矩形
contextT.DrawRectangle(300, 200, 100, 100)

// 仅填充颜色不绘制边线
contextT.Fill()

// 开始用路径法画一个三角形

// 用十六进制字符串设置颜色
// 可以加上前面的“#”号,也可以后面加上一个字节表示透明度
contextT.SetHexColor("#FF000099")

// 将虚拟的画笔移动到坐标为 (300, 120) 的点
contextT.MoveTo(300, 120)

// 从当前点绘制一条线到坐标为 (240, 230)的点为止
// 当前点将随之移动到 (240, 230)
contextT.LineTo(240, 230)

// 继续绘制一条直线到 (360, 230)
contextT.LineTo(360, 230)

// 继续绘制一条直线到 (300, 120)
contextT.LineTo(300, 120)

// 创建一个新的线性渐变色
gradientT = gg.NewLinearGradient(240, 120, 360, 230)

// 注意从16进制数字创建颜色的方法
gradientT.AddColorStop(0, itk.NewRGBAFromHex("#00FF00FF"))
gradientT.AddColorStop(1, itk.NewRGBAFromHex("0000FFFF"))
gradientT.AddColorStop(0.5, itk.NewRGBAFromHex("FF0000"))

// 渐变色也可以用于线条颜色的渐变
contextT.SetStrokeStyle(gradientT)

// 设置粗一点的线宽以体现渐变色
contextT.SetLineWidth(10)

// 真正绘制三角形
contextT.Stroke()

// 从gg的画布获取Go语言中的image.Image对象
// 注意 := 和 = 作为赋值用,在Gox语言中是等价的
img := contextT.Image()


// 创建一个字节缓冲区,用于存放图片的二进制数据
bufT  = new(bytes.Buffer)

// 将生成的图片以PNG格式编码到缓冲区中
image_png.Encode(bufT, img)

// 获取字节缓冲区的字节数组([]byte)类型引用
dataT =  bufT.Bytes()

// 设置github.com/sciter-sdk/go-sciter及其子包window的简称
sciter = github_scitersdk_gosciter
window = github_scitersdk_gosciter_window

// 将图片的字节数组数据转换为BASE64编码的字符串
base64StrT := tk.EncodeToBase64(dataT)

// 设置Sciter格式的HTML网页字符串
// 此处非常简单,就是一个网页框架
// 后续DOM操作将由Gox代码来进行
// 注意,为了正确在Sciter中显示中文,最好加上两种meta定义utf-8编码
htmlT := `
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
</head>
<body>
</body>
</html>

`

// 锁定GUI线程,理论上GUI编程都要求在同一线程中更新界面元素
runtime.LockOSThread()

// 创建Sciter窗口,并指定窗口区域位置及大小
w, err := window.New(sciter.DefaultWindowCreateFlag, sciter.NewRect(0, 0, 800, 600))

checkError(err)

// 设置该窗口的一些属性
w.SetOption(sciter.SCITER_SET_SCRIPT_RUNTIME_FEATURES, sciter.ALLOW_FILE_IO | sciter.ALLOW_SOCKET_IO | sciter.ALLOW_EVAL | sciter.ALLOW_SYSINFO)

// 载入HTML页面框架
// 第二个参数是基URL(base url),我们一般不用
w.LoadHtml(htmlT, "")

// 设置窗口标题,注意是中文的
w.SetTitle("图片转向")

// 获取Sciter页面的根元素
rootT, _ := w.GetRootElement()

// 获取body元素
bodyT, _ := rootT.SelectFirst("body")

// 创建一个div,其中文本为空(第二个参数)
divT, _ := sciter.CreateElement("div", "")

// 在body元素中插入div,第二个参数是插入的位置
_ := bodyT.Insert(divT, 0)

// 设置div的innerHTML
// 第二个标志SIH_REPLACE_CONTENT表示替换innerHTML的全部
// 还有可用的标志位有SIH_INSERT_AT_START、SIH_APPEND_AFTER_LAST
// SOH_REPLACE、SOH_INSERT_BEFORE、SOH_INSERT_AFTER,看名字即可知道大意
divT.SetHtml(`<span id="titleTextID">演示图片</span>`, sciter.SIH_REPLACE_CONTENT)

// 设置div的id属性
divT.SetAttr("id", "mainDivID")

// 设置div的style属性(即CSS)
divT.SetStyle("margin-bottom", "10px")

// 获取我们之前定义的span标签
// 这是在设置div的HTML中用文本定义的
// 注意选择器的快捷写法
spanT, _ := bodyT.SelectFirst("span#titleTextID")

// 设置文字颜色为红色
spanT.SetStyle("color", "#ff0000")


// 再创建一个img元素准备装载图片
imgT, _ := sciter.CreateElement("img", "")

// 在body中插入元素,序号为1,也就是第二个元素,即在前面的div之后
_ := bodyT.Insert(imgT, 1)

// 设置该元素的src属性为BASE64格式的图片
imgT.SetAttr("src", `data:image/png;base64,`+base64StrT)

// 设置图片初始的旋转角度为0,即不旋转
imgT.SetStyle("transform", "rotate(0deg)")

// 设置旋转的旋转轴为图片中心点
imgT.SetStyle("transform-origin", "50% 50%")

// 输出目前的根节点的HTML文本以供参考
// 第二个参数为true表示是outerHTML,否则是innerHTML
plv(rootT.Html(true))

// 启动一个goroutine协程,负责图片旋转
// 由于后面的w.Run是阻塞的,所以必须要用线程来处理旋转
// 旋转的方法是不断修改img元素的transform样式的角度值
// 每次顺时针旋转45度
go func() {
    angleT := 0

    for i= 0; i< 20; i ++ {
        // 旋转20次,每次间隔1秒
        tk.SleepSeconds(1)

        angleT += 45

        imgT.SetStyle("transform", fmt.Sprintf("rotate(%ddeg)", angleT))
    }
}()


// 显示窗口并运行GUI图形界面
w.Show()
w.Run()


代码中依然有非常详尽的注释,实现思路也非常清晰。附加几点说明:

  • 为代码清晰,一些异常情况还是没有处理;
  • 输出的结果HTML中,一些属性可能会没有;
  • 旋转为20次,通过循环控制,可以设置成无限
  • 注意,Gox制作的图形界面程序,如果Windows上不想出现CMD的黑框,可以下载Goxg版本,或者在自行编译时,加上ldflags="-H windowsgui"参数。

注意:

  • Gox语言是脱胎于Go语言(Golang)的开源脚本语言,解释执行,但相比Go语言更贴近高级语言,语法硬性限制也少一些;是一门偏向快速应用的语言,也可以说是一个集成工具;

  • Gox语言主要优势有三点:

    • 第一,Gox语言本身只有一个可执行文件,绿色免配置,下载即可使用,无需安装Go语言环境,无需编译,非常适合快速制作原型以及云服务器上的远程开发;
    • 第二,Gox中可以直接使用绝大多数Go语言标准库中的对象和方法函数,也内置了很多常用、优秀的第三方库,充分发挥Go语言多年积累的资源优势;
    • 第三,与很多其他主流语言不同,Gox语言着力解决了GUI图形界面编程的问题,内置了基于Giu(imgui)、LCL、Sciter的三套图形界面编程库,直接可以进行快捷高效的图形界面开发(LCL、Sciter只需分别下载一个动态链接库文件,执行和分发时附带上即可),特别适合编写演示原型系统。

作为脚本语言,Gox语言性能肯定不如Go语言这样的编译型语言快,但由于Gox语言与Go语言的紧密联系,Gox语言编写的脚本可以很容易的改写成Go语言代码,编译执行后就可以发挥Go语言的速度优势了。因此,Gox语言也比较适合做初期的Go语言调试,还有一个更直接的方式是使用Gotx(在Gox官网上也有下载),这是使用完全和Go语言一样语法的解释器,可以理解成集成了Go语言标准库和不少第三方库的解释执行的Go语言,一样也不需要搭建Go语言环境。Gotx与Gox的区别在于,Gotx仍然遵循Go语言的文法,代码相对复杂一些,限制也多一些,但改写回Go语言准备编译执行时,基本上没有成本。

Gox的官网在这里,也可以在浏览器搜索引擎中直接搜索“gox语言”,Github页面在这里,在这里可以看到很多Gox语言的学习指南和实际应用实例。

推荐阅读更多精彩内容