How to manage function when use concurrent in livecode? - scope

I have two graphics. These graphics have countdown function. When open card these graphics are start countdown. When countdown reach 0 these graphics are call function "refresh". These graphic are call function in same time. How to manage it?
Here my code on card for graphic use function:
on refresh
if eCount is not empty then
add 1 to eCount
else
put 0 into eCount
end if
wait 300 milliseconds with messages
if eCount >= 2 then
--dosomething()
put empty into eCount
end if
end refresh
Update ---
local eCount
on refresh
add 1 to eCount
if eCount >= 2 then
--dosomething()
put 0 into eCount
else if eCount = 1 then
--dosomethingOnce()
put 0 into eCount
end if
end refresh
When two graphics have call function "refresh" in same time. It's call method "--dosomethingOnce()". How do I fix?
And here my graphic code.
on countDown countT
if countT > 0 then
send "countDown countT" to me in 1 secs
else
send "refresh" to card "Main"
end if
end countDown

Here's another idea that I think may solve your problem (if I understand correctly!)
local eCount, sPending
on refresh
add 1 to eCount
if not sPending then
put true into sPending
send "doSomething" to me in 100 millisecs
end if
end refresh
on doSomething
if eCount = 1 then
-- execute code for 1 timer
else
-- execute code for more than 1 timer
end if
-- reset flag
put false into sPending
put 0 into eCount
end doSomething
If the two timers finish within 100 millisecs of each other then the code for multiple timers will execute - otherwise the code for 1 timer will execute. Adjust the 100 millisec setting to whatever you need.

My understanding is that you want something to happen only when both of the timers have called the refresh function. Try the following ..
local eCount
on refresh
add 1 to eCount
if eCount >= 2 then
--dosomething()
put 0 into eCount
end if
end refresh
by declaring eCount as a script local variable, before the handler, it will retain it's value. When refresh is called the second time, the 'do something' code should execute.

Related

Application Wait time issue

I am stuck on this bit and not sure how to get past it. I need to be able to pause and the resume my code. I went with this idea to place a value into a cell, which the Application Wait time will use.
This bit is fine and works
Dim i As Integer
Dim Response As Integer
Sheets("Sheet3").Range("Z1").Value = "01:00:00" 'will place 1hr wait time in Sheet3 Z1
t = CStr(Range("Z1"))
Application.Wait (Now + TimeValue(t))
I then have a CommandButton2 on my userform, which will reset the wait time from 1hr wait to 2 seconds. In theory this should then resume the code from where it left off. The wait time is between the IE opening and loading and the FOR EACH loop running, so once it has resumed it would action the FOR EACH loop of the code.
Sheets("Sheet3").Range("Z1").Value = "00:00:02" `Reset time to 2 sec from 1hr
MY PROBLEM
I can not click on button2 when the wait time is running. Is there a way I could do this so that when the wait time code is running, button2 is still enabled to use?
BACKGROUND
User click commandbutton1 and code runs, IE opens and navigates to the correct page. NOW the code waits. As it gives the user time to select from several options (Too many to put in a code). Once done the user click the second command button which resets the code to 2sec and they can extract their information. In theory it should continue, however I can not click on the second button while the wait code is running. Is there a work around?
I have tried to split the code into subs, but I was having several error messages and I gave up on that method. I did look at a doEvent loop, but I am limitted in coding and was not sure how it would work and if it would continue the code OR start from the begining. As starting from the begining is not how I want to do it.
Thanks in advance.
POST EDITED
This is the code now, not 100% sure if it is correct, it now allows me to change the value of Z1 via button2 but the FOR LOOP does not run
Before
After button2 is clicked
'#################### APPLICATION WAIT ################
Dim i As Integer
Dim Response As Integer
Sheets("Sheet3").Range("Z1").Value = "01:00:00" 'will place the 1hr wait time in Sheet3 Z1
t = CStr(Range("Z1"))
'Application.Wait (Now + TimeValue(t)) 'commented out or it freezes excel
WaitUntil = Now + TimeValue(t)
Do While Now <= WaitUntil
DoEvents
Loop
'#################### APPLICATION WAIT ################
'FOR LOOP
For Each element In elements
DoEvents
```
Application.Wait() pauses the entire application, not just your code. You should be able to achieve the intended result using a loop with DoEvents:
...
WaitUntil = Now + TimeValue(t)
Do While Now <= WaitUntil
DoEvents
Loop
...
EDIT:
The DoEvents loop doesn't work because you set the value of t only once, so when your button2 changes value of Z1 to 00:00:02, the value of t is still 01:00:00. To fix this, you can reset value of t inside the loop:
...
WaitUntil = Now + TimeValue(t)
Do While Now <= WaitUntil
DoEvents
If t <> CStr(Range("Z1")) Then 'If value of Z1 changed, update t
t = CStr(Range("Z1"))
WaitUntil = Now + TimeValue(t)
End If
Loop
...
I have managed to fix the problem, should anyone ever need this in the future here is the work around. Also a very big Thankyou to Kirill Tkachenko for putting me on the right path
I done it with a DoWhile Loop. This part of the code went in between browser (IE) is fully loaded and before the FOR EACH LOOP, then this code is triggered.
THE CODE
It places a 1hr wait time in Z1, then THE DoWhile loop runs.
When Button2 is clicked The time changes to under 3sec and the sheet recalclates
column Z.
Then the if statement kicks in and as the DoWhile states the time should be more
than 3sec and Z1 now is 2sec the GOto runs next and goes to the lable I
created to tell the code to start here. That is just before the FOR
EACH loop
'#################### APPLICATION WAIT ################
Dim i As Integer
Dim Response As Integer
Sheets("Sheet3").Range("Z1").Value = "01:00:00" 'will place the 1hr wait time in Sheet3 Z1
t = CStr(Range("Z1"))
WaitUntil = Now + TimeValue(t)
Do While Now > "00:00:03"
Sheets("Sheet3").Columns("Z").Calculate
If Sheets("Sheet3").Range("Z1").Value = "00:00:02" Then
GoTo StartForLoop_Restart ' use GOTo command and label to reinitiate the sub
End If
DoEvents
Loop
'#################### APPLICATION WAIT ################
'Restart the code HERE, this is the key part
StartForLoop_Restart:
'FOR LOOP
For Each element In elements
DoEvents
This is what I put in Button2
Sheets("Sheet3").Range("Z1").Value = "00:00:02"
Sheets("Sheet3").Columns("Z").Calculate
I know its not the best code in the world and could be written better, but the workaround works and hopefully someone can do a much better job in writing it in the future.

Wait until specific elements loaded

I am trying to scrape a website and there are many elements I am dealing with. I need to wait for elements to be loaded.
This is my try till now but I am waiting a long time and sometimes I get errors.
.FindElementById("ContentPlaceHolder1_Button1").Click
.Wait 2000
GoBack1:
Set elePrint = .FindElementById("IconImg_CrystalReportViewer1_toptoolbar_print", timeout:=20000, Raise:=False)
If elePrint Is Nothing Then
Application.Wait Now() + TimeValue("00:00:01"): GoTo GoBack1
Else
elePrint.Click
End If
GoBack2:
Set eleExport = .FindElementById("theBttnbobjid_1545642213647_dialog_submitBtn", timeout:=20000, Raise:=False)
If eleExport Is Nothing Then
Application.Wait Now() + TimeValue("00:00:01"): GoTo GoBack2
Else
eleExport.Click
End If
Is there a better way for doing this?
This is the html part
<tbody><tr valign="middle"><td height="21" width="5" style="background-image:url('aspnet_client/system_web/4_0_30319/crystalreportviewers13/js/crviewer/../dhtmllib/images/skin_standard/button.gif');background-position:0px 0px;"></td><td id="theBttnCenterImgbobjid_1545656314367_dialog_submitBtn" align="center" class="wizbutton" style="padding-left:3px;padding-right:3px;background-image:url('aspnet_client/system_web/4_0_30319/crystalreportviewers13/js/crviewer/../dhtmllib/images/skin_standard/button.gif');background-position:0px -42px;"><nobr><a id="theBttnbobjid_1545656314367_dialog_submitBtn" href="javascript:void(0)" class="wizbutton" role="button">Export</a></nobr></td><td height="21" width="5" style="background-image:url('aspnet_client/system_web/4_0_30319/crystalreportviewers13/js/crviewer/../dhtmllib/images/skin_standard/button.gif');background-position:0px -21px;"></td></tr></tbody>
You can try looping your elements until they become available.
On Error Resume Next
Do While .FindElementById("theBttnbobjid_1545642213647_dialog_submitBtn") Is Nothing
DoEvents
Loop
On Error Goto 0
I would also consider adding a timer to your loop if for some reason the the webpage hangs - which if this is the case you could reload the webpage or perform some other error handling operation.
You can shorten this to
Do
Loop While .FindElementsByCss("#theBttnbobjid_1545642213647_dialog_submitBtn").Count = 0
Though you should set a timeout
Const MAX_WAIT_SEC As Long = 10
Dim t
t = Timer
Do
If Timer - t > MAX_WAIT_SEC Then Exit Do
Loop While .FindElementsByCss("#theBttnbobjid_1545642213647_dialog_submitBtn").Count = 0
There won't be an error and there is no need to yield control. If the item is not found the .Count will be zero.
If the id is dynamic and you have a constant substring (which only occurs for one id attribute on the page (to be safe - may not be essential) you can use ^, *, $ operators in css). For example, if the start string is constant across pages change the css selector to
[id^='theBttnbobjid']
If occurs more than once, and the index is constant across pages then use the index to interact later e.g.
.FindElementsByCss("[id^='theBttnbobjid']")(2)

VBScript not doing what's needed

Dim checkon, check
checkon = 1
toggle = 0
Do While checkon = 1
If (Chr(window.event.keyode = "m")) and toggle = 0 Then
toggle = 1
MsgBox "working"
End If
If (Chr(window.event.keyode = "m")) and toggle = 1 Then
toggle = 0
End If
Do While toggle = 1
If window.event.button = 1 Then
MsgBox "working"
WshShell.Sendkeys ("-{F10}")
WScript.Sleep 500
End If
Loop
Loop
The goal of this is to have it be run, and constantly check for the user to press M, which alternated toggle between 0 and 1. When toggle is true, the program is supposed to check for the mouse being held down and if it is click twice a second.
Currently it's giving an error
Object required:'Window"
and before that it did simply nothing. I am aware the code looks ugly, but I pasted msgboxes as a method of debugging what's going wrong, and none of them activated.
From what I read, you can't listen to events like that in a VBScript CScript environment. Usually window.event would imply the presence of a web page, but you said you're running this from the desktop, so perhaps you copied the code from a bad example. You may need to use another language or tool to do this.

VBA stops printing values in excel

I have written a macro to take values from an external simulation tool and prints the results in excel.The simulaion tool will give values only every 30 seconds and will run for days. Hence i have given a delay in VB for 30 seconds in loop. I leave it overnight for running. In the next morning i could see none of the results were updated after certain rows.But the VBA editor header shows the macro is running and the external simulation tool is also running. The last updated row in excel is not constant everytime. Why does VBA stops printing the results in excel? Any help will be much appreciated.
Sample code:
For l = 3 To lastrow1_mul + 2
Module4.Wait 30
nv = send_data.Call
Sheets(SecondSheetName).Range(SecondSheetCol_9 & l) = Hex(nv)
dv = DT.Call
If dv = 44 Then
Sheets(SecondSheetName).Range(SecondSheetCol_10 & l) = "A"
ElseIf dv = 54 Then
Sheets(SecondSheetName).Range(SecondSheetCol_10 & l) = "B"
Else
Sheets(SecondSheetName).Range(SecondSheetCol_10 & l) = "C"
End If
Next l
Module 4:
Function Wait(PauseTime As Single)
Dim StartTime As Single
StartTime = Timer
While Timer < StartTime + PauseTime
DoEvents
Wend
End Function
Send_data and DT are external simulation tool's functions.Variable lastrow1_mul's value is getting updated around 7000 but rows in excel stops printing around 500 itself(not constant always) .
looks OK but look at using Application.OnTime shown below. Also what happens if the simulation hasnt returned data, shoudnt you build in a bigger, maybe 45 second intervals?
Application.OnTime Now + TimeValue("00:00:30"), "RunProcess"
The 'Timer' i used in the code was the culprit. Timer counts the seconds elapsed(in fractions) since midnight. So it will not update when the time becomes 00.00.00. So the wait function i wrote will keep on waiting!
Here is the solution
Function Wait()
Dim StartSecond As Single
Dim EndSecond
StartSecond = Now
EndSecond = DateAdd("s", 30, Now)
While Now < EndSecond
DoEvents
Wend
End Function

VB6 DoEvents not Working as Desired

I am working with a proprietary VB6 COM library. The library has some functions which fire and I get the results once complete through Events.
Container Class:
Private WithEvents myObj As proprietaryObj
Public status as Integer
Set myObj = new proprietaryObj
status = 1
Call myObj.DoIt1()
...
' Call back event function
Private Sub myObj_Done(ByVal Code As Long)
...
MsgBox "Finished"
status = 2
End Sub
So everything works well (verified). What I want to do is encapsulate the above code + more in a class, so that I wrap multiple functions which need to stack, or get executed consecutively after a successful callback report.
So I went ahead and did this:
Call myObj.DoIt1()
Do
If myObj.Status = 2 Then Exit Do
If myObj.Status = -1 Then Exit Do 'Error
DoEvents
Loop
call myObj.DoIt2()
I get the "Finished" dialog box, but DoIt2 never fires. In fact, if I pause my code while running debug after I see the "Finished" message, the code is currently executing the DoEvents, and the myObj.Status = 1 as if it were never touched.
It almost seems as if a different instance of the object was created for a separate thread? How do I safely and correctly wait for the event callback to fire before continuing execution with the next DoIt2() ?
You need to use your local status variable, because that is what you are setting to 2:
Call myObj.DoIt1()
Do
If status = 2 Then Exit Do
If status = -1 Then Exit Do 'Error
DoEvents
Loop
call myObj.DoIt2()
Else you could also try to set status = 2 before the call to MsgBox "Finished".

Resources