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()
}
Related
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()
}
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().
I have the code below, It shows a window, generate an image as raster and then update the window content with it.
However, the setContent method is slow (with it I have 100% of 1 cpu core and almost 0 without).
I wonder if there is anything to do to do what I do here efficiently (modifiying underlaying raster, use gpu in anyway...) . I would like to be able to generate an image with raster and then display it at ~60 fps efficiently.
Any suggestion or other tools to do it better would be appreciated.
package main
import (
"image/color"
"math/rand"
"time"
"fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/canvas"
)
func main() {
myApp := app.New()
w := myApp.NewWindow("Raster")
go func() {
for {
time.Sleep(time.Millisecond * 500)
raster := canvas.NewRasterWithPixels(
func(_, _, w, h int) color.Color {
return color.RGBA{uint8(rand.Intn(255)),
uint8(rand.Intn(255)),
uint8(rand.Intn(255)), 0xff}
})
w.SetContent(raster)
}
}()
w.SetFullScreen(true)
// w.Resize(fyne.NewSize(120, 100))
w.ShowAndRun()
}
Setting the window content is a very expensive call as it has to re-layout all the content and check sizes etc.
Just call raster.Refresh() instead.
You could also cache the raster pixels in your widget so you don’t have to create a new image for every frame to refresh.
Here's some sample code that puts a Gauge on the screen and make the progress bar increase 1 value every second. On MacOS I don't see the progress bar update unless I drag the window around or resize it manually with the mouse. Any idea how to force the whole thing to repaint? I'm calling f.Refresh() and f.Update()
package main
import "github.com/dontpanic92/wxGo/wx"
import "time"
var g wx.Gauge
type MyFrame struct {
wx.Frame
}
func (f *MyFrame) startUpload() {
for {
time.Sleep(time.Second)
g.SetValue(g.GetValue() + 1)
f.Refresh()
f.Update()
}
}
func NewMyFrame() MyFrame {
f := MyFrame{}
f.Frame = wx.NewFrame(wx.NullWindow, -1, "Test Thread")
mainSizer := wx.NewBoxSizer(wx.HORIZONTAL)
g = wx.NewGauge(f, wx.ID_ANY, 100, wx.DefaultPosition, wx.NewSize(600, 40), 0)
f.SetSizer(mainSizer)
mainSizer.Add(g, 100, wx.ALL|wx.EXPAND, 50)
f.Layout()
go f.startUpload()
return f
}
func main() {
wx1 := wx.NewApp()
f := NewMyFrame()
f.Show()
wx1.MainLoop()
return
}
Update: I've been reading http://docs.wxwidgets.org/trunk/overview_thread.html and I'm trying code like:
b := wx.NewPaintEvent()
f.GetEventHandler().QueueEvent(b)
instead of calling Refresh and Update but my wx.NewPaintEvent doesn't do anything. Maybe I'm making the wx.NewPaintEvent wrong? Or I'm adding it to the wrong EventHandler?
Generally speaking, doing anything with GUI objects from the non-main (i.e. not the one that initialized the library) thread is not supported by wxWidgets.
The usual workaround is to post an event to the main thread asking it to update the widget instead of doing it directly in the worker thread. In C++ this can be done easily and elegantly using CallAfter(), but I don't know enough about Go and wxGo to show you an example of doing it in this language.
author of wxGo fixed it:
https://github.com/dontpanic92/wxGo/issues/10
wx.Bind(f, wx.EVT_THREAD, func(e wx.Event) {
threadEvent := wx.ToThreadEvent(e)
the_gauge.SetValue(threadEvent.GetInt())
}, UPLOAD_WORKER_ID)
then in the thread:
threadEvent := wx.NewThreadEvent(wx.EVT_THREAD, UPLOAD_WORKER_ID)
threadEvent.SetInt(50)
f.QueueEvent(threadEvent)
I'm trying to build a full screen terminal application. I'm using Go as my language of choice. I've figured out how to read from os.Stdin, but I'm unclear on how to clear the terminal window and manipulate the cursor position. I also want to capture the terminal input without it being printed (echoed back).
My questions are:
How can I effectively clear and print to the terminal with column/row coordinates?
How do I stop the terminal from printing keys pressed
My intent:
I want to create a full screen terminal application that renders it's own UI and handles input internally (hot keys/navigation/etc...).
If there are any libraries that cover this sort of use case please feel free to suggest them.
The easiest way to clear the terminal and set position is via ansi escape codes. However, this may not be the ideal way as variation in terminals may come back to bite you.
fmt.Print("\033[2J") //Clear screen
fmt.Printf("\033[%d;%dH", line, col) // Set cursor position
A better alternative would be to use a library like goncurses or termbox-go (credit: second is from Tim Cooper's comment).
With such a library you can do things like this:
import (
gc "code.google.com/p/goncurses"
)
func main() {
s, err := gc.Init()
if err != nil {
panic(err)
}
defer gc.End()
s.Move(5, 2)
s.Println("Hello")
s.GetChar()
}
Code above copied from Rosetta Code
As of December 2019, I would recommend using rivo/tview library.
(goncurses mentioned by #vastlysuperiorman has not been updated since June 2019 and termbox-go is explicitly declared unmaintained).
Here's the "hello world" app, taken from the project's README (reformatted for readability):
package main
import (
"github.com/rivo/tview"
)
func main() {
box := tview.NewBox().
SetBorder(true).
SetTitle("Hello, world!")
if err := tview.NewApplication().SetRoot(box, true).Run(); err != nil {
panic(err)
}
}
tview provides screenshots and example code as well as the standard godoc reference.
To stop the terminal from printing keys pressed you can use the below code:
import (
"fmt"
"syscall"
"golang.org/x/crypto/ssh/terminal"
)
func main(){
fmt.Print("Enter Value: ")
byteInput, _ := terminal.ReadPassword(int(syscall.Stdin))
input:= string(byteInput)
fmt.Println() // it's necessary to add a new line after user's input
fmt.Printf("Your input is '%s'", input)
}