使用Go和Goey构建桌面应用程序的教程

2020-05-18 00:54:17

本教程展示了如何构建一个简单的Goey应用程序,并介绍了一个对Goey应用程序有用的反应式架构。需要明确的是,该体系结构并不是完全被动的,但确实使用了单向数据流,从而简化了GUI应用程序的编写。对于此体系结构,有4个必需的功能。

关于GUI,主要功能有一个职责,那就是启动GUI的事件循环。事件循环在所支持的本机GUIAPI中是常见的,但显然细节有所不同。包循环负责抽象这些差异。对于我们的简单程序,只需要该包中的一个函数loop.Run。此函数将初始化事件循环,并保持该循环运行,直到没有更多的顶级窗口。

在事件循环开始之前,需要创建顶级窗口,但这会使问题复杂化。不同平台的细节有所不同,但是没有一个主要的桌面GUI API是线程安全的。这意味着任何GUI对象(如Windows)都应该在与事件循环相同的操作系统线程上创建。循环不是让调用者来协调线程,而是采用回调来处理GUI的初始化。

如果错误处理得当,上面列出的两个要求将导致函数main的以下定义。

func main(){err:=循环。如果err!=nil{fmt,则运行(CreateWindow)。Fprintf(操作系统。标准,";错误:%s\n";,错误)操作系统。退出(1)}}。

虽然窗口中的布局和控件使用声明性方法,但必须创建顶级窗口。窗口是使用goey.NewWindow创建的,并且可以使用方法更新属性。对于这个简单的示例,只需要设置窗口上的两个属性,它们是在创建窗口时设置的。这意味着单个调用足以创建和初始化窗口。

第二个参数设置窗口的内容。我们可以在这里提供必要的数据,但程序还需要在其他地方生成(并重新生成)窗口的内容。为避免重复,该代码位于main.ender中。

还有另一个重要的细节。该函数将*goey.Window复制到全局变量中。全局状态通常不好,但main.mainWindow的值在初始化后不会更改。当我们对事件做出反应时,需要此值来更新窗口的内容。

函数createWindow()错误{mw,err:=goey。NewWindow(";one Button";,Render())if err!=nil{return err}mainWindow=mw return nil}。

这是一个小帮助器函数,它使用main.ender重新计算窗口的内容,然后更新窗口的内容。请注意,对SetChild的调用是Goey魔力发生的地方,也是Goey具有声明性的原因。软件包将协调数据定义的内容和依赖于平台的控件之间的任何差异,以便窗口的内容匹配。调用方不需要添加、修改或删除任何控件。

func updateWindow(){err:=mainWindow。如果err!=nil{fmt,则SetChild(Render())。Fprintf(操作系统。标准,";错误:%s\n";,Err)}}。

请注意,此函数只能在同时运行GUI事件循环的线程上调用。否则,将并发访问GUI API,这可能会导致崩溃。如果需要从任意Goroutine调用此函数,请使用loop.Do。

此函数返回定义顶级窗口所需布局和内容的数据结构。理想情况下,所有影响内容的状态都将作为参数传递,但在这个简单的示例中,状态的一小部分存储在全局变量clickCount中。

根据clickCount的值,按钮的标签将会更改。否则,布局和内容是静态的。这里不会详细介绍布局,只是说布局依赖于简单的、可组合的小部件。请查看Goey包中可用的小部件,以了解可用的布局选项。

GUI定义的声明性质不仅包括外观,还包括事件的回调。在本例中,请注意该按钮有一个名为onClick的事件。在这种情况下,事件处理程序是使用函数文字内联定义的。作为对事件的响应,程序可以更新其状态,但请注意,它不会直接修改任何控件。相反,我们调用updateWindow。这将再次调用Render以确定当前布局和内容。

函数Render()base。小组件{text:=";单击我!";如果单击Count>;0{text=text+";(";+strconv.。Itoa(ClickCount)+";)";}退货&;goey。填充{插图:Goey。DefaultInsets(),Child:&;Goey。Align{Child:&;Goey。按钮{text:text,onclick:func(){clickCount++updateWindow()}},},}}

事件的回调在与GUI事件循环相同的OS线程上执行。因此,在回调中创建新窗口和修改这些窗口是安全的。相反,长时间运行的代码会导致GUI冻结,因此任何可能需要相当长时间的操作都应该移到另一个Goroutine。当长时间运行的操作完成后,可以通过调用loop.Do安排GUI的更新。

请注意,此函数使用全局变量表示程序的状态。如果该状态作为参数传递,那么GUI布局和控件就可以正常工作,并且可以很容易地进行测试。

总而言之,一旦添加了导入和注释,完整的应用程序就是76行。对main.ender稍作修改,使其具有功能,就可以使GUI的很大一部分内容可测试。可以在存储库中查看完整的源文件。如果感兴趣,请查看其他示例。