本文介绍: Go 语言生态中,GUI 一直是短板,更别说跨平台的 GUI 了。fyne向前迈了一大步。fyne 是 Go 语言编写跨平台的 UI 库,它可以很方便地移植手机设备上。fyne使用上非常简单,同时它还提供fyne命令打包静态资源应用程序我们简单介绍基本控件布局然后介绍如何发布一个fyne应用程序

Canvas and CanvasObject 画布画布对象

In Fyne a is the area which an application is drawn within. Each window has a canvas which you can access witbut usually you will find functions on that avoid accessing the canvas.Canvas Window.Canvas() Window

fynecanvas一个应用程序绘画区域,每一个窗口都有一个可以通过Window.Canvas() 访问canvas 但是通常你都会使用其他函数从而避免直接访问这个canvas

Everything that can be drawn in Fyne is a type of . The example here opens a new window and then shows different types of primitive graphical element by setting the content of the window canvas. There are many ways that each type of object can be customised as shown with the text and circle examples.CanvasObject

fyne中每一个可以绘制的都属于CanvasObject 类型这里实例打开一个窗口然后通过设置窗口canvas的内容显示不同的类型图形事件元素。像文本和圆形例子他们通过很多种方法自定义每种类型对象

As well as changing the content shown using it is possible to change the content that is currently visible. If, for example, you change the of a rectangle you can request a refresh of this existing component using .Canvas.SetContent() FillColour rect.Refresh()

除了使用Canvas.SetContent()修改显示的内容外也可以修改当前可见的东西 例如修改  fillcolour 矩形的颜色,你可以使用rect.Refresh()刷新这个组件

package main


import (

   "fyne.io/fyne/v2"

   "fyne.io/fyne/v2/app"

   "fyne.io/fyne/v2/canvas"

   "image/color"

   "time"

)


func main() {

   myApp := app.New()

   myWindow := myApp.NewWindow("Canvas")

   myCanvas := myWindow.Canvas()


   blue := color.NRGBA{R: 0, G: 0, B: 180, A: 255}

   rect := canvas.NewRectangle(blue)

   myCanvas.SetContent(rect)


   go func() {

      time.Sleep(time.Second)

      green := color.NRGBA{R: 0, G: 180, B: 0, A: 255}

      rect.FillColor = green

      rect.Refresh()

   }()


   myWindow.Resize(fyne.NewSize(100, 100))

   myWindow.ShowAndRun()

}

运行效果 1秒后变色

 

Widget 部件

A is a special type of canvas object that has interactive elements associated with it. In widgets the logic is separate from the way that it looks (also called the ).fyne.Widget WidgetRenderer

fyne.Widget一个特殊canvas对象,他具有与之关联交互元素,在widget中,逻辑外观是相互分开的,也被叫做(WidgetRenderer

Widgets are also types of and so we can set the content of our window to a single widget. See how we create a new and set it as the content of the window in this example.CanvasObject widget.Entry

widget部件也是一种 CanvasObject 所以我们可以将窗口设置一个单个的widget部件,下面这个例子展示如何创建一个新的widget.Entry并且设置他的内容

package main


import (

"fyne.io/fyne/v2/app"

"fyne.io/fyne/v2/widget"

)


func main() {

myApp := app.New()

myWindow := myApp.NewWindow("Widget")


myWindow.SetContent(widget.NewEntry())

myWindow.ShowAndRun()

}

运行效果

 

Container and Layouts 容器布局

In the previous example we saw how to set a to the content of a , but it is not very useful to only show one visual element. To show more than one item we use the type.CanvasObject Canvas Container

前面实例中,我们看到如果设置CanvasObject的canvas内容,但是显示一个视觉元素这些并不是很有用,为了显示更多的东西我们使用container 对象

As the also is a fyne.Container, we can set it to be the content of a fyne.CanvasObject. In this example we create 3 text objects and then place them in a container using the function. As there is no layout set we can move the elements around like you see with .fyne.Container fyne.CanvasObject fyne.Canvas container.NewWithoutLayout() text2.Move()

由于fyne.container 也是一个fyne.canvasobject,我们就可以将其设置为fyne.canvas的内容,在本例中我们创建3个文本对象然后将他们放置在NewWithoutLayout() 函数创建的容器中。他们没有布局我们可以用text.move()移动元素。就像你看到的那样。

package main


import (

"image/color"


"fyne.io/fyne/v2"

"fyne.io/fyne/v2/app"

"fyne.io/fyne/v2/canvas"

"fyne.io/fyne/v2/container"

//"fyne.io/fyne/v2/layout"

)


func main() {

myApp := app.New()

myWindow := myApp.NewWindow("Container")

green := color.NRGBA{R: 0, G: 180, B: 0, A: 255}


text1 := canvas.NewText("Hello", green)

text2 := canvas.NewText("There", green)

text2.Move(fyne.NewPos(20, 20))

content := container.NewWithoutLayout(text1, text2)

// content := container.New(layout.NewGridLayout(2), text1, text2)


myWindow.SetContent(content)

myWindow.ShowAndRun()

}

运行结果

 

implements a method for organising items within a container. By uncommenting the line in this example you alter the container to use a grid layout with 2 columns. Run this code and try resizing the window to see how the layout automatically configures the contents of the window. Notice also that the manual position of is ignored by the layout code.fyne.Layout container.New() text2

fyne.layout 实现了一种在容器内部组织项目方法通过取消注释container.New()示例中的行,您可以更改容器以使用具有 2 列的网格布局运行代码尝试调整窗口大小查看布局如何自动配置窗口内容。另请注意,text2 布局代码忽略手动位置

To see more you can check  out the Layout list.

更多信息详见layout list

Widget List 部件列表

Standard Widgets (in widget package) 标准部件

Accordion 手风琴

Accordion displays a list of AccordionItems. Each item is represented by a button that reveals a detailed view when tapped.

Accordion 显示 AccordionItems 列表每个项目都由一个按钮表示点击按钮会显示详细视图

 

Button 按钮

Button widget has a text label and icon, both are optional.

按钮部件文本图标,两者都是可选的

 

Card 卡片

Card widget groups elements with a header and subheader, all are optional.

卡片部件使用标题和子标题元素进行分组,所有都是可选的

 

Check 复选框

Check widget has a text label and a checked (or unchecked) icon.

检查部件有一个文本标签和一个选中(未选中)的图标

 

Entry 输入框

Entry widget allows simple text to be input when focused.

输入框部件允许在获得焦点是输入简单的文本

PasswordEntry  密码输入框

widget hides text input and adds a button to display the text.

密码输入隐藏输入文本,并且添加一按钮来显示文本

 

FileIcon 文件图标

FileIcon provides helpful standard icons for various types of file. It displays the type of file as an indicator icon and shows the extension of the file type.

文件图标 控件 为各类文件提供有用的标准图标,他将文件类型显示为指示器图标显示文件类型扩展名。

 

Form 表单控件

Form widget is two column grid where each row has a label and a widget (usually an input). The last row of the grid will contain the appropriate form control buttons if any should be shown.

表单控件时两列的网格,其中每一行都有一个标签和一个小部件(通常是一个输入最后一行将包含适当的表单控制按钮

 

Hyperlink 超链接

Hyperlink widget is a text component with appropriate padding and layout. When clicked, the URL opens in your default web browser.

超链接控件具有适当填充和布局的文本组件单击后,url 将在你默认web 浏览器打开

 

Icon 图标

Icon widget is a basic image component that load’s its resource to match the theme.

图标控件是一个基本图像控件,他加载资源匹配主题

 

Label 标签

Label widget is a label component with appropriate padding and layout.

标签部件 是具有适当的填充和布局的标签组件

 

Progress bar 进度条

ProgressBar widget creates a horizontal panel that indicates progress.

progressbar 创建一个指示进度的水平面板

 

ProgressBarInfinite   无限进度条

widget creates a horizontal panel that indicates waiting indefinitely An infinite progress bar loops 0% -> 100% repeatedly until Stop() is called.

创建一个无限进度条控件一直在0-100 之间反复,直到调用stop() 函数为止

 

RadioGroup 单选

RadioGroup widget has a list of text labels and radio check icons next to each.

单选组 有一个文本标签列表每个旁边的单选检查图标

 

Select 下拉选择

Select widget has a list of options, with the current one shown, and triggers an event function when clicked.

下拉选择栏部件有一个选项列表,显示当前列表,并在单击触发函数

 

SelectEntry 下拉选择框可输入选择

Select entry widget adds an editable component to the select widget. Users can select an option or enter their own value.

 小部件将可编辑组件添加选择框小组件用户可以选择一个值或者输入自己的值

 

Separator 分割线

Separator widget shows a dividing line between other elements.

分割线组件 显示与其他元素之间的分界线

 

Slider 滑块控件

Slider if a widget that can slide between two fixed values.

滑块控件可以在两个固定值之间滑动切换

 

TextGrid 文本网格

TextGrid is a monospaced grid of characters. This is designed to be used by a text editor, code preview or terminal emulator.

文本网格控件是一个等宽的字符网格,旨在提供文本编辑器,代码预览终端仿真器使用

 

Toolbar 工具栏

Toolbar widget creates a horizontal list of tool buttons.

创建工具按钮的水平列表

 

Collection Widgets (in widget package) 集成部件

Collection widgets provide advanced caching functionality to provide high performance rendering of massive data. This does lead to a more complex constructor, but is a good balance for the outcome it enables. Each of these widgets uses a series of callbacks, the minimum set is defined by their constructor function, which includes the data size, the creation of template items that can be re-used and finally the function that applies data to a widget as it is about to be added to the display.

集成控件提供高级缓存功能,以实现海量数据高性能呈现,这导致了很多复杂构造函数,但是对于他可以实现的结果来说是一个很好的平衡,每一个小部件都使用一系列回调函数,最小集合通过他们析构函数构建的包括了数据大小,可重复使用的模板数据应用与部件的函数

List 列表

List provides a high performance vertical scroll of many sub-items.

列表提供子项性能垂直滚动

 

Table 表格

Table provides a high performance scrolled two dimensional display of many sub-items.

表格提供了许多子项的高性能滚动二维显示

 

Tree 树

Tree provides a high performance vertical scroll of items that can be expanded to reveal child elements..

树提供了一个高性能垂直滚动项目,可以展开以显示子元素

 

Container Widgets (in container package) 容器部件(container 包)

Container widgets are like regular containers but they provide some additional functionality.

容器部件类似常规容器 但是他们提供了额外功能

AppTabs 应用选项卡

AppTabs widget allows switching visible content from a list of TabItems. Each item is represented by a button at the top of the widget.

AppTabs 小部件允许从 TabItems 列表切换可见内容。每个项目都由小部件顶部的按钮表示。

 

Scroll 滚动

ScrollContainer defines a container that is smaller than the Content.

ScrollContainer 定义了一个小于 Content 的容器

 

Split 分割

SplitContainer defines a container whose size is split between two children.

 

Layout List 布局列表

Standard Layouts 标准布局

Horizontal Box (HBox) 水平布局

Horizontal Box arranges items in a horizontal row. Every element will have the same height (the height of the tallest item in the container) and objects will be leftaligned at their minimum width.

水平盒子项目排列在水平方向上,每个项目都有相同的高度,(容器中最高的项目) 并且 对象将在器最小宽度处左对齐

 

Vertical Box (VBox) 垂直盒子

Vertical Box arranges items in a vertical column. Every element will have the same width (the width of the widest item in the container) and objects will be topaligned at their minimum height.

垂直盒子将项目排列垂直排列中,每个元素具有相同的宽度,(容器中最宽的宽度),并且对象将在器最小高度顶部对齐

 

Center 居中

Center layout positions all container elements in the center of the container. Every object will be set to it’s minimum size.

居中布局 将 所有容器元素放置在容器中心,每个对象都将设置最小尺寸

 

Form 表单布局

Form layout arranges items in pairs where the first column is at minimum width. This is normally useful for labelling elements in a form, where the label is in the first column and the item it describes is in the second. You should always add an even number of elements to a form layout.

 表单布局,将项目成对排列,其中一列的宽度最小,这对于在表单中标记元素通常是很有用的,其中标签位于第一列,而他的描述项目位于第二列。你应该始终像表单中添加偶数个元素

 

Grid 网格布局

Grid layout arranges items equally in the available space. A number of columns is specified, with objects being positioned horizontally until the number of columns is reached at which point a new row is started. All objects have the same size, that is width divided by column total and the height will be total height divided by the number of rows required. Minus padding.

网格布局可以在空间平均排列项目,指定列数,对象水平放置,直到达到列数,此时换行开始,所有对象具有相同的大小,即宽度除以总列,高度除以总行,减去填充

 

GridWrap 网格环绕

GridWrap layout arranges all items to flow along a row, wrapping to a new row if there is insufficient space. All objects will be set to the same size, which is the size passed to the layout. This layout may not respect item MinSize to manage this uniform layout. Often used in file managers or image thumbnail lists.

GridWrap 布局将所有项目排列成一行,如果空间不足,则换行到新行。所有对象都将设置为相同的大小,即传递给布局的大小。此布局可能不尊重项目 MinSize 来管理统一布局。通常用于文件管理器图像缩略图列表。

 

Border 边框布局

Border layout supports positioning of items at the outside of available space. The border is passed pointers to the objects for (top, left, bottom, right). All items in the container that are not positioned on a border will fill the remaining space.

边框布局支持将项目定位在可用空间之外。边框被传递给(上、左、下、右)对象的指针。容器中定位边框上的所有项目将填充剩余空间

 

Max 最大

Max layout positions all container elements to fill the available space. The objects will all be fullsized and drawn in the order they were added to the container (last-most is on top).

最大布局定位所有容器元素以填充可用空间。这些对象都将是全尺寸的,并按照它们添加到容器中顺序绘制最后一个在顶部)。

 

Padded 填充

Padded layout positions all container elements to fill the available space but with a small padding around the outside. The size of the padding is theme specific. The objects will all be drawn in the order they were added to the container (last-most is on top).

填充布局定位所有容器元素以填充可用空间,但在外部有一个小填充。填充的大小是特定于主题的。所有对象都将按照它们添加到容器中顺序绘制(最后一个在顶部)。

 

Combining Layouts 组合布局

It is possible to build up more complex application structures by using multiple layouts. Multiple containers that each have their own layout can be nested to create complete user interface arrangements using only the standard layouts listed above. For example a horizontal box for a header, a vertical box for a left side file panel and a grid wrap layout in the content area – all inside a container using a border layout can build the result illustrated below.

可以嵌套多个容器,每个容器都有自己的布局,以仅使用上面列出的标准布局创建完整的用户界面排列例如标题的水平框,左侧文件面板垂直框和内容区域中的网格环绕布局 – 使用边框布局的所有容器内都可以构建如下所示结果

 

Dialog List 对话框列表

Standard Dialogs

Standard Dialogs 标准对话框

Color

Allow users to pick a colour from a standard set (or any color in advanced mode).

允许用户标准集(或高级模式下的任何颜色)中选择一种颜色

 

Confirm

Ask for conformation of an action.

询问动作的构象。

 

FileOpen

Present this to ask user to choose a file to use inside the app. The actual dialog displayed will depend on the current operating system.

展示内容以要求用户选择要在应用内使用的文件。实际显示的对话框取决于当前操作系统

 

Form

Get various input elements in a dialog, with validation.

对话框中获取各种输入元素,并进行验证

 

Information

A simple way to present some information to the app user.

一种向应用程序用户呈现一些信息简单方法

 

Custom 通用

Present any content inside a dialog container.

对话框容器中呈现任何内容

 

Adding Shortcuts to an App 向应用程序添加快捷方式

Shortcuts are common tasks that can be triggered by keyboard combinations or context menus. Shortcuts, much like keyboard events, can be attached to a focused element or registered on the Canvas to always be available in a Window.

快捷键是可由键盘组合或上下文菜单触发常见任务快捷键键盘事件非常相似,可以附加到焦点元素或在画布注册,以始终在窗口中可用

Registering with a Canvas 使用canvas 注册

There are many standard shortcuts defined (such as fyne.ShortcutCopy) which are connected to standard keyboard shortcuts and right-click menus. The first step to adding a new Shortcut is to define the shortcut. For most uses this will be a keyboard triggered shortcut, which is a desktop extension. To do this we use desktop.CustomShortcut, for example to use the Tab key and Control modifier you might do the following:

定义了许多标准快捷键例如fyne.ShortcutCopy),它们连接标准键盘快捷键右键菜单。第一步添加Shortcut是定义快捷方式。对于大多数用途,这将是一个键盘触发的快捷方式,它是一个桌面扩展。为此,我们使用desktop.CustomShortcut,例如使用 Tab 键和 Control 修饰符,您可以执行以下操作

ctrlTab    := &desktop.CustomShortcut{KeyName: fyne.KeyTab, Modifier: fyne.KeyModifierControl}

ctrlAltTab := &desktop.CustomShortcut{KeyName: fyne.KeyTab, Modifier: fyne.KeyModifierControl | fyne.KeyModifierAlt}

Notice that this shortcut can be re-used so you could attach it to menus or other items as well. For this example we want it to be always available, so we register it with our window’s Canvas as follows:

请注意,此快捷方式可以重复使用,因此您也可以将其附加到菜单或其他项目。对于此示例我们希望它始终可用,因此我们将其注册到我们的窗口 Canvas 中,如下所示

ctrlTab := &desktop.CustomShortcut{KeyName: fyne.KeyTab, Modifier: fyne.KeyModifierControl}

w.Canvas().AddShortcut(ctrlTab, func(shortcut fyne.Shortcut) {

log.Println("We tapped Ctrl+Tab")

})

w.Canvas().AddShortcut(ctrlAltTab, func(shortcut fyne.Shortcut) {

log.Println("We tapped Ctrl+Alt+Tab")

})

As you can see there are two parts to registering a shortcut in this way – passing the shortcut definition and also a callback function. If the user types the keyboard shortcut then the function will be called and the output printed.

如您所见,以这种方式注册快捷方式有两个部分 – 传递快捷方式定义回调函数。如果用户键入键盘快捷键,则将调用该函数并打印输出

Adding shortcuts to an Entry 输入框(Entry)添加快捷方式

It can also be helpful to have a shortcut apply only when the current item is focused. This approach can be used for any focusable widget, and is managed by extending that widget and adding a TypedShortcut handler. This is much like adding key handlers, except the value passed in will be a fyne.Shortcut.

仅在当前项目获得焦点时应用快捷方式也很有帮助。这种方法用于任何可聚焦的小部件,并通过扩展该小部件并添加

TypedShortcut处理程序管理。这很像添加键处理程序,除了传入的值将是fyne.Shortcut.

type myEntry struct {

widget.Entry

}


func (m *myEntry) TypedShortcut(s fyne.Shortcut) {

if _, ok := s.(*desktop.CustomShortcut); !ok {

m.Entry.TypedShortcut(s)

return

}


log.Println("Shortcut typed:", s)

}

From the excerpt above you can see how a TypedShortcut handler might be implemented. Inside this function you should check whether the shortcut is of the custom type used earlier. If the shortcut is a standard one it’s a good idea to call the original shortcut handler (if the widget had one). With those checks done you can compare the shortcut with the various types you are handling (if there are multiple).

您可以看到如何实现

TypedShortcut处理程序。在此函数中,您应该检查快捷方式是否属于之前使用的自定义类型。如果快捷方式是标准快捷方式,最好调用原始快捷方式处理程序(如果小部件有一个)。完成这些检查后,您可以将快捷方式与您正在处理的各种类型(如果有多个)进行比较

Using the Preferences API  使用[Preferences] API

Storing user configurations and values is a common task for application developers, but implementing it across multiple platforms can be tedious and time-consuming. To make it easier, Fyne has an API for storing values on the filesystem in a clean and understandable way while the complex parts are handled for you.

Lets start with the setup of the API. It is part of the Preferences interface where storage and loading functions exist for values of Bool, Float, Int and String. They each consist of three different functions, one for loading, one loading with a fallback value and lastly, one for storing values. An example of the three functions and their behaviour can be seen below for the String type:

存储用户配置和值是应用程序开发人员的一项常见任务,但跨多个平台实现它可能是乏味且耗时的。为了使它更容易,Fyne 有一个 API 用于文件系统上以一种清晰易懂的方式存储值,同时为您处理复杂的部分。

让我们从 API 的设置开始。它是Preferences接口的一部分,其中存在 Bool、Float、Int 和 String 值的存储和加载函数。它们每个都包含三个不同的功能,一个用于加载,一个用于加载备用值,最后一个用于存储值。下面是 String 类型的三个函数及其行为示例

// String looks up a string value for the key 字符串查找键的字符串值

String(key string) string

// StringWithFallback looks up a string value and returns the given fallback if nofound StringWithFallback

//查找一个字符串值,如果未找到,则返回给定回退

StringWithFallback(key, fallback string) string

// SetString saves a string value for the given key     SetString 为给定保存字符串值

SetString(key string, value string)

These functions can be accessed through the created application variable and calling the Preferences() method on. Please note that it is necessary to create the apps with a unique ID (usually like a reversed url). This means that the application will need to be created using app.NewWithID() to have its own place to store values. It can roughly be used like the example below:

这些函数可以通过创建应用程序变量和调用

Preferences()方法访问。请注意,必须创建具有唯一 ID 的应用程序(通常像反向 url)。这意味着需要使用创建应用程序

app.NewWithID()来拥有自己的位置来存储值。它大致可以像下面的例子一样使用:

a := app.NewWithID("com.example.tutorial.preferences")

[...]

a.Preferences().SetBool("Boolean", true)

number := a.Preferences().IntWithFallback("ApplicationLuckyNumber", 21)

expression := a.Preferences().String("RegularExpression")

[...]

To show this, we are going to build a simple little app that always closes after a set amount of time. This timeout should be user changeable and applied on the next start of the application.

Let us start by creating a variable called timeout that will be used to store time in the form of time.Duration.

var timeout time.Duration

Then we could create a select widget to let the user select the timeout from a couple pre-defined strings and then multiplying the timeout by the number of seconds that the string relates to. Lastly, the “AppTimeout” key is used to set the string value to the selected one.

为了展示这一点,我们将构建一个简单的小应用程序,它总是在设定的时间关闭。此超时应该用户更改的,并在应用程序的下一次启动时应用。

让我们首先创建一个名为的变量,该变量timeout将用于以 的形式存储时间time.Duration

timeoutSelector := widget.NewSelect([]string{"10 seconds", "30 seconds", "1 minute"}, func(selected string) {

    switch selected {

    case "10 seconds":

        timeout = 10 * time.Second

    case "30 seconds":

        timeout = 30 * time.Second

    case "1 minute":

        timeout = time.Minute

    }


    a.Preferences().SetString("AppTimeout", selected)

})

Now we want to grab the set value and if none exists, we want to have a fallback that sets the timeout to the shortest one possible to save the user time when waiting. This can be done by setting the selected value of timeoutSelector to the loaded value or the fallback if that happens to be the case. By doing it this way, the code inside the select widget will run for that specific value.

现在我们想要获取设置的值,如果不存在,我们想要一个后备,将超时设置为尽可能短的时间,以节省用户等待时的时间。这可以通过将 timeoutSelector的选定值设置

为加载的值或在这种情况下的回退来完成。通过这样做,选择小部件内的代码将为该特定值运行

timeoutSelector.SetSelected(a.Preferences().StringWithFallback("AppTimeout", "10 seconds"))

The last part will just be to have a function that starts in a separate goroutine and tells the application to quit after the selected timeout.

最后一部分将只是拥有一个在单独的 goroutine启动的函数,并告诉应用程序在选定的超时后退出

go func() {

    time.Sleep(timeout)

    a.Quit()

}()

In the end, the resulting code should look something like this:

最后,生成代码应如下所示

package main


import (

    "time"


    "fyne.io/fyne/v2/app"

    "fyne.io/fyne/v2/widget"

)


func main() {

    a := app.NewWithID("com.example.tutorial.preferences")

    w := a.NewWindow("Timeout")


    var timeout time.Duration


    timeoutSelector := widget.NewSelect([]string{"10 seconds", "30 seconds", "1 minute"}, func(selected string) {

        switch selected {

        case "10 seconds":

            timeout = 10 * time.Second

        case "30 seconds":

            timeout = 30 * time.Second

        case "1 minute":

            timeout = time.Minute

        }


        a.Preferences().SetString("AppTimeout", selected)

    })


    timeoutSelector.SetSelected(a.Preferences().StringWithFallback("AppTimeout", "10 seconds"))


    go func() {

        time.Sleep(timeout)

        a.Quit()

    }()


    w.SetContent(timeoutSelector)

    w.ShowAndRun()

}

System Tray Menu  系统托盘菜单

Adding a System Tray menu  增加系统托盘

Since the v2.2.0 release Fyne has built in support for a system tray menu. This feature displays an icon on macOS, Windows and Linux computers and when tapped will pop out a menu as specified by the app.

自从fyne 2.2.0版本后就支持创建系统托盘,此功能在macoswindowslinux 中显示为一个图标点击时将弹出应用程序指定菜单

As this is a desktop specific feature we must first do a runtime check that the app is running in desktop mode. To do this, and get a reference to the desktop features, we do a Go type assertion:

由于这是特定与桌面的功能,因此我们必须首先进行运行检查运行程序是否桌面模式下运行,为此,获取对桌面功能的引用,我们执行go 类型断言

if desk, ok := a.(desktop.App); ok {

...

}

If the ok variable is true then we can set up a menu using the standard Fyne menu API that you might have used in Window.SetMainMenu before.

如果 ok 变量为 true,那么我们可以使用标准 Fyne 菜单 API 设置菜单,您之前可能在 Window.SetMainMenu 中使用过该 API。

m := fyne.NewMenu("MyApp",

fyne.NewMenuItem("Show", func() {

log.Println("Tapped show")

}))

desk.SetSystemTrayMenu(m)

With this code added to the setup of your application you can run the app and see that it shows a Fyne icon in the system tray. When you tap it a menu will appear containing “Show” and “Quit”.

The default icon is the Fyne logo, you can either fix this using app metadata or by setting the app icon in App.SetIcon or for system tray directly using desk.SetSystemTrayIcon

将此代码添加到应用程序的设置中后,您可以运行该应用程序,并看到它在系统托盘中显示 Fyne 图标。当您点击它时,将出现一个包含“显示”和“退出”的菜单

默认图标是 Fyne 徽标,您可以使用应用程序数据或通过在 App.SetIcon 中设置应用程序图标来解决问题,或者直接使用桌面用于系统托盘。设置系统托盘图标

Manage window lifecycle

By default a Fyne app will exit when you close all windows and this may not be what you want with a system tray app. To override the behaviour you can use the Window.SetCloseIntercept feature to override what happens when a window is closed. In the example below we hide the window instead of closing it by calling Window.Hide(). Add this before you show the window for the first time.

默认情况下,当您关闭所有窗口时,Fyne 应用程序将退出,这可能不是您想要的系统托盘应用程序。要覆盖该行为,您可以使用 Window.SetCloseIntercept 功能来覆盖窗口关闭时发生的情况。在下面的示例中,我们隐藏窗口,而不是通过调用 Window.Hide() 来关闭它。在首次显示窗口之前添加此项。

w.SetCloseIntercept(func() {

w.Hide()

})

The benefit of hiding a window is that you can simply show it again using Window.Show() which is much more efficient than creating a new window if the same content is needed a second time. We update the menu created earlier to show the window that was hidden above.

隐藏窗口的好处是,您可以简单地使用 Window.Show() 再次显示它,如果第二次需要相同的内容,这比创建新窗口要高效得多。我们更新之前创建的菜单以显示上面隐藏的窗口。

fyne.NewMenuItem("Show", func() {

w.Show()

}))

Complete app 完整app

That’s all there is to setting up a system tray menu with Fyne! The complete code for this tutorial is as follows.

这就是使用 Fyne 设置系统托盘菜单的全部内容!本教程的完整代码如下所示。

package main



import (

"fyne.io/fyne/v2"

"fyne.io/fyne/v2/app"

"fyne.io/fyne/v2/driver/desktop"

"fyne.io/fyne/v2/widget"

)


func main() {

a := app.New()

w := a.NewWindow("SysTray")


if desk, ok := a.(desktop.App); ok {

m := fyne.NewMenu("MyApp",

fyne.NewMenuItem("Show", func() {

w.Show()

}))

desk.SetSystemTrayMenu(m)

}


w.SetContent(widget.NewLabel("Fyne System Tray"))

w.SetCloseIntercept(func() {

w.Hide()

})

w.ShowAndRun()

}

运行结果

 

Data Binding  数据绑定

Data binding was introduced in Fyne v2.0.0 and makes it easier to connect many widgets to a data source that will update over time. the package has many helpful bindings that can manage most standard types that will be used in an application. A data binding can be managed using the binding API (for example ) or it can be connected to an external item of data like ().data/bindingNewStringBindInt(*int)

Fyne v2.0.0 中引入数据绑定,使将许多小部件连接到将随时间更新数据源变得更加容易。该data/binding包有许多有用的绑定,可以管理将在应用程序中使用的大多数标准类型。可以使用绑定 API(例如NewString)来管理数据绑定,也可以将其连接外部数据项,例如 (`BindInt(*int))。

Widgets that support binding typically have a constructor to set up the binding when creating the widget. You can also call and to manage the data of an existing widget. The following example shows how you can manage a data item that is bound to a simple widget.…WithDataBind()Unbind()StringLabel

支持绑定的小部件通常有一个…WithData构造函数来在创建小部件时设置绑定。您还可以调用Bind()和 Unbind()管理现有小部件的数据。以下示例显示了如何管理String绑定到简单Label小部件的数据项。

package main


import (

"time"


"fyne.io/fyne/v2/app"

"fyne.io/fyne/v2/data/binding"

"fyne.io/fyne/v2/widget"

)


func main() {

a := app.New()

w := a.NewWindow("Hello")


str := binding.NewString()

go func() {

dots := "....."

for i := 5; i >= 0; i-- {

str.Set("Count down" + dots[:i])

time.Sleep(time.Second)

}

str.Set("Blasoff!")

}()


w.SetContent(widget.NewLabelWithData(str))

w.ShowAndRun()

}

You can find out more in the data binding section of this site.

 

 

Compile Options 编译选项

Build tags

Fyne will typically configure your application appropriately for the target platform by selecting the driver and configuration. The following build tags are supported and can help in your development. For example if you wish to simulate a mobile application whilst running on a desktop computer you could use the following command:

Fyne 通常会通过选择驱动程序配置来为目标平台适当地配置您的应用程序。支持以下构建标签,它们可以帮助您的开发例如,如果您希望在台式计算机上运行时模拟移动应用程序,您可以使用以下命令:

go runtags mobile main.go

Tag

Description

gles

Force use of embedded OpenGL (GLES) instead of full OpenGL. This is normally controlled by the target device and not normally needed.强制使用嵌入式 OpenGL (GLES) 而不是完整的 OpenGL。这通常由目标设备控制,通常不需要

hints

Display developer hints for improvements or optimisations. Running with

hints

will log when your application does not follow material design or other recommendations.显示改进或优化开发人员提示。当您的应用程序不遵循材料设计或其他建议时,运行

hints记录下来。

mobile

This tag runs an application in a simulated mobile window. Useful when you want to preview your app on a mobile platform without compiling and installing to the device.此标签在模拟移动窗口中运行应用程序。当您想在移动平台上预览您的应用程序而不编译安装设备时很有用。

no_native_menus

This flag is specifically for macOS and indicates that the application should not use the macOS native menus. Instead menus will be displayed inside the application window. Most useful for testing an application on macOS to simulate the behavior on Windows or Linux.此标志专门用于 macOS,表示应用程序不应使用 macOS 原生菜单。相反,菜单将显示在应用程序窗口内。对于在 macOS 上测试应用程序以模拟 Windows 或 Linux 上的行为最有用。

原文地址:https://blog.csdn.net/qq_51768842/article/details/129867536

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_20576.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注