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
Related
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
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...)
I'm having an issue with passing dynamic arguments to a button callback with Fyne. I'm trying to create a list of buttons that when clicked will call a function with a specific string argument:
clients := []fyne.CanvasObject{}
for _, uuidStr := range uuidStrs {
clientBtn := widget.NewButton(uuidStr, func() { server.selectClient(uuidStr))
clients = append(clients, clientBtn)
}
clientsListed := container.New(layout.NewGridLayout(1), clients...)
The issue is that all of the buttons, when clicked, will select the last client e.g. it will call server.selectClient(uuidStr) where uuidStr is always uuidStrs[len(uuidStrs)-1], but I want each button to pass in a unique uuidStr. All of the buttons display the correct string, but don't pass the correct string in the callback.
How can I get the buttons, when clicked, to select the client it displays?
This is due to how Go re-uses variables in loop iterations. You need to "capture" the value so that the button callback func gets the right value. As follows:
clients := []fyne.CanvasObject{}
for _, uuidStr := range uuidStrs {
uuid := uuidStr
clientBtn := widget.NewButton(uuidStr, func() { server.selectClient(uuid))
clients = append(clients, clientBtn)
}
clientsListed := container.New(layout.NewGridLayout(1), clients...)
I have recently looked into Go plugins instead of manually loading .so files myself.
Basically, I have a game server application, and I want to be able to load plugins (using plugins package) when the server starts. Then, in the plugin itself, I want to be able to call exported functions that are a part of the server.
Say I have this plugin, which is compiled to example_plugin.so using go build -buildmode=plugin:
package main
import "fmt"
func init() {
fmt.Println("Hello from plugin!")
}
Then say I have this server application, which loads the plugin (and ultimately calls the "init" function under the hood):
package main
import (
"fmt"
"plugin"
)
func main() {
fmt.Println("Server started")
if _, err := plugin.Open("example_plugin.so"); err != nil {
panic(err)
}
}
// some API function that loaded plugins can call
func GetPlayers() {}
The output is:
Server started
Hello from plugin!
This works as expected, however I want to be able to call that GetPlayers function (and any other exported functions in the server application, ideally) from the plugin (and any other plugins.) I was thinking about making some sort of library consisting of interfaces containing API functions that the server implements, however I have no idea where to start. I know I will probably need to use a .a file or something similar.
For clarification, I am developing this application for use on Linux, so I am fine with a solution that only works on Linux.
Apologies if this is poorly worded, first time posting on SO.
As mentioned in the comments, there is a Lookup function. In the documentation for the module they have the following example:
// A Symbol is a pointer to a variable or function.
// For example, a plugin defined as
//
// var V int
//
// func F() { fmt.Printf("Hello, number %d\n", V) }
//
// may be loaded with the Open function and then the exported package
// symbols V and F can be accessed
package main
import (
"fmt"
"plugin"
)
func main() {
p, err := plugin.Open("plugin_name.so")
if err != nil {
panic(err)
}
v, err := p.Lookup("V")
if err != nil {
panic(err)
}
f, err := p.Lookup("F")
if err != nil {
panic(err)
}
*v.(*int) = 7
f.(func())() // prints "Hello, number 7"
}
I think the most confusing lines here are
*v.(*int) = 7
f.(func())() // prints "Hello, number 7"
The first one of them performs a type assertion to *int to assert that v is indeed a pointer to int. That is needed since Lookup returns an interface{} and in order to do anything useful with a value, you should clarify its type.
The second line performs another type assertion, this time making sure that f is a function with no arguments and no return values, after which, immediately calls it. Since function F from the original module was referencing V (which we've replaced with 7), this call will display Hello, number 7.
I have a struct type with a number of fields that all implement a Renderer interface. The field types implement the interface with pointer receivers. I would like to have a function that takes the field name and calls the Render method on that field. I am able to locate the field and get lots of information about it, but doing the type assertion seems to be biting me because of the pointer receivers. Here's some code that shows my problem:
package main
import (
"fmt"
"reflect"
)
type Renderer interface {
Render()
}
type First struct {
ExampleField Field
}
type Field []int
func (f *Field) Render() {
fmt.Println("Hello from first")
}
func main() {
f := First{
Field{1, 2, 3},
}
f.ExampleField.Render()
renderField("ExampleField", &f)
renderField2("ExampleField", &f)
}
func renderField(field string, f *First) {
structVal := reflect.ValueOf(*f)
renderType := reflect.TypeOf((*Renderer)(nil)).Elem()
fieldToRender := structVal.FieldByName(field)
fieldPtr := reflect.PtrTo(fieldToRender.Type())
fmt.Printf("Implements? %v\n", fieldPtr.Implements(renderType))
fmt.Printf("Addressable? %v\n", fieldToRender.CanAddr())
fieldInter := fieldToRender.Interface()
if renderer, ok := fieldInter.(Renderer); ok {
// Pointer receiver so this never gets called
fmt.Print("Able to cast")
renderer.Render()
}
}
func renderField2(field string, f *First) {
structVal := reflect.ValueOf(*f)
fieldToRender := structVal.FieldByName(field)
vp := reflect.New(reflect.TypeOf(fieldToRender))
vp.Elem().Set(reflect.ValueOf(fieldToRender))
vpAddr := vp.Elem().Addr()
typeVal := vpAddr.Interface()
fmt.Println(typeVal) // <main.Field Value>⏎
renderer := typeVal.(Renderer)
renderer.Render()
// interface conversion: *reflect.Value is not main.Renderer: missing method Render
}
renderField2 seems to get me close but Addr() gives me a *Reflect.Value and when I call Interface() that seems to be the underlying type instead. If I switch to a non-pointer receiver then the first function works. I found reflect value Interface and pointer receiver which seems to be almost exactly what I'm asking, and the question is answered but if I actually call the isZeroer method presented in the playground link it's always false so it doesn't actually seem to answer the question.
It seems like Addr is the key because it specifically mentions pointer receivers but I'm struggling to coerce it back into an interface.
Use this code:
func renderField(name string, f *First) {
structVal := reflect.ValueOf(f).Elem()
field := structVal.FieldByName(name).Addr().Interface()
if renderer, ok := field.(Renderer); ok {
renderer.Render()
}
}
The key point is to change:
structVal := reflect.ValueOf(*f)
to:
structVal := reflect.ValueOf(f).Elem()
The statement used in the question creates a non-addressable struct value. The fields in a non-addressable struct are also not addressable, therefore it's not possible to access the pointer receiver on the fields.
The statement used in this answer creates an addressable struct value.