Problem
I can't find function to set position of the window. I'v looked into code and I see SetPos func for different elements and wondering why it's not added for fyne.Window type.
func main() {
a := app.New()
w := a.NewWindow("Trying to position window")
if drv, ok := fyne.CurrentApp().Driver().(desktop.Driver); ok {
w = drv.CreateSplashWindow()
// something like this?
// w.SetPos(x, y)
// w.Move(x, y)
}
Workaround
I forked project and created func:
func (w *window) GLFWindow() *glfw.Window {
return w.view()
}
to expose unexported underlying window attribute w.viewport. and it unlocks bunch of methods i can use now
if ww := w.GLFWindow(); ww != nil {
ww.SetPos(x, y)
}
It looks I'm gonna use it (forked/edited version) but maybe you can suggest fyne-way to do it :)
Question
Is there existing way to set position of the window? Or access underlying w.viewport attribute?
This is not possible with the exported API, because many OS do not support it.
See discussion about the decision process at https://github.com/fyne-io/fyne/issues/1155
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()
}
I have this summarized code to display tabs in a notebook in Gtk. Basically, I have created a custom struct with a embedded label, that is added to the notebook. After that, I want to get back that custom widget, but I get a Invalid type assertion. I have read a lot about structs and interfaces, but I can't get it to work.
package main
import "github.com/gotk3/gotk3/gtk"
type NotebookPage struct {
gtk.Label
}
func main() {
gtk.Init(nil)
win, _ := gtk.WindowNew(gtk.WINDOW_TOPLEVEL)
notebook, _ := gtk.NotebookNew()
win.Add(notebook)
content1, _ := gtk.LabelNew("Content 1")
page1 := NotebookPage{Label: *content1}
label1, _ := gtk.LabelNew("Label 1")
notebook.AppendPage(&page1, label1)
content2, _ := gtk.LabelNew("Content 2")
page2 := NotebookPage{Label: *content2}
label2, _ := gtk.LabelNew("Label 2")
notebook.AppendPage(&page2, label2)
win.ShowAll()
win.Connect("destroy", func() {
gtk.MainQuit()
})
backwidget1, _ := notebook.GetNthPage(0)
backpage1, _ := backwidget1.(*NotebookPage)
gtk.Main()
}
Looks like your problem is here on line 35 as the message says:
backpage1, _ := backwidget1.(*NotebookPage)
notebook.GetNthPage returns a *Widget, *NotebookPage is not a *Widget, so you're not allowed to cast to it. If the function didn't return a concrete type (if it returned the same IWidget interface), and if it didn't roundtrip through gtk C libraries, you could do this.
As it is if you want to get your custom widget back you probably need to get at the underlying gtk widget or serialisation (which presumably stores your custom label), extract the label and build a new NotebookPage.
So you need something like :
func NewNotebookPage(widget *Widget) {
return &NotebookPage{Label: widget.GetLabelSomehow()}
}
It looks like Label is also a widget :) This is painful because it's trying to work with C++ inheritance in Go. You'll have to find out how to unfreeze your label from the widget.C.GtkWidget
I want to create a tool with Go that lets me resize multiple windows on my screen. As an example lets assume that I want to find my Firefox window and my Atom (text editor) window and place them, so that they take up exactly half of my screen (FF left, Atom right).
So far I realized, that I need to use the Windows API for that. I created a method that gives me all handles and the titles of all windows, but I'm struggling with geometry information. I understand that the api call GetWindowRect will help, but how can I get the information out of a pointer to a rect?
Follow up question 1: what other information can I get about the windows?
Follow up question 2: How do I resize the window so that it takes exactly half my screen size? I guess, I need another call to get the monitor dimensions.
What I have so far is the code below. The main program finds all handles and displays those containing 'Atom' in the title. The windows package contains the code accessing the windows API.
My current result is that I get 2 handles for atom (why not just 1?). I guess, I have to learn more about the Windows API, too. Are there good summaries to understand the basics?
main.go:
package main
import (
"resizer/windows"
"fmt"
"log"
"strings"
)
func main() {
const title = "Atom"
m := windows.GetAllWindows()
fmt.Printf("Map of windows: \n")
for handle := range m {
if strings.Contains(m[handle].Title(), title) {
fmt.Printf("'%v'\n", m[handle])
}
}
}
windows.go:
package windows
import (
"fmt"
"log"
"syscall"
"unsafe"
)
var (
user32 = syscall.MustLoadDLL("user32.dll")
procEnumWindows = user32.MustFindProc("EnumWindows")
procGetWindowTextW = user32.MustFindProc("GetWindowTextW")
)
// Window represents any Window that is opened in the Windows OS
type Window struct {
handle syscall.Handle
title string
}
// Title returns the title of the window
func (w Window) Title() string {
return w.title
}
// GetAllWindows finds all currently opened windows
func GetAllWindows() map[syscall.Handle]Window {
m := make(map[syscall.Handle]Window)
cb := syscall.NewCallback(func(h syscall.Handle, p uintptr) uintptr {
bytes := make([]uint16, 200)
_, err := GetWindowText(h, &bytes[0], int32(len(bytes)))
title := "||| no title found |||"
if err == nil {
title = syscall.UTF16ToString(bytes)
}
m[h] = Window{h, title}
return 1 // continue enumeration
})
EnumWindows(cb, 0)
return m
}
// EnumWindows loops through all windows and calls a callback function on each
func EnumWindows(enumFunc uintptr, lparam uintptr) (err error) {
r1, _, e1 := syscall.Syscall(procEnumWindows.Addr(), 2, uintptr(enumFunc), uintptr(lparam), 0)
if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}
// GetWindowText gets the title of a Window given by a certain handle
func GetWindowText(hwnd syscall.Handle, str *uint16, maxCount int32) (len int32, err error) {
r0, _, e1 := syscall.Syscall(procGetWindowTextW.Addr(), 3, uintptr(hwnd), uintptr(unsafe.Pointer(str)), uintptr(maxCount))
len = int32(r0)
if len == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}
GetWindowRect() writes the geometry to the RECT structure you pass the pointer to in. It operates exactly like the GetWindowText() call you already have; the difference is you have to provide the RECT structure yourself.
You should be able to just get away with copying the structure verbatim. To substitute data types, use this page. The definition for RECT says all the fields are LONG, which that page says is "[a] 32-bit signed integer". So this should suffice:
type RECT struct {
left int32 // or Left, Top, etc. if this type is to be exported
top int32
right int32
bottom int32
}
(Most likely irrelevant, but it's worth pointing out that RECT operates identically to image.Rectangle, with left and top being Min and right and bottom being Max. They are not identical because image.Rectangle uses int, so you may want to consider providing conversion functions if you want to use image's geometry functions to manipulate rectangles instead of GDI's.)
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 found several questions with similar titles, but cannot find the answer to my question in them:
I have the following simple scenario:
types:
type intMappedSortable interface {
getIntMapping() int
}
type Rectangle struct {
length, width int
}
func (r Rectangle) getIntMapping() int {
return r.Area();
}
func (Rectangle r) getIntMapping() int {
return r.length * r.width;
}
main:
func main() {
r := rand.New(rand.NewSource(time.Now().UnixNano()))
var values []int
values = make([]int, 0)
for i := 0; i < 10; i++ {
values = append(values, r.Intn(20))
}
var rects []Rectangle;
rects = make([]intMappedSortable, len(values));
for i,v:= range values {
r := Rectangle{v,v};
rects[i] = r;
}
for i,v:= range rects {
fmt.Println(v.Area());
}
rectsRet := make(chan intMappedSortable, len(rects));
sort(rects, rectsRet);
}
doWork:
func sort(values []intMappedSortable, out chan intMappedSortable) {...}
How do i manage to pass the Rectangles to the sorting function and then work with the sorted rectangles in main after it?
I tried:
var rects []*Rectangle;
rects = make([]*Rectangle, len(values));
as a habit from my C days, i don't want to copy the rectangles, just the addresses, so i can sort directly in the original slice, preventing 2 copy procedures for the whole data.
After this failed i tried:
var rects []intMappedSortable;
rects = make([]*Rectangle, len(values));
i learned that Go handles "polymorphism" by holding a pointer to the original data which is not exposed, so i changed *Rectangle to Rectangle, both gave me the compilererror that Rectangle is not []intMappedSortable
What obviously works is:
var rects []intMappedSortable;
rects = make([]intMappedSortable, len(values));
for i,v:= range values {
r := Rectangle{v,v};
rects[i] = r;
}
But are are these rectangles now copied or is just the memoryrepresentation of the interface with their reference copied? Additionally there now is no way to access length and width of the rectangles as the slice is not explicitly of type rectangle anymore.
So, how would i implement this scenario?
I want to create a slice of ANY structure, that implements the mapToInt(), sort the slice and then keep working with the concrete type after it
EDIT/FOLLOWUP:
I know its not good style, but i'm, experimenting:
can i somehow use type assertion with a dynamic type like:
func maptest(values []intMappedSortable, out interface{}) {
oType := reflect.TypeOf(out);
fmt.Println(oType); // --> chan main.intMappedSortable
test := values[0].(oType) //i know this is not working AND wrong even in thought because oType holds "chan intMappedSortable", but just for theory's sake
}
how could i do this, or is this not possible. I do not mean wether it is "meant to be done", i know it is not. But is it possible?^^
But are are these rectangles now copied or is just the memory representation of the interface with their reference copied?
The latter, see "what is the meaning of interface{} in golang?"
An interface value is constructed of two words of data:
one word is used to point to a method table for the value’s underlying type,
and the other word is used to point to the actual data being held by that value.
I want to create a slice of ANY structure, that implements the mapToInt(), sort the slice and then keep working with the concrete type after it
That isn't possible, as there is no genericity in Go.
See "What would generics in Go be?"
That is why you have projects like "gen":
generates code for your types, at development time, using the command line.
gen is not an import; the generated source becomes part of your project and takes no external dependencies.