Tutorial on building a desktop app with Go and Goey

栏目: IT技术 · 发布时间: 4年前

内容简介:This tutorial shows how to build a simple Goey application, and introduces a reactive architecture useful for Goey applications. To be clear, the architecture is not fully reactive, but does use a unidirectional flow for data that simplifies writing GUI ap

Tutorial: Hello, World!

This tutorial shows how to build a simple Goey application, and introduces a reactive architecture useful for Goey applications. To be clear, the architecture is not fully reactive, but does use a unidirectional flow for data that simplifies writing GUI applications. For this architecture, there are 4 required functions.

Note: For brevity, comments have been stripped from the code samples.

Note: If interested in thefull source, the program exists in the examples directory.

func main

With respect to the GUI, the main function has one responsibility, which is to start the GUI's event loop. Event loops are common across the native GUI APIs supported, but obviously the details differ. The package loop is responsible for abstracting away those differences. For our simple program, only one function from that package is required, loop.Run . This function will initialize the event loop, and keep that loop running until there are no more top-level windows.

Before the event loop starts, the top-level window needs to be created, but there is a complication. Details differ between platforms, but none of the major desktop GUI APIs are thread-safe. This means that any GUI objects, such as windows, ought to be created on the same OS thread as the event loop. Rather than leave it to the caller to coordinate the threading, loop instead takes a callback to handle initializing the GUI.

With proper error handling, the two requirements listed above leads to the following definition for the function main .

func main() {
    err := loop.Run(createWindow)
    if err != nil {
        fmt.Fprintf(os.Stderr, "error: %s\n", err)
        os.Exit(1)
    }
}

func createWindow

While the layout and controls within a window use a declarative approach, top-level windows are created imperatively. Windows are created using goey.NewWindow , and properties can be updated using methods. For this simple example, only two properties on the window need to be set, and they are set when the window is created. This means that a single call is sufficient to create and initialize the window.

The first parameter sets a title for the window.

The second parameter sets the contents of the window. We could provide the necessary data here, but the program will also need to generate (and regenerate) the window's contents elsewhere. To avoid duplication, that code is located in main.render .

There is one other important detail. The function copies the *goey.Window into a global variable. Global state is generally bad, but the value of main.mainWindow will not change once initialized. This value is required to update the window's contents when we react to events.

func createWindow() error {
    mw, err := goey.NewWindow("One Button", render())
    if err != nil {
        return err
    }
    mainWindow = mw

    return nil
}

func updateWindow

This is a small helper function that recalculates the contents of the window, using main.render , and then updates the contents of the window. Note that the call to SetChild is where the magic of goey happens, and what makes goey declarative. The package will reconcile any differences between the data-defined contents, and the platform-dependent controls, so that the window's contents match. There is no need for the caller to add, modify, or delete any controls.

func updateWindow() {
    err := mainWindow.SetChild(render())
    if err != nil {
        fmt.Fprintf(os.Stderr, "error: %s\n", err)
    }
}

Beware, this function can only be called on the thread that is also running the GUI event loop. Otherwise, there will be concurrent access to the GUI API, which will likely cause a crash. If you need to call this function from an arbitrary goroutine, please use loop.Do .

func render

This function returns a data structure that defines the desired layout and contents of the top-level window. Ideally, all state that affects the contents would be passed as an argument, but in this simple example, the small bit of state is stored in a global variable, clickCount .

Based on the value of clickCount , the button's label will change. Otherwise, the layout and contents are static. Details on layout will not be covered here, except to say that layout depends on simple, composable widgets. Please look at the widgets available in the package goey to see what layout options are available.

The declarative nature of the GUI definition does not just cover the appearance, it also covers callbacks for events. In this case, note that the button has an event called OnClick . In this case, the event handler is defined inline using a function literal. In response to events, the program can update its state, but note that it does not directly modify any controls. Instead, we call updateWindow . This will again call into render to determine the current layout and contents.

func render() base.Widget {
    text := "Click me!"
    if clickCount > 0 {
        text = text + "  (" + strconv.Itoa(clickCount) + ")"
    }

    return &goey.Padding{
        Insets: goey.DefaultInsets(),
        Child: &goey.Align{
            Child: &goey.Button{Text: text, OnClick: func() {
                clickCount++
                updateWindow()
            }},
        },
    }
}

The callbacks for events are executed on the same OS thread as the GUI event loop. It is therefore safe to create new windows, and modify those windows, within callbacks. Conversely, and long-running code will cause the GUI freeze, so any operations that may take appreciable time should be moved to another goroutine. When a long-running operation has completed, and update of the GUI can be arrange by calling loop.Do .

Note that this function uses a global variable for the program's state. If that state was passed as an argument, then the GUI layout and controls would be functional, and could easily be tested.

Summary

All told, once imports and comments are added, the complete application is 76 lines. A small modification to main.render to make it functional would make a large part of the GUI testable. Thecomplete source file can be viewed in the repository. If interested, please checkoutthe other examples.

Tutorial on building a desktop app with Go and Goey


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

绝对价值

绝对价值

[美] 伊塔马尔·西蒙森 艾曼纽·罗森 / 钱峰 / 中国友谊出版公司 / 2014-7 / 45.00元

绝对价值指的是经用户体验的产品质量,即使用某件产品或者享受某项服务的切实感受。 过去,消费就像是押宝。一件商品好不好,一家餐馆的环境如何,没有亲身体验过消费者无从得知,只能根据营销人员提供的有限信息去猜测。品牌、原产地、价位、广告,这些重要的质量线索左右着消费者的选择。 然而,互联网和新兴科技以一种前所未有的速度改变了商业环境。当消费者可以在购买前查看到交易记录和消费者评价,通过便捷的......一起来看看 《绝对价值》 这本书的介绍吧!

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具