I want to trigger a variable from false to true when my event is done. Is there like an "on Exit" action area for events? There is only "action" and the variable does not change. Or is there a best way to change my variable?
There is no OnExit code field for Event, as these code fields are only used on process modelling blocks (such as Source, Delay, .....). These blocks typically have a flow through them and give the user the opportunity to trigger actions at certain points of time in this flow.
An Event, on the other hand, is not part of a process flow. It is a simple trigger, like an egg-timer, to execute a piece of code. This piece of code is executed at the exact time when the event triggers, while the simulation time is paused. So when you want something to happen after the code you defined in your Event, just add it at the end of the code.
I attached a screenshot to show you how it could look like to change a variable by an Event:
Related
I am currently working on a LabVIEW project and have found myself stuck on how to make a while loop exit when I press the abort (stop) button. For a simple while loop I understand how to do this - but the problem is that this while loop is nested inside of an event structure and I'm guessing that the button cannot be pressed while the loop is executing. Attached here is a picture of part of my code (that contains this specific event case which is causing me problems): To spend a little more time explaining what the problem is - I think the code is doing what I want it to do (namely output a set of commands in a repeated cycle with a wait timer) but I cannot stop the code mid cycle (pressing the abort button with my mouse does nothing - as in the button doesn't show it being pressed and the indicator shows no change, I also can't use any other functionality of my program during the cycle which I'm assuming is related). And I do not want to stop the LabVIEW program from running entirely - just the code inside the while loop pictured above. This is what the front panel is configured too for completeness:
Essentially what I want to happen is the while loop to execute when I press DWG and in the middle of the cycle be able to abort it. Sorry if my code seems a little messy. Additionally, I've tried the same code with a for loop originally (via a conditional terminal so it could stop early) and that didn't work either. Thanks for any help I appreciate it!
Your problem is that inside the event structure, by default the UI is frozen so no UI actions (keyboard/mouse/etc) are processed until you exit that frame.
Option 1. You can right click the Event Structure and select "Edit events handled by this case" dialog and then uncheck the "Lock panel" checkbox -- that will allow the UI to be live while you are in that frame. I do not recommend this solution generally unless you have an extremely simple user interface because it leads to the user being able to change controls without the events behind those controls being processed (not a good UI experience for users). But if the UI is simple enough, that works.
Option 2. You can create a user event that is the code you want inside your While Loop. When the Deg Wait Go button is pressed, use the "Generate User Event" node to trigger that event. Do the same thing in the user event case so that the event re-triggers itself if and only if the Abort button has not been pressed.
Option 3. Create a separate loop OUTSIDE your UI loop that does your processing with some sort of command queue running between the UI loop and that other loop. The other loop moves into various states at the request of the UI loop... it's the one that does nothing until it receives a "go" message and then keeps looping until it receives a "stop" message. You can Google "queued message handler" for extensive details of this solution. This is the most common solution for complex UI requirements, especially useful for separating concerns of the UI code from the execution code.
My event structure in the following part of my VI will work when I start up the program, but never again until I stop and restart. I figure I'm not doing something simple, can someone help?
The event structure is in a while loop. Again, it works once, but not after that...
You have set Enable Beeper Value change action. It will occur once you change the value of the button from the front panel, or change the value trough property node with signaling. Changing value using a local variable or property node value property will not cause Event Handler to register the event.
It seems to me your case structure is fine.
When you press "enable beeper" the event structure should execute the case you have shown and enter the case structure in the True frame.
When you press the button again, the event structure should be executed again this time processing the False frame of the case structure.
(I assume, since you have a local variable, that "enable beeper" is not latched).
If this is what you want to happen but not what's happening the issue could be somewhere else.
Is the Read from Visa function working properly? Could it be waiting for a reply from the hardware?
I am using simple event handler in the while loop.
I have value change event for the boolean button. There is some code that takes 3-4 seconds to execute.
The problem is I am not able to click anything on my Front panel during this period. Is it possible to allow the user to click on other controls when event handler is working on some case (as I understand the event handler is able to collect all events and process them ASAP)?
I fully agree with Mikhail N Zakharov's answer, but anyway your problem can be easily solved by just unchecking the checkbox named Lock panel until the case for this event complates
Please see screenshot below.
PS. Once again it is not the best practise to make event structure to work for 3-4 seconds.
I think you need to restructure your application to make it more responsive. LabVIEW best development practices suggest keeping event handler code as fast as possible. One of the ways to handle this would be to send a message into the queue on the change of this Boolean control and process the queue in a separate loop.
I am running a lengthly task in an Action and I would like to have a display of where I am at. For that I created a Text Field and I tried it with setStringValue:
[textField setStingValue: [NSSting stringWithFormat:#"%ld",currentValue]]
The code works but unfortunately it is not updating the NSTextField after every iteration but rather when the whole Action is done.
What am I doing wrong?
This is because applications with the Cocoa framework use an event loop to perform operations, and events occur in a completely serial fashion.
An event is basically any kind of action that the framework designer could not predict or found convenient to have run in a delayed manner. Since you can't predict when clicks will be performed, they need to be considered events; and for efficiency reasons (since you don't want to repaint a component multiple times if you don't need to), the repaint actions are events too.
Your action runs in response to a user event (for instance, a click on a button is an event) and therefore blocks all other events waiting in the queue until it's complete. However, components are repainted in response to a different, framework-triggered event, and as such the text field must wait until your action completes to repaint itself. This is why you cannot visually change the value of a text field from inside an action.
In order to notify your user of the progress of your task, you'll need to run it on a different thread. There's a lot to say about threads, so you should probably read some about them. I'm also sure that there are plenty of examples of how to run a long action in a background thread and update the UI accordingly for Cocoa all over the Internet.
When you click on a UI component, and it enters the Action block, the code is running on the main thread, the same thread that is painting the UI. If you run a long running operation in that block, it isn't going to paint until you are done because it is busy doing whatever you have it doing - you have hijacked the paint thread.
As said elsewhere, you need to spawn another thread, and then have the new thread perform the long running operation, and occasionally send messages to have the UI be updated by the main thread.
As a next step, go read the Apple documentation on NSThread, specifically:
+ (void)detachNewThreadSelector:(SEL)aSelector toTarget:(id)aTarget withObject:(id)anArgument
Be aware that threading is a non-trivial domain area, and be ready for some wierd behavior if you aren't careful.
I have a vb.net based windows application, where when "GO" button is clicked a bunch of data is loaded into DB. So in my application as soon as "GO" button is clicked I want to just disable it and would like to enable it back when the uploading has completed.
Now in my specific method for btnGo_Click() I have:
btnGo.Enabled = False
as first line and
btnGo.Enabled = True
as last line in the same method.
But I fail to understand why the "GO" though appears as being disabled still allows click when processing is going on. Also if I remove the last line, it gets disabled permanently and doesn't allow the click event.
Kindly suggest what am I doing wrong?
Edit (Dated: 25th Jan 2012): I made changes as suggested by our collegues, but I am facing a new issue here. I am facing an issue where the textbox gets updated but not always. I have updated my textbox in "_ProgressChanged" event of the background worker thread. In my case if there is 10 records uploaded. Then there are 10 lines of updates that are expected in the texbox. But only few lines are shown in the textbox. Is it the repaint issue again? Kindly suggest...Because all other things are done as per your suggestion
You're not doing anything wrong. The problem is that the UI doesn't get updated until the code inside of your event handler method finishes executing. Then, the button is disabled and immediately enabled in rapid sequence.
That explains why if you forget to reenable the button control at the end of the event handler method, it is still disabled—because you told it to disable the button in the first line of the method.
This is a classic case of why you should never perform long-running computational tasks inside of an event handler method, because it blocks the UI from being updated. The computation actually needs to happen on a separate thread. But don't try to create the thread manually, and definitely don't try to update your UI from a separate thread. Instead, use the BackgroundWorker component to handle all of this for you automatically. The linked MSDN documentation has a great sample on how to use it.
Disable the button before starting the BackgroundWorker, and then re-enable it in its Completed event, signaling the completion of your database load.
Since you're trying to execute a function that can take some time, I'd advise you to make use of threading. In .NET there's a BackgroundWorker component which is excellent for performing tasks asynchronous.
On button click, invoke the BackgroundWorker like this:
if not bgwWorker.IsBusy then
btnGo.enabled = false
bgwWorker.RunWorkerAsync()
end if
And use the completed event to enable the button again:
Private Sub bgwWorker_DoWork(ByVal sender As System.Object, _
ByVal e As System.ComponentModel.DoWorkEventArgs) _
Handles bgwWorker.DoWork
' Do your things
End Sub
Private Sub bgwWorker_RunWorkerCompleted(ByVal sender As System.Object, _
ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) _
Handles bgwWorker.RunWorkerCompleted
' Called when the BackgroundWorker is completed.
btnGo.enabled = true
End Sub
In the example above, I've used bgwWorker as the instance of a BackgroundWorker.
The button click event is handled as soon as the UI thread has idle time.
After you disable your button, the UI thread is keept busy by your code. At the end of your method, you re-enable the button, and after that you exit the method and allow for idle time.
As a consequence, the button will already be enabled at the point in time where the click event is handled, so your click is "recognized".
The solution is, as others already suggested, to use a Backgroundworker.
Dont try to use doEvents() as a solution (never do), since this would be prone to introduce other subtle problems. That said, you can prove the above explanation with some experimental doEvents in your code. You will see that the click is discarded if a doEvents is performed before the button gets re-enabled. On the other hand, performing a doEvents directly after the button.disable (to "update the GUI") will not help if it is executed before the click.
If your btnGo_Click() is ran inside main thread, UI could not be updated correctly inside a time-consuming task.
The best way you can do what you need is running your method in a BackgroundWorker.
I just tried disabling a button, Updateing the form, Sleeping, and enabling it again. It still performed the click (A click that was done while it "slept" with the button disabled) after it was enabled.
I guess forms "remember" clicks.
(EDIT: I did this in C#.)
It's usually not a good idea to manage the state of a submit button. Instead, perform validation on submit.
I would like to add 2 enhancements to the answer generally described here which is to 'do the work in another thread'.
Ensure button.enable=true always gets called
1.a. You should use a try block in button_click . If there is an error in launching the thread, CATCH should re-enable the button.
1.b. The task complete call back should also ensure the button is enabled using try/catch/finally
1.c The task timeout should also re-enable the button
A common error based on exactly the situation described here is rapid-clicker-person clicks the button twice in rapid succession.
This is possible because its possible, even if unlikely, that 2 click messages get queued and processed before the button is disabled. You can not assume the events happen synchronously.
IMHO a best practice is to use a static variable. Initialize it to 0. Set it to one as the very first statement and set it to 0 following the guidelines in POINT 1.
The second statement in button click should simply RETURN/EXIT if the value > 0
If you are using a worker thread, the static variable may have to be located in that code. I would not advise making it a form level variable.
I had a slightly different issue not being able to call click.
I have a routine that turns the UI on/off based on a validation routine.
i will say that I disagree w/ the suggestion to do validation in the submit. The button should not be enabled if we are able to tell the form is invalid.
My issue was that I was calling the validation from several places. One of the calls was the CustomCellDraw event of a grid which was firing very frequently.
So while it appeared that I was simply disabling/enabling the button a few times, I really was doing this almost continually.
I was able to trouble shoot by placing a label on the form and kind of doing a console.log thing. I immediately realized button.Enabled was flickering, which led me down the correct trouble shooting path.
I realize this addresses a different root cause than op described. But it does address the problem the op describes.