Gox语言中内置了著名的gonum/plot包,它的作用与Python中的Matplotlib库类似,可以辅助进行科学计算中的图表绘制,非常方便。该包更详细的文档可以参考这里。
基本折线图绘制
下面是绘制一个最基本的折线图的例子。
<code>// 导入plot包
plot = import("plot")
// 创建一个绘制上下文环境
p, _ = plot.New()
// 设置图表的抬头标题
p.Title.Text = "Gonum Plot Example"
// 设置图标X、Y坐标的文字
p.X.Label.Text = "X"
p.Y.Label.Text = "Y"
// 创建一组准备绘制的坐标点
points = make(plot.XYs)
// 创建并为第一个点赋值X、Y坐标
points[0] = make(plot.XY)
points[0].X = 0.0
points[0].Y = 0.0
// 采用简便的方法创建第2、3、4个点
points[1] = plot.NewXY(1.0, 1.0)
points[2] = plot.NewXY(2.0, 4.0)
points[3] = plot.NewXY(3.0, 9.0)
// 在图表中加入这些点,并给相应的曲线设定图例的名字
plot.AddLinePoints(p, "y = x * x", points)
// 将折线图保存为4英寸见方大小的图片points.png,
// 需要确保c:\\test目录已经存在
p.Save(4*plot.Inch, 4*plot.Inch, `c:\\test\\points.png`)
/<code>
程序执行后,可以在C盘的test目录下找到points.png图像文件,打开后可以看到绘制出来的图形如下:
可以看到一个简单的折线图已经成功绘制出来了。
将绘制好的图形展示在界面上
下面我们用一个简单的界面将画好的图展示出来:
<code>plot = import("plot")
p, _ = plot.New()
p.Title.Text = "Gonum Plot Example"
p.X.Label.Text = "X"
p.Y.Label.Text = "Y"
points = make(plot.XYs)
points[0] = make(plot.XY)
points[0].X = 0.0
points[0].Y = 0.0
points[1] = plot.NewXY(1.0, 1.0)
points[2] = plot.NewXY(2.0, 4.0)
points[3] = plot.NewXY(3.0, 9.0)
plot.AddLinePoints(p, "y = x * x", points)
p.Save(4*plot.Inch, 4*plot.Inch, `c:\\test\\points.png`)
var gui = import("gui")
func onButtonClick() {
\texit()
}
func loop() {
\tlayoutT = []gui.Widget{
\t\tgui.ImageWithFile(`c:\\test\\points.png`, 300, 300),
\t\tgui.Button("Close", onButtonClick),
\t}
\tgui.SingleWindow("Plot Diagram", layoutT)
}
mainWindow = gui.NewMasterWindow("Plot Diagram", 400, 400, gui.MasterWindowFlagsNotResizable, nil)
gui.LoopWindow(mainWindow, loop)
/<code>
这段代码很好理解,就是将保存的折线图载入到界面上的一个ImageWithFile控件上,效果是这样的:
这样,我们就完全控制了图片生成到展示的全过程。
从内存中加载图片并展示
但上面的例子是以保存的png图像文件作为中转载入图片,如果要直接载入内存中的图片,可以这样。
<code>plot = import("plot")
p, _ = plot.New()
p.Title.Text = "Gonum Plot Example"
p.X.Label.Text = "X"
p.Y.Label.Text = "Y"
points = make(plot.XYs)
points[0] = make(plot.XY)
points[0].X = 0.0
points[0].Y = 0.0
points[1] = plot.NewXY(1.0, 1.0)
points[2] = plot.NewXY(2.0, 4.0)
points[3] = plot.NewXY(3.0, 9.0)
plot.AddLinePoints(p, "y = x * x", points)
image = import("image")
rgbaT, errT = image.LoadPlotImage(p, 4*plot.Inch, 4*plot.Inch)
checkError(errT, nil)
p.Save(4*plot.Inch, 4*plot.Inch, `c:\\test\\points.png`)
var gui = import("gui")
func onButtonClick() {
\texit()
}
textureT = nil
flagT = 0
f = func() {
\tif flagT != 0 {
\t\treturn
\t}
\tflagT = 1
\ttextureT, errT = gui.NewTextureFromRgba(rgbaT)
\tif errT != nil {
\t\tplerr(errT)
\t\treturn
\t}
}
func loop() {
\tgo f()
\tlayoutT = []gui.Widget{
\t\tgui.ImageWithFile(`c:\\test\\points.png`, 300, 300),
\t\tgui.Custom(func() {
\t\t\tif textureT != nil {
\t\t\t\tgui.Image(textureT, 348, 348).Build()
\t\t\t}
\t\t}),
\t\tgui.Button("Close", onButtonClick),
\t}
\tgui.SingleWindow("Plot Diagram", layoutT)
}
mainWindow = gui.NewMasterWindow("Plot Diagram", 400, 750, gui.MasterWindowFlagsNotResizable, nil)
gui.LoopWindow(mainWindow, loop)
/<code>
运行的结果如下:
第二张图片即是直接从内存中加载的图片,注意其尺寸设置与gui.ImageWithFile的有所不同,另外,加载图片所需的NewTextureFromRGBA必须要在线程中启动,这是受imgui所限制的。
用LCL库展示图片
上面同样的功能用LCL库实现图形界面的代码如下,更适合传统的编程思维一些。
<code>plot = import("plot")
p, _ = plot.New()
p.Title.Text = "Gonum Plot Example"
p.X.Label.Text = "X"
p.Y.Label.Text = "Y"
points = make(plot.XYs)
points[0] = make(plot.XY)
points[0].X = 0.0
points[0].Y = 0.0
points[1] = plot.NewXY(1.0, 1.0)
points[2] = plot.NewXY(2.0, 4.0)
points[3] = plot.NewXY(3.0, 9.0)
plot.AddLinePoints(p, "y = x * x", points)
p.Save(4*plot.Inch, 4*plot.Inch, `c:\\test\\points.png`)
lcl = import("lcl")
os = import("os")
errT = lcl.InitLCL()
if errT != nil {
\tplerr(errT)
\texit()
}
application = lcl.GetApplication()
application.Initialize()
application.SetTitle("Calculator with LCL")
application.SetMainFormOnTaskBar(true)
mainForm = application.CreateForm()
mainForm.SetWidth(880)
mainForm.SetHeight(480)
mainForm.SetCaption("Calculator with LCL")
mainForm.SetPosition(lcl.PoScreenCenter)
mainForm.Font().SetSize(11)
img1 = lcl.NewImage(mainForm)
img1.SetBounds(10, 10, 400, 400)
img1.SetParent(mainForm)
img1.Picture().LoadFromFile(`c:\\test\\points.png`)
img1.SetStretch(true)
img1.SetProportional(true)
imagetk = import("imagetk")
itk = imagetk.NewImageTK()
bufT, errT = itk.LoadPlotImageInMemory(p, 4*plot.Inch, 4*plot.Inch, "png")
checkError(errT, nil)
mem = lcl.NewMemoryStream()
mem.Write(bufT.Bytes())
mem.SetPosition(0)
img2 = lcl.NewImage(mainForm)
img2.SetBounds(420, 10, 400, 400)
img2.SetParent(mainForm)
img2.Picture().LoadFromStream(mem)
img2.SetStretch(true)
img2.SetProportional(true)
mem.Free()
onButtonCloseClick = func(sender) {
\tapplication.Terminate()
}
buttonClose = lcl.NewButton(mainForm)
buttonClose.SetParent(mainForm)
buttonClose.SetLeft(10)
buttonClose.SetTop(420)
buttonClose.SetCaption("Close")
buttonClose.SetOnClick(&onButtonCloseClick)
application.Run()
/<code>
执行后的结果界面如下:
效果还是不错的,其中,左侧的图是通过图像文件进行中转显示的,右侧的图是直接通过内存中缓冲区中转显示的。
代码中用到了imagetk库,这是封装了一些便捷的图形处理相关的函数。
需要特别注意的是,由于LCL受VCL的影响发展而来,所以所有的对象理论上都应该释放,调用该对象的Free成员函数即可。之前很多的例子为了演示方便,并没有都进行资源释放。
閱讀更多 陸滿庭 的文章