mirror of
https://github.com/rivo/tview.git
synced 2026-04-26 13:25:51 +03:00
[GH-ISSUE #128] Questions: efficient way draw screen #98
Labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
starred/tview#98
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Originally created by @stephencheng on GitHub (Jun 2, 2018).
Original GitHub issue: https://github.com/rivo/tview/issues/128
I am not entirely clearly understanding how Draw() method works and how it is efficient.
For example, I have a page with a few flexs, then the flex contains tables/list/textview etc.
I did update using app.Draw() each time when a primitive updated with the new data. It ends up with a few app.Draw() calls in just almost a second. The whole screen sometimes flashes in a funny way that one region/area was updated but updated again due to the refresh of the bigger area of parent primitive.
So questions:
Is the app.Draw the only method to call to redraw the screen? and does it have the smart to refresh the root primitive and all children primitives?
If so, how do I refresh each child primitive element individually?
I'd assume the interface method: Draw(screen tcell.Screen) is for app.Draw to trigger the draw for each primitive, am I right? again, is there a method in primitive I can call to only refresh that primitive only?
Thanks
@rivo commented on GitHub (Jun 5, 2018):
To answer your questions:
app.Draw()is the only application-wide method to update the screen. Apart from some synchronization, it only calls theDraw()function of the root primitive. If your root primitive is a container primitive (e.g.Flex,Grid, orPages), it will then callDraw()of its contained primitives. Drawing always happens top-down.tcellwill only update changes to the screen buffer. (I haven't actually tested this myself but that's my assumption.) So if there was no change to any of your primitives, you can callapp.Draw()many times and nothing will be sent to the terminal. (You will have some CPU overhead to redraw the screen buffer but I would think that's negligible.)app.Draw(), or to be more precise, insert additional drawing actions before and after drawing the primitives, but I think that's not what you're asking for. But in general, there is no dependency from the primitives back toApplication. In theory, you could write your own replacement forApplicationand handle drawing differently. (But then you'd also have to do all the event handling yourself.)This is what
tcellsays on the subject:Two additional notes:
app.Draw()is called implicitly in response to events. So if you do something in response to an event (in the same goroutine), you don't need to callapp.Draw()again. Only if you modify your primitives in a separate goroutine (e.g. writing toTextView), you need to callapp.Draw()to reflect those changes.app.Draw(). For example, if you write individual characters to aTextViewinstead of longer strings, you may want to callapp.Draw()periodically instead of after each "changed" event.@stephencheng commented on GitHub (Jun 12, 2018):
Thanks, it's very good explanation. I think currently I will just rely on existing implementation. I am happy as long as the Draw only update when needed(there is buffer change)
Thank you
@ardnew commented on GitHub (Jul 6, 2018):
To minimize updates/flickering, I'm currently doing as you suggested in your comment -- primitives will post to a channel when they've made a change, the channel is polled periodically with an
app.Draw()being called one time if necessary, and then the channel is drained. This seems to be working fine.However, since it appears from #59 that our
Application'stcell.Screenwon't be exposed to us, I'm wondering how we are supposed to use all the primitives' draw methods (e.g.func (*TreeView) Draw(tcell.Screen)) that are exported through the API? We obviously can't use them without atcell.Screen. Please correct me if I'm wrong, but the only way then to access those primitive draw methods (without defining our ownApplicationreplacement) is to:func tryDraw(tcell.Screen) bool, viaSetBeforeDrawFunc()app.Draw()periodicallytryDraw()events, check each primitive if it has a draw update available and call its primitive draw method using the screen reference passed into the callbacktruefrom the callback so that drawing does not continue (hopefully for the remainder of primitives only?)This seems cumbersome (and susceptible to unintentionally cancelling other draw events) so I'm wondering if I am misunderstanding how to use those primitive draw methods. Is there a simpler way to tell a single primitive to redraw?
Thanks
@rivo commented on GitHub (Jul 17, 2018):
A primitive's
Draw()function is exported, yes, but only because those types implement the publicPrimitiveinterface. It's not meant to be called directly by any application, unless you write your ownApplicationreplacement.So, similar to @stephencheng's question, I'm wondering why you want to call a primitive's
Draw()function directly. It should be safe to callapp.Draw()for updates. As mentioned further above,tcellattempts to minimize the number of characters sent to the terminal so the main overhead is drawing into the internal screen buffer which should be minimal.Also,
tviewdoes not implement clipping. So if you usePagesfor example, with overlapping windows (e.g. aModal), updating a primitive in the background will mess up your layout.If you insist on calling a primitive's
Draw()function directly, I would suggest writing your ownApplicationclass. But note that in the end, you'll have to calltcell'sscreen.Show()function, too, which also updates the entire screen and not just your primitive. So I'm not sure if you'll gain something from that.Let me know what you think about this.
@ardnew commented on GitHub (Jul 17, 2018):
I'll admit, the intent with calling the primitives'
Draw()routine was premature optimization on my part without fully understanding the mechanics. I was misunderstanding their purpose for being exported, assuming they were an efficient alternative toapp.Draw(). Also -- painful experiences from certain other Win32 GUI toolkits I was trying to avoid.Once I implemented the buffering method (by polling for
Draw()updates) you suggested, I've had zero performance issues with screen drawing, sotcellmust be handling the updates efficiently.I'm comfortable just trusting
tcellhere, as it keeps my code cleaner and far simpler (with probable equivalent or superior performance).@rivo commented on GitHub (Jul 17, 2018):
I'm glad that you found a solution! If there's anything else regarding this topic, feel free to keep the discussion going. I haven't tested
tcell's update performance myself so if it fails in some cases, maybe there's some merit in looking into optimizing ontview's side (or even examinetcellin more detail).