fyne.Do confusion #5646
-
While using fyne v2.6.0-rc2, I wrote a widget renderer that has this Refresh method: func (r RunTreeNodeViewRenderer) Refresh() {
r.txt.Text = r.nv.txt
if r.nv.IsSelected() {
r.rct.FillColor = TreeNodeBgSelected
} else if r.nv.hover {
r.rct.FillColor = TreeNodeBgHover
} else {
r.rct.FillColor = TreeNodeBg
}
n := r.nv.n
n.RLockTree()
st, blocked, cl := n.Status, n.Blocked, n.StatusColor(true)
n.RUnlockTree()
r.st.Text = rt.StatusEmoji(st, blocked)
r.st.Color = cl
fyne.DoAndWait(func() {
r.rct.Refresh()
r.st.Refresh()
r.txt.Refresh()
})
} The most important thing is that it calls the All right, if I call the above Refresh() method from the main goroutine then I get this error:
but if I don't use fyne.Do or fyne.DoWait then I get this error instead:
You see, the Refresh() method sometimes needs to be called from the main goroutine, but sometimes it needs to be called from another goroutine. So no matter if I use fyne.Do or not, I'll always get an error. Of course, I can always call
The general question is this: how to write a method that calls other fyne methods, and can be called from either the main goroutine and also from other goroutines? What is the preferred way to do this in 2.6 ? |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 4 replies
-
Don't bury What needs to be fixed is when your app calls |
Beta Was this translation helpful? Give feedback.
-
Yes, I understand that my renderer's Refresh() method should not be semantically different from any other renderer's Refresh method. But the concept of a "correct thread" was not clear. So you are saying that whenever I call the Refresh method of any widget from anywhere, I must already know if the current call stack is part of the main goroutine or not? Generally speaking, it is not possible to write a function that can be called from both the main goroutine and also from other goroutines. It must either be a "call from main thread" function or a "call from elsewhere function", or introduce an extra parameter. This is very different from the previous fyne version. Some programs (just like mine) will need more refactoring. Migrating to v2.6 cannot be done by simply wrapping specific calls in fyne.Do and fyne.DoWait. Migrating between versions cannot be done formally, it requires semantical analysis, and possibly splitting existing code into different variants. Thanks for the explanation. |
Beta Was this translation helpful? Give feedback.
-
Yes, for example I have this method that is called whenever the current node changes. func (nv *RunTreeNodeView) NodeChanged() {
nv.txt = nv.n.DisplayLabel()
nv.Refresh()
} This method also needs to be called from the main thread, because it calls Refresh. Any function that calls NodeChanged() directly must also be called from from the main thread etc. In reality, the current node can be changed from many places, including widget code that is already wrapped in fyne.Do, and other goroutines. I have to keep in mind (and possibly document?) which functions should be called from the main thread and which are not. In a bigger program, when there are a lot of (old) functions calling each other, this can really be a pain. But I also understand that this is the only way to make it efficient. The general solution for fyne.Do would be: go func() {
fyne.Do(func() }
nv.Refresh()
})
} but that's a burden. And I think there is no general solution for fyne.DoWait. Thank you again |
Beta Was this translation helpful? Give feedback.
Don't bury
fyne.DoAndWait
in your widgetRefresh
, because it should always be called on the correct thread.What needs to be fixed is when your app calls
Refresh()
from a background goroutine.