GUI via Fyne: changing elements on app page on button clicking - go

What is the best practice to change elements on an app page at button clicking.
For example, I have such code
package main
import (
"fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/widget"
)
func main() {
a := app.New()
w := a.NewWindow("Hello")
hello := widget.NewLabel("Hello Fyne!")
w.SetContent(container.NewVBox(
hello,
widget.NewButton("Hi!", func() {
// do something
}),
))
w.ShowAndRun()
}
I want to change elements on this window if clicking on a NewButton. And display a new Buttons with different functions at their clicking

If you want to change the content of a container you will want to set the Container to a variable so you can access its methods and fields later to manipulate the content.
content := container.NewVBox(…)
w.SetContent(container)
Then you can use methods on content or change its Objects field then call Refresh().

Related

Create a verbose console inside Golang app using Fyne

Comming from Python with PyQt gui, I was used to add kind of console in my programm. The purpose was to indicate to the user information on the processes in progress, on the execution errors encountered, etc.
In Python/PyQt, I was using QLineEdit to do that. It was pretty easy to use. Just create and insert the widget in my gui and add a row for each information by calling appen().
For example, the console could say "esedb loading" when loading an esedb file, then "esedb file loaded" when finished, then "esedb parsing" for the next step, etc...
Now, I'm learning Golang with Fyne and I'm looking for a way to do something similar.
I found widget.NewTextGrid() but it doesn't work as I expect.
I can't just append new line. If I understand well, I have to store text in a string variable
Could you advice me about the way to do that ?
Thanks!
package main
import (
//"fmt"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/layout"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
)
func main() {
myapp := app.New()
myappGui := myapp.NewWindow("Example")
myappGui.Resize(fyne.NewSize(400, 600))
textConsole := widget.NewTextGrid()
TextGrid is a complex component designed for managing character specific font styles in a monospace arrangement (like a terminal etc).
For performance I would recommend a VBox in a Scroll widget where each line is another appended Label (you can set them to monospace text style as well). If you want the text to be interactive then as other answers have said the NewMultiLineEntry is likely for you.
Text is complex and we are working hard to optimise more of the complex usages and large file handling, so it will get smoother in later releases…
widget.TextGrid does not have a method to append a line, but it does support querying its current content using TextGrid.Text(). So what you may do is set a new text that is its current content and the new line concatenated, e.g.:
textConsole.SetText(textConsole.Text() + "\n" + line)
But know that widget.TextGrid does not support scrolling: its size will be dictated by its string content. You can make it scrollable of course by using a container.Scroll.
For example:
func main() {
myapp := app.New()
w := myapp.NewWindow("Example")
w.Resize(fyne.NewSize(500, 300))
textConsole := widget.NewTextGrid()
scrollPane := container.NewScroll(textConsole)
w.SetContent(scrollPane)
go func() {
for {
textConsole.SetText(textConsole.Text() + time.Now().String() + "\n")
scrollPane.ScrollToBottom()
time.Sleep(time.Second)
}
}()
w.ShowAndRun()
}
Alternatively you may use a multiline widget.Entry. It also supports selecting any part of it, and by default it's also editable. You may disable editing of course. It supports scrolling by default.
See this example:
func main() {
myapp := app.New()
w := myapp.NewWindow("Example")
w.Resize(fyne.NewSize(500, 300))
textConsole := widget.NewMultiLineEntry()
textConsole.Disable() // Disable editing
w.SetContent(textConsole)
go func() {
for {
textConsole.SetText(textConsole.Text + time.Now().String() + "\n")
time.Sleep(time.Second)
}
}()
w.ShowAndRun()
}

How to make a grid / Fyne / Golang

I'm making an application with Fyne.
I need to create a grid where the left column will be fixed and the right column will stretch. In general, there will be a menu on the left, and the main block on the right (a screenshot of the expected one below).
I read the documentation https://developer.fyne.io/container/grid but still don't understand how to do it. Help me please.
Grid
package main
import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/app"
)
func main() {
application := app.New()
window := application.NewWindow("title")
window.Resize(fyne.NewSize(1920, 1080))
window.ShowAndRun()
}
you can check the demo application of Fyne.
$ go get fyne.io/fyne/v2/cmd/fyne_demo
$ fyne_demo
from your description here is an example with menu on the left, and the main block on the right:
package main
import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/widget"
)
func main() {
a := app.New()
w := a.NewWindow("Fyne Demo")
w.SetMaster()
content := container.NewMax()
title := widget.NewLabel("Component name")
intro := widget.NewLabel("An introduction would probably go\nhere, as well as a")
intro.Wrapping = fyne.TextWrapWord
tutorial := container.NewBorder(
container.NewVBox(title, widget.NewSeparator(), intro), nil, nil, nil, content)
split := container.NewHSplit(makeNav(), tutorial)
split.Offset = 0
w.SetContent(split)
w.Resize(fyne.NewSize(640, 460))
w.ShowAndRun()
}
func makeNav() fyne.CanvasObject {
tree := widget.NewTreeWithStrings(menuItems)
return container.NewBorder(nil, nil, nil, nil, tree)
}
var menuItems = map[string][]string{
"": {"welcome", "collections", "advanced"},
"collections": {"list", "table"},
}
output :
if you explore their demo on your go path source you can see the full function of (makeNav) that will make things clickable.
and to make (the left column will be fixed and the right column will stretch):
split.Offset = 0
It sounds like you are making a Border layout (something attached to one edge). If so then you can set the left to be minimum size and the content stretch to fill by doing:
container.NewBorder(nil, nil, left, nil, content)
(the parameters are top, bottom, left, right, middle).
If you want the user to control the split then do as suggested elsewhere and user container.NewHSplit(left, right).

go and Fyne - Trying to create a single window that shows two different sets of information depending on button pressed

I have the following code which will not compile which shows two different sets of content depending on which button is pressed. The objective is to change content programatically based on buttons selected (in the same window).
The compile error is at line 19 and 27 saying content1 and content2 are undefined.
Problem Solution is more important than fixing compile error!
package main
import (
"fmt"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/widget"
)
func main() {
myApp := app.New()
myWindow := myApp.NewWindow("Hello")
myWindow.Resize(fyne.NewSize(500, 600))
label1 := widget.NewLabel("You are in content 1")
button1 := widget.NewButton("Button 1",
func() {
fmt.Println("tapped button1")
myWindow.SetContent(content2)
myWindow.Show()
})
label2 := widget.NewLabel("You are in content 2")
button2 := widget.NewButton("Button 2",
func() {
fmt.Println("tapped button2")
myWindow.SetContent(content1)
myWindow.Show()
})
content1 := container.NewVBox(
label1,
button1,
)
content2 := container.NewVBox(
label2,
button2,
)
myWindow.SetContent(content1)
myWindow.ShowAndRun()
}
You can’t reference a variable before it is defined.
Try using var content1 fyne.CanvasObject (for example) before you reference it and changing content1 := to content1 = so you update its value once you know it.

ellipsis expansion fyne NewVBox

I'm trying to create a Fyne vertical box with a series of buttons but can't figure out the basic mechanism. I think this is a Go question, not a Fyne question, something I don't understand about go.
Here is a minimal program to show what I mean:
package main
import (
"fmt"
"fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/widget"
)
func main() {
a := app.New()
w := a.NewWindow("Button List")
btn0 := widget.NewButton("button 0", func() {
fmt.Println("Pressed 0")
})
btn1 := widget.NewButton("button 1", func() {
fmt.Println("Pressed 1")
})
btns := []*widget.Button{btn0, btn1}
vbox := container.NewVBox(
// does work
btns[0],
btns[1],
// doesn't work
// btns...,
)
w.SetContent(
vbox,
)
w.ShowAndRun()
}
My understanding is that the argument btns... should produce the same effect as the list of arguments btn[0], btn[1], but it apparently doesn't. If I comment out the lines
btn[0],
btn[1],
and uncomment the line
btns...
I get the error message
cannot use btns (type []*"fyne.io/fyne/v2/widget".Button) as type
[]fyne.CanvasObject in argument to container.NewVBox
So, my newbie questions:
whats going on here, i.e., why doesn't btns... work?
what should I be using as the argument to NewVBox instead?
To do what you're wanting to do here you need to modify the slice of *widget.Button to be a slice of fyne.CanvasObject.
When spreading into a variadic parameter like this, the types have to match exactly to what the variadic parameter is expecting. This means the type needs to be the interface itself and not a type that implements the interface.
In your case, the following will work:
btns := []fyne.CanvasObject{btn0, btn1}
vbox := container.NewVBox(btns...)

How to resize window to dialog size in Fyne

I am using the Fyne package for Go. All I want to do is display a dialog, and have the window automatically resize to the maximum size of the dialog. I've gone over the documentation and looked at examples but I can't find it anywhere! The window is always 0x0 pixels. If I resize the window out I can tell that the dialog has a maximum height and width but I can't seem to feed that back into the size of the window. Here is my code:
import (
"fyne.io/fyne"
"fyne.io/fyne/app"
"fyne.io/fyne/dialog"
)
var myApp fyne.App
func main() {
myApp = app.New()
myWindow := myApp.NewWindow("test")
cnfm := dialog.NewConfirm("Test Dialog!", "Are you sure you want to interact with this test dialog?", loseCallback, myWindow)
cnfm.Show()
cnfm.Resize(cnfm.MaxSize())
myWindow.Show()
// uncomment to have a window greater than 0x0
//myWindow.Resize(fyne.NewSize(375, 180))
myApp.Run()
}
func loseCallback(yes bool) {
myApp.Quit()
}
This seems really basic but I just can't find any way to achieve this! I've tried playing with layouts, containers, etc but nothing makes a difference, the window is always 0x0. I would really appreciate some help with this!
Fyne doesn't really have a concept of MaxSize. There is MinSize() (what a component should never be smaller than) and it's Size() which is the current size on screen.
Dialogs appear over the current content so are not constrained, or expaneded, by the content layout etc.
As a dialog is not designed to be the main content of a while I wonder if changing your content to be inside the window would be more suitable (as the dialog is really used when something in the app is happening and you want to interrupt it).
For example:
import (
"fyne.io/fyne"
"fyne.io/fyne/app"
"fyne.io/fyne/container"
"fyne.io/fyne/layout"
"fyne.io/fyne/widget"
)
var myApp fyne.App
func main() {
myApp = app.New()
myWindow := myApp.NewWindow("test")
yes := widget.NewButton("Yes", closeCallback)
yes.Importance = widget.HighImportance
myWindow.SetContent(container.NewVBox(
widget.NewLabel("Are you sure you want to interact with this test dialog?"),
container.NewHBox(layout.NewSpacer(),
widget.NewButton("No", closeCallback), yes,
layout.NewSpacer())))
myWindow.ShowAndRun()
}
func closeCallback() {
myApp.Quit()
}

Resources