LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Moving Objects with Mouse using Dynamic Event Registration

This post was created as a continuation for the digressing discussion on Darren's Weekly Nugget 04/19/2010.

 

The following information is the way I understand dynamic event registration. If you know more than I do or need to make corrections, by all means... 

 

tbob,

The method used was dynamic event registration/unregistration for moving the mouse. The motivation for dynamic event registration vs. static registration is mostly behind the "Mouse Move" event on the Pane. The mouse is going to move a lot on the pane, but only when we are actually "dragging" do we want the event to respond.

 

By initially wiring a null ref into the first "Register for Events" node, we create a "placeholder" for events that we know we want to eventually use in the program. But for now, they're "off". We don't turn "on" the Mouse Move or Mouse Up events until the Mouse Down event has occured on the ruler (actually, a static event registration would have sufficed for the "Mouse Down" event, that's arguably the first flaw in my program).

 

The second flaw in my program (my fault) is that the null ref for the Pane in the initial Register for Events node has a label called "Ruler". "Show Label" on that ref and change it to "Pane". Now the event registration headers in the Event Structure will say "<Pane>: Mouse Move" rather than the misleading "<Ruler>: Mouse Move".

 

Once the "Mouse Move" event has been registered for the Pane, you notice there's a property node in there. PN's are painfully slow, since they force a thread swap to the UI thread. (Watch your CPU while you are dragging the ruler - I wouldn't be surprised if you see 20% or more utilization on a core). This is another reason we only want the Mouse Move event registration responsive only while we're dragging, and not all the time.

 

Finally, when a "Mouse Up" has been detected, that signals we no longer need to respond to the Mouse Move events, so wiring another Null ref (of the same type) into the Register for Events node cancels that previous registration's event queue, trashing any events that have not yet been handled. (That's an anecdotal observation, can someone confirm?)

 

Your original question involved the difference between the references vs. the class specifier constants. My best understanding is that they are virtually the same thing, it's just that one is linked to a valid object, and the other is simply a type specifier for the ref of an object. Basically, one has a type and a "value" (link to the object), and the other is just a type with no "value". 

 

The third flaw in my program is a usability flaw. If you Mouse Down on the ruler, drag your cursor out of the window, release the mouse, and then hover your mouse back over the window, you will notice you are still moving the ruler around even though you are not still "dragging" with the mouse held down! That's LabVIEW's flaw that I cannot fix (without calling into the mouse functions under Input Devices and setting up my own polling loop.) I would like to see a "User Interaction Ended Event" for handling flaws like this.

 

As a final note, if I remember correctly I learned a lot of what I know about event registrations by reading some of Ton's posts.

 

Hope this helps a bit, and hopefully someone who knows more than I do will pipe in with corrections so we can all learn something.

 

I have attached a VI with the improvements.

 

TransparentRuler2.png

Message 1 of 32
(6,382 Views)
Yes, Ton's post about dynamic event registration. It seems we just missed the third anniversary of that gem.
Message 2 of 32
(6,372 Views)

JackDunaway wrote:

 

The third flaw in my program is a usability flaw. If you Mouse Down on the ruler, drag your cursor out of the window, release the mouse, and then hover your mouse back over the window, you will notice you are still moving the ruler around even though you are not still "dragging" with the mouse held down! That's LabVIEW's flaw that I cannot fix (without calling into the mouse functions under Input Devices and setting up my own polling loop.) I would like to see a "User Interaction Ended Event" for handling flaws like this.


 

 You can use the VI.Mouse Leave event to unregister.

 


As a final note, if I remember correctly I learned a lot of what I know about event registrations by reading some of Ton's posts.


Specifically, this nugget about dynamic events can be help.

___________________
Try to take over the world!
0 Kudos
Message 3 of 32
(6,371 Views)

And as a final note, I'm a shifter, not a tunneler for this sole reason: the zero iteration For Loop. If you iterate a For Loop zero times and only use tunnels for the refs, you will end up with a null ref on the other side!! But if you shift the ref, you will maintain the valid reference.

 

That's one other difference you'll see between my code and the original nugget. I have never heard this advice on the forums, I have just learned it the hard way. Smiley Indifferent

 

ShiftVersusTunnels.png

Message 4 of 32
(6,362 Views)

tst wrote:

 You can use the VI.Mouse Leave event to unregister.


Well, you can, but that's not always the desired behavior. If your mouse leaves the pane with the button still held down, and then re-enters the pane with the button still held down, you would expect the drag to still be in progress (at least I would).

 

Screwed if you do, screwed if you don't, just have to pick the way you wanna be screwed without the User Interaction Ended event.

Message 5 of 32
(6,358 Views)

JackDunaway wrote:

Once the "Mouse Move" event has been registered for the Pane, you notice there's a property node in there. PN's are painfully slow, since they force a thread swap to the UI thread. (Watch your CPU while you are dragging the ruler - I wouldn't be surprised if you see 20% or more utilization on a core). This is another reason we only want the Mouse Move event registration responsive only while we're dragging, and not all the time.


There are ways to reduce the CPU consumption, all of which basically involve minimizing the amount of WORK by not doing WORK whenever the event occurs, but limiting it to a lower frequency:

 

 Example_VI_BD.png

 

In this case, the actual work is done in the timeout case, which is only called 500 ms after the last value change and then resets the timeout. There are other options, but those are left as an exercise to the reader. If you want an example which is more suited to what's shown in this thread, look at the panning process of the code capture tool.


___________________
Try to take over the world!
0 Kudos
Message 6 of 32
(6,347 Views)

 


JackDunaway wrote:

 

...If you iterate a For Loop zero times and only use tunnels for the refs, you will end up with a null ref on the other side!! But if you shift the ref, you will maintain the valid reference.

 

...I have never heard this advice on the forums, I have just learned it the hard way. :smileyindifferent:

 

ShiftVersusTunnels.png


 

Jack-

 

Thanks in general for investing time to make nice images (...a simple diagram being worth more than 1000 lines of code and all...).  Here are a few others who may have discovered this the hard way:  link, link, link, link, ...

 

Feeding that For Loop with an empty array constant might further (better?) illustrate the concept for posterity (at least that's how I got bit). 

 

Keep up the good work...


Certified LabVIEW Architect
TestScript: Free Python/LabVIEW Connector

One global to rule them all,
One double-click to find them,
One interface to bring them all
and in the panel bind them.
0 Kudos
Message 7 of 32
(6,330 Views)

Excellent, thanks for all the links! The reason I said "I have never found it on the forums" is because I have never looked for it! Once I discovered the source of that bug (however long ago it was...), it was very evident what was happening and needed no further forum investigation.

 

Yes, agreed, autoindexing is the real biter here (who wires 0 to N anyway?), especially on unwired input arrays for SubVI's.

Message 8 of 32
(6,324 Views)

Thanx jack.  The part I didn't know about was that the Class Reference is essentially a Null Reference.  Now it makes perfect sense.  Using a Null reference for a placeholder.  Brilliant.

 

About the shift registers in a 0 iteration For Loop, I learned about that in a LV Intermediate class several years ago.  I thought I had made a reference to that in one of my posts at some time.  I guess you just missed that one.  Nobody wires a 0 to N, but sometimes the value wired to N is a product of some other function, which could turn out to be 0.  Another case is if an empty array is wired in with indexing enabled.  Both of these will cause 0 iterations.  I use the shift register on error wires also in all of my For Loops.  I would not want to lose an error if there was one coming into a 0 iteration loop.  Without shift registers, the output would be no error.

 

Being in the test and measurement world, I have never had to use dynamic event registration.  But its a good thing to know.  Now I know that a Class Reference is a Null reference.

 

- tbob

Inventor of the WORM Global
0 Kudos
Message 9 of 32
(6,292 Views)

 


tst wrote:

 

There are other options, but those are left as an exercise to the reader. 



Were you thinking of unregistering while handling the event that you caught and then re-registering?  I've had success with this when there's a slider controlling a DAQmx waveform analog output (otherwise we get way too many value change events to stay responsive to the slider).

 

 


tst wrote:

 

___________________
Try to take over the world!



Allusion to the Brain, LabVIEW Everywhere (Touch Panel, PDA, RT, FPGA, ADI Blackfin, ...), or both?


Certified LabVIEW Architect
TestScript: Free Python/LabVIEW Connector

One global to rule them all,
One double-click to find them,
One interface to bring them all
and in the panel bind them.
0 Kudos
Message 10 of 32
(6,253 Views)