Jump to content

A tale of two ui update methods


Which is better in general?  

14 members have voted

You do not have permission to vote in this poll, or see the poll results. Please sign in or register to vote in this poll.

Recommended Posts

So, I wanted to get an opinion here?

I'm Pulling new values in from a device, and have UI elements change color depending on their value. This is of course done through property nodes. My question is what is better:

A. Updating UI element only on value change (checking for value changes)

B. Updating all UI elements all at once within a Defer front panel updates segment? ( and not checking for value changes)

Thats all.

Link to comment

So i took, some time to do some research here. I believe my method will have to be reviewed for fairness. So ill upload the two VIs i used.

image.png.7c3b40045d2ef2af1808e7046db290ef.png

It looks to me that if you have less than 10 elements update on average. than you'll probably get decent performance. but after looking at this i would probably recommend always deferring front panel updates, if managing property nodes in any way.

MethodPerformance.vi TestRunner.vi

Link to comment

The front panel should be visible during the test to ensure that it is actually redrawn. It should also contain the necessary number of indicators to make both cases are comparable. If you reuse the same reference multiple times and defer panel updates, it only measures the iteration time of the For-loop + a single redraw, which is not the same as updating the UI on every loop iteration.

Here is a version of your VI that has 100 boolean indicators on the front panel and is visible during test (it is also important to have all indicators visible on screen during test): MethodPerformance.vi

On my PC the differences are much smaller with this version (if you set the number of elements to 1, the number of updates is roughly the same).

image.png.8512b790e31a627f5f5de4fc13c398ef.png

 

Link to comment

There is important aspect to the Front Panel defer updates nodes, this is from the help

When you set this property to TRUE, LabVIEW redraws any front panel objects with pending changes then defers all new requests for front panel updates. For example, controls and indicators do not redraw when you change their properties or values. If the operating system requests a redraw, such as if the window is no longer behind another window, LabVIEW redraws the front panel with the current properties instead of the original properties. If FALSE, LabVIEW immediately redraws the changed elements of the front panel.

 

So when you use it, it actually causes the front panel to redraw twice.

Link to comment

You could also use a color box indicator behind a transparent control. Color box indicators dont need the UI thread to update, I dont think.

to answer your question, I'm coming from an industrial control background so I'd say B. you dont need to change color faster than say 200-300 ms unless color is critical to your application, so a consistent polled update seems much easier to implement and is more stable of course

Edited by smithd
Link to comment

I have tried many different techniques but my currently preferred one is to assume upfront that I am going to use property nodes for all value updates so this then allows me to have a nice Update Sub VI where I update all the values every time I need to update anything. Any non-trivial GUI seems to require property nodes to do the "fancy" stuff, so I just start off with them like that. I normally don't bother with a Defer unless I am dealing with a *lot* of indicators (which is probably a smell of a badly designed GUI) or one of the usual suspects like a big table or tree control.

Link to comment
9 minutes ago, Neil Pate said:

I have tried many different techniques but my currently preferred one is to assume upfront that I am going to use property nodes for all value updates so this then allows me to have a nice Update Sub VI where I update all the values every time I need to update anything. 

You probably already know this, but property nodes have a larger performance hit than a local variable when just the value needs to be updated.  But if you want to update it in a subVI I get why you might use property nodes.  The alternative there is to use the Set Control Values by Index function.  You can read the indexes of the values you want to update in an initialization step, then pass on the indexes, and values you want to update.

Of course this exercise, and this topic has diminishing returns.  I mean lets say I just update all UI elements, all the time, periodically.  The time it takes to update all of this can vary a lot based on what needs to happen but lets just say it takes 100ms which to be fair is a long time.  Will the user notice it takes a while?  Maybe.  Okay so add the defer updates and lets say it is down to 50ms.  Okay lets just update the elements that change 30ms on average due to some overhead.  Okay use Set Control Values by Index, 10ms and you've added a decent amount of complexity to the code that might not have needed it.

So for me it usually starts with just update everything and see how it goes, then refactor as needed.  It feels like the lazy method but I've gone down the other road where I'm hyper concerned with performance and timing and I spend lots of time making the code great, but overly complicated which can make the code harder to maintain.  Various tools can help minimize these issues, but then there are potential down sides of that too.

Link to comment

The only time I consistently use Defer Panel Updates is with a TreeView control, which is slow to update the display (particularly when creating hundreds of rows).

Another strategy is to check for data change to see if an update is needed. But, really, it depends on what you're doing. A configuration/editing screen is driven by user actions, so one event trips multiple updates to the UI and Defer Panel Updates can be useful. A screen showing periodic data may update each message with no issues, or checks if anything needs to be updated and when it does may need to defer updates to do so quickly. Though if you have something like an informational/debug screen that is only shown when requested then might not update the front panel unless it's visible.

10 minutes ago, hooovahh said:

It feels like the lazy method

Could put that as you've done the cost-benefit analysis and the improvement is not justified.  :) 

Link to comment
21 minutes ago, hooovahh said:

You probably already know this, but property nodes have a larger performance hit than a local variable when just the value needs to be updated.  But if you want to update it in a subVI I get why you might use property nodes.  The alternative there is to use the Set Control Values by Index function.  You can read the indexes of the values you want to update in an initialization step, then pass on the indexes, and values you want to update.

Of course this exercise, and this topic has diminishing returns.  I mean lets say I just update all UI elements, all the time, periodically.  The time it takes to update all of this can vary a lot based on what needs to happen but lets just say it takes 100ms which to be fair is a long time.  Will the user notice it takes a while?  Maybe.  Okay so add the defer updates and lets say it is down to 50ms.  Okay lets just update the elements that change 30ms on average due to some overhead.  Okay use Set Control Values by Index, 10ms and you've added a decent amount of complexity to the code that might not have needed it.

So for me it usually starts with just update everything and see how it goes, then refactor as needed.  It feels like the lazy method but I've gone down the other road where I'm hyper concerned with performance and timing and I spend lots of time making the code great, but overly complicated which can make the code harder to maintain.  Various tools can help minimize these issues, but then there are potential down sides of that too.

My programs are so well optimised the user never notices the delay 😉

Joking aside, if I am updating that many controls that the GUI is slow to paint it is probably symptomatic of a bad GUI design.

Link to comment

So, for me it is not just about response time. but CPU usage. We do development on laptops and keeping things below 10% usually prevent the fan from turning on. for some reason using property nodes to do things is very cpu intensive. It is curious to me that the property node takes so much effort to get things working. 

Changing property node use (in my case) brought cpu usage from 16-21% down to around 0.2-1.0% 

I believe the LabVIEW UI is based on an older version of Qt. (i might be wrong), which is a retained mode GUI. I theorize the cost of setting UI properties is that they are expected to be immediately present on the User interface. theorizing again that it could be possible to access a retained property and have much lower cost to this method of UI update.

But, ehh old UI's don't change easy, and I'm sure they have good reasons for picking the things they do. The hard part for me is that it is hard to make educated decisions on how to interact with UI elements. 

Strange caveats exist, like @mcduff explained above. It would be interested if there is somewhere more of the caveats are collected.

 

Link to comment

@LogMAN Good basic info. I think perhaps the biggest item on that list against property nodes was:

Negatives

  • Required to update the front panel item every single time they are called.  

But this is the same as a changing value. And Dereferences would be hardly noticeable as a speed impact. I think there is more here than the kb lets on. 

 

Link to comment
12 minutes ago, Taylorh140 said:

But this is the same as a changing value. And Dereferences would be hardly noticeable as a speed impact.

Not necessarily. Controls, Indicators and local variables have "built in logic to prevent front panel updates when continuously updating the same value. This prevents front panel re-draws". You can achieve similar results by deferring FP updates. Of course, writing to the control or indicator directly is the most efficient solution, because they "do not need to de-reference pointers, nor make copies of the data in memory".

For your specific case, perhaps you can update the UI less frequently to reduce CPU load. Displaying a new value every 100 ms is more than sufficient for most applications.

If you have a lot of graphs, pictures, etc., consider reducing the amount of data and update them only when necessary.

Link to comment
On 3/24/2020 at 4:46 PM, LogMAN said:

The front panel should be visible during the test to ensure that it is actually redrawn. It should also contain the necessary number of indicators to make both cases are comparable. If you reuse the same reference multiple times and defer panel updates, it only measures the iteration time of the For-loop + a single redraw, which is not the same as updating the UI on every loop iteration.

Here is a version of your VI that has 100 boolean indicators on the front panel and is visible during test (it is also important to have all indicators visible on screen during test): MethodPerformance.vi

On my PC the differences are much smaller with this version (if you set the number of elements to 1, the number of updates is roughly the same)

I I'm remembering everything correctly, you'll also have to be careful of which elements need to be redrawn when deferring FP updates. If you haven't deferred FP updates then toggling visibility of two controls will force LabVIEW to redraw just the area of those two individual objects but in the case that FP updates have been deferred, the visibility has been toggled, then updates are undeferred, I believe LabVIEW ends up redrawing a single rectangle which encompasses both objects (so for N objects just draw the smallest rectangle that would cover all objects needing to be redrawn).

Link to comment
8 hours ago, jacobson said:

I believe LabVIEW ends up redrawing a single rectangle which encompasses both objects (so for N objects just draw the smallest rectangle that would cover all objects needing to be redrawn).

This might vary by operating system, but I think you're correct. I have only once had reason to drill that deep into the draw manager layer of LabVIEW's C++ code. But the whole point of deferring updates is to avoid flicker, so it would make sense that LV would aggregate into a single rectangle and render that as a single block... if it tries to do all the small rectangles, that's probably (my educated guess) the same flicker that would've occurred if defer never happened. 

Link to comment
On 3/25/2020 at 8:48 AM, Neil Pate said:

Joking aside, if I am updating that many controls that the GUI is slow to paint it is probably symptomatic of a bad GUI design.

Well that or some really complicated stuff is happening.  Here is a UI I've been working on lately.

921426066_SequenceDraw.png.c1a552e3a1f00b20fbfb1f3727d065ed.png

It is the view of a sequence that is currently running.  On the left is a green arrow that moves to whatever step is currently being executed.  Next to this is Goto arrows showing that a step has a condition that might jump to another step.  Then there is the tree control that shows the sequence.  In it is the step name (I blocked out many) then two columns that show some settings on the step with icons, and a column for a comment on the step.  To the right is detailed information about the step that is currently selected.

I'm welcome to have some feedback but here is how I made it.  The green arrow is in a pane by itself.  The arrow is 2D picture control constant, and the position of the control is changed but the value of the control never changes.  The set of Goto arrows is another 2D picture control.  This time it takes up the whole pane and is redrawn as needed.  Since the user can scroll the tree, this means we need to poll the scroll position (on mouse down, mouse move, wheel scroll, limit to 1 event) and if it changed redraw the arrows.  The While Loop can be collapsed and if that happens, the green arrow needs to point to it since the current step is in the loop.  In this example if the while loop is collapsed, then the Goto arrows need to be removed.  This is done by reducing that pane size to 0, making more room for the sequence tree.  The tree is set to fill the next pane.  The icons in the tree are two separate 2D picture controls (since icons can't be in multiple columns easily, and their size is 16x16).  The position and value of them are dependent on the scroll positions of the tree.  To reduce weird UI stuff when resizing, the 2D pictures must be aligned to be at the top of the tree control.  The 2D pictures aren't transparent because doing so means the Erase First option must be set, which ends up flickering when scrolling since these need to be changed on scroll, window resize, or collapsing of the while loop.  Because of not being able to be transparent, and wanting to align to the top, the 2D pictures actually contain the headers as part of their pictures.  Maybe I could have done this with another pane.  When scrolling too far right or left we want only partial column icons to be drawn so the column width is checked on scroll to ensure it looks right.  Also since the pictures aren't transparent, the blue background of an icon is separate image.  Oh and the scroll wheel works, and will mess with lots of these things.

This mostly works well.  But as you might guess it can take some time to draw all these things, figure out where they should go, and get it looking right.  At first I had the quick and dirty of on any Mouse Move, Mouse Down, or Wheel Scroll event, just redraw everything.  This meant tons of events firing all the time doing mostly nothing, and some times flickering the screen.  So then I started looking at ways to improve it.  If there is a value change on the tree, we don't need to update the Goto arrows at all, just the icons.  Other information can be cached too to help with performance.  Maybe we could draw all Goto arrow permutations on start, and then switch between them as needed then we wouldn't have to draw them.  We would only need to move them vertically as needed.  This is the kinda thing I was talking about when I said diminishing returns.  Right now drawing that goto arrow is probably 10ms or so.  But by complicating the code we could bring it down to 1ms.  Is it necessary?  Well no but if the total time of doing things adds up to be too much we could look into that to reduce the performance.

Oh and to add to the complicated nature of this, this is the same UI that is used to create the sequence, with dragging and dropping and moving steps around, dragging Goto arrows around, and clicking icons to perform other actions like sliding in another subpanel, or changing the icons.  In all of this, the defer panel updates is only used once, and it is on a full refresh which only happens on first load.  Everything else we just update what we need and it has been pretty responsive.

Link to comment

@hooovahh I have to agree, some UI's just do fancy things, and I am a sucker for a good looking UI. Keeping them synchronized with scrolling can definitely be a challenge. (Mouse Move and Wheel Scroll event update like crazy, Do you Limit the maximum events?)

Let me know if you find a perfect way to handle scroll synchronization. :)

 I've used the picture control in the past to draw run-time configurable controls. It can be a resource hog if not carefully pruned. (I always hear about how the picture control is so fast and lean, but In my experience its only true for simple items with only a few <100 draw instructions.)  

So many UI things. It is usually the most time consuming part. (for the people and the CPU)

image.png

Link to comment
1 hour ago, Taylorh140 said:

Let me know if you find a perfect way to handle scroll synchronization. :)

Far from perfect, but what I have at the moment, is on Mouse Move, Mouse Down, or Mouse Wheel event (with the limit to 1 just like you), read the Top Left Visible Cell.  This gives the Tag and Column number.  Using a second property node, write the Active Item Tag, and Active Column Number to what was just read.  And then read the Active Cell Position Left.  I then use a feedback node to see if the Top Left tag, Column, or Cell Position Left has changed from the last time the event was fired.  If it hasn't do nothing.  If it has, then go do what it takes to either shift the current image up and down, or left and right.  Left and right are property nodes on the controls to move them, but value can stay the same.  As for shifting and image up and down, I use Norms low level code found here, which is pretty fast, but I did have to add a couple of op codes to it since his post.  Alternatively you might be able to set the image origin to get the same effect but that uses a property node, as apposed to the value of the image control.

 

  • Like 1
Link to comment

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.