Fyne passing changing arguments to multiple buttons - go

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...)

Related

How to read multiple parameters from a URL

I have a reverse proxy API that reads the parameters of a localhost API call and then sends those parameters to a 3rd party API.
I'm able to get this working correctly if I only use one parameter. Like so:
http://localhost:8080/path?page=1
I want to be able to use more than one parameter however like so:
http://localhost:8080/path?page=1&param=x
Please see my code below:
This function catches an HTTP request and then sends those parameters to another API.
func (s *Server) getReverseProxy(w http.ResponseWriter, r *http.Request) {
// when I try to append another query in the list a long with page, I get an error
keys, ok := r.URL.Query()["page"]
if !ok || len(keys[0]) < 1 {
log.Println("Url Param 'page' is missing")
return
}
// Query()["key"] will return an array of items,
// we only want the single item.
key := keys[0]
log.Println("Url Param 'page' is: " + string(key))
params := url.Values{
"page[size]": []string{"100"},
"page[number]": []string{""},
}
u := &url.URL{
Scheme: "https",
Host: "url.com",
Path: "/path",
RawQuery: params.Encode(),
}
}
Without having to refractor, am I missing something simple here? How can I add another parameter for my function to catch?
The line of code below ...
keys, ok := r.URL.Query()["page"]
it returns the param value of page, but in []string type. To retrieve more params, simply add similar statement with different param name. for example:
keysPage, ok := r.URL.Query()["page"]
keysParamA, ok := r.URL.Query()["ParamA"]
keysParamB, ok := r.URL.Query()["ParamB"]
keysParamC, ok := r.URL.Query()["ParamC"]
Or, you can also use the r.URL.Query().Get(key) to return the param value in string type.
page := r.URL.Query().Get("page")
paramA := r.URL.Query().Get("ParamA")
paramB := r.URL.Query().Get("ParamB")
paramC := r.URL.Query().Get("ParamC")
r.URL.Query() returns a map[string][]string
you can do a
keys, ok := r.URL.Query()
//browse through keys by
keys["params"]
keys["page"]

Change dataType from bigquery.Value to string

I connect BigQuery with Go language as the following API documentation demonstrates,
https://cloud.google.com/bigquery/docs/reference/libraries?hl=en_US
After that, I need to get sql results in specific row and column, and judge if it equals a specific string. Could I change bigquery.Value to string and how to do that?
See how to use RowIterator.Next() here.
Next loads the next row into dst. Its return value is iterator.Done if there are no more results. Once Next returns iterator.Done, all subsequent calls will return iterator.Done.
dst may implement ValueLoader, or may be a *[]Value, *map[string]Value, or struct pointer.
Value is of type interface{} so if you are sure that value you have is string str := fmt.Sprintf("%v", row[i]) should work. It is often better to define a struct type that has members representing fields of query result row (with types mapped according to the table in documentation I linked above) and give the pointer to it to RowIterator.Next() instead of slice/map of bigquery.Value.
type myRow struct {
Name string
Num int
}
// ...
q := client.Query("select name, num from t1")
it, err := q.Read(ctx)
// handle err
for {
// instead of: var row []bigquery.Value
var row myRow // <-- use custom struct type here
err := it.Next(&row)
if err == iterator.Done {
break
}
// handle err != nil
someFuncThatTakesString(row.Name)
}

How can I dynamically populate a struct?

I want to dynamically populate my internal struct, for an atomic insert. I am new to go so pointers and referencing them is something that I am still learning. I can not figure out why this for each loop is putting the same fields in twice. I tried removing the '&' then I get a cannot use type as *type error, I checked to make sure my loop was hitting every object in the tradeArray, and it is. It looks like it is overwriting the object before it with the last one it loops over. How can I fix this?
func createTrade(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
var tradeArray []Trade
if err := json.NewDecoder(r.Body).Decode(&tradeArray); err != nil {
e := Error{Message: "Bad Request - Improper Types Passed"}
w.WriteHeader(http.StatusBadRequest)
_ = json.NewEncoder(w).Encode(e)
return
}
for _, trade := range tradeArray {
internal := InternalTrade{
Id: strconv.Itoa(rand.Intn(1000000)),
Trade: &trade,
}
submit := TradeSubmitted{
TradeId: internal.Id,
ClientTradeId: trade.ClientTradeId ,
}
submitArray = append(submitArray, submit)
trades = append(trades, internal)
}
if err := json.NewEncoder(w).Encode(submitArray); err != nil {
e := Error{Message:"Internal Server Error"}
w.WriteHeader(http.StatusInternalServerError)
_ = json.NewEncoder(w).Encode(e)
return
}
}
edit: I was able to fix my problem by creating a new variable to hold the trade and referencing that variable in the struct creation. I am not sure how this is different that what I was doing above with just referencing the "trade" if someone could explain that I would greatly appreciate it.
for _, trade := range tradeArray {
p := trade
internal := InternalTrade{
Id: strconv.Itoa(rand.Intn(1000000)),
Trade: &p,
}
submit := TradeSubmitted{
TradeId: internal.Id,
ClientTradeId: trade.ClientTradeId ,
}
submitArray = append(submitArray, submit)
trades = append(trades, internal)
}
Let's look at just these parts:
var tradeArray []Trade
// code that fills in `tradeArray` -- correct, and omitted here
for _, trade := range tradeArray {
internal := InternalTrade{
Id: strconv.Itoa(rand.Intn(1000000)),
Trade: &trade,
}
submit := TradeSubmitted{
TradeId: internal.Id,
ClientTradeId: trade.ClientTradeId ,
}
submitArray = append(submitArray, submit)
trades = append(trades, internal)
}
This for loop, as you have seen, doesn't work the way you want. Here's a variant of it that's kind of similar, except that the variable trade has scope that extends beyond the for loop:
var trade Trade
for i := range tradeArray {
trade = tradeArray[i]
internal := InternalTrade{
Id: strconv.Itoa(rand.Intn(1000000)),
Trade: &trade,
}
// do correct stuff with `internal`
}
Note that each internal object points to a single, shared trade variable, whose value gets overwritten on each trip through the loop. The result is that they all point to the one from the last trip through the loop.
Your fix is itself OK: each trip through the loop, you make a new (different) p variable, and use &p, so that each internal.Trade has a different pointer to a different copy. You could also just do trade := trade inside the loop, to create a new unique trade variable. However, in this particular case, it may make the most sense to rewrite the loop this way:
for i := range tradeArray {
internal := InternalTrade{
Id: strconv.Itoa(rand.Intn(1000000)),
Trade: &tradeArray[i],
}
// do correct stuff with `internal`
}
That is, you already have len(tradeArray) different Trade objects: the slice header tradeArray gives you access to each tradeArray[i] instance, stored in the underlying array. You can just point to those directly.
There are various advantages and disadvantages to this approach. The big advantage is that you don't re-copy each trade at all: you just use the ones from the array that the slice header covers, that was allocated inside the json Decode function somewhere. The big disadvantage is that this underlying array cannot be garbage-collected as long as you retain any pointer to any of its elements. That disadvantage may have no cost at all, depending on the structure of the remaining code, but if it is a disadvantage, consider declaring tradeArray as:
var tradeArray []*Trade
so that the json Decode function allocates each one separately, and you can point to them one at a time without forcing the retention of the entire collection.

gotk3, notebook appendpage with a custom widget and get it back

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

If value is 0 then input value will be empty, value otherwise

Problem:
I have created a simple form, where there's an input field "num". After submission I want to show the value of num in the same input field, in other words want to retain the input in that field. If the value was set to 0 then I want to ignore that.
I can do it in several languages but I'm not sure about how to do it in Golang. My current template file has,
<input type="text" placeholder="foo" name="bar" value="{{if gt .N 0 }} {{.N}} {{end}} "/>
Server file contains:
data := &listOfReport {
R: r,
I: i,
N: n
}
listTmpl := template.Must(template.New("list_tmpl").Parse(string(report.Template["xxx.tmpl"])))
if err := listTmpl.Execute(w, data); err != nil {
http.Error(w, fmt.Sprintf("Error rendering template %v", err), 500)
}
Another thought is to make N a string so make it '' or value in the server file. But that actually spoils the variable's name/purpose.
Is there any better way to do it? Is ther any better way to access GET parameters directly from template? Please note that the value of N is originally got from a GET variable.
*This code is not tested
There is no standard/builtin way to get any request parameters from within a template, you'll have to put it into your data. (You could write a function which does this for you, but that will result in an ugly hack.)
I don't see what's wrong with your solution.
I take a similar approach, but use structs.
type SignupForm struct {
Name string
Email string
Etcera bool
}
// Type alias
type M map[string]interface{}
...
// In the handler that accepts your form
err := r.ParseForm()
if err != nil {
// handle error
}
signup := SignupForm{}
err := decoder.Decode(signup, r.PostForm)
if err != nil {
// handle error
}
// Store the 'saved' form contents somewhere temporary -
// e.g.
// - cookies (keep in mind the 4K browser limit)
// - server side sessions (Redis; how I do it)
// - db
// In the handler that renders your form
err := template.ExecuteTemplate(w, "form.html", M{
"form": signup,
"csrfToken": csrfToken,
// and so on...
})
Note that wherever you store the form data, make sure it is temporary. Server side sessions are ideal as you can have them expire (if you don't want to delete them manually).

Resources