Application Wait time issue - excel

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.

Related

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)

Wait until specific value appears in selenium excel vba

I have an element with this html
<span id="ContentPlaceHolder1_Label2" designtimedragdrop="1319" style="display:inline-block;color:Firebrick;font-size:Medium;font-weight:bold;width:510px;"></span>
and after clicking Save button on the page this part changes to that
<span id="ContentPlaceHolder1_Label2" designtimedragdrop="1319" style="display:inline-block;color:Firebrick;font-size:Medium;font-weight:bold;width:510px;">تم حفظ التعديل بنجاح</span>
You would notice this value تم حفظ التعديل بنجاح ..
After that I should click another button but the problem appears when the internet connection is slow. I got the other button clicked before saving
How can I wait for the appearance of the value تم حفظ التعديل بنجاح and then after the appearance of that text >> move to the another button
Thanks advanced for help
I have tried this solution and it worked well for me
This was with the help of Ziggus' suggestion
Do Until .FindElementById("ContentPlaceHolder1_Label2").Text = "تم حفظ التعديل بنجاح"
Application.Wait Now() + TimeValue("00:00:01")
Loop
I would re-write this as you risk an infinite loop. Make it a timed loop and add in a DoEvents.
Dim result As String, testElement As Object, t As Date
Const MAX_WAIT_SEC As Long = 10 '<==adjust time here
t = Timer
Do
DoEvents
On Error Resume Next
Set testElement = .FindElementById("ContentPlaceHolder1_Label2")
result = testElement.Text
If Timer - t > MAX_WAIT_SEC Then Exit Do
On Error GoTo 0
Loop While result <> "تم حفظ التعديل بنجاح"

Update chart in Excel after every iteration of loop

I'm trying to animate a parametric curve in Excel by generating its ranges in an iterative loop. I'm using the code:
Public Sub playswirl()
Range([a8], [a8].End(xlDown)).Clear
Set cell = Range("a8")
Do While cell.Offset(0, 1) <> ""
cell.Offset(-1, 0).Copy cell
ActiveSheet.ChartObjects(2).Chart.Refresh
DoEvents
Sleep (50)
Set cell = cell.Offset(1, 0)
Loop
End Sub
The chart is cleared in the first instruction, then rebuilt one item at a time in the loop. While the code is running, I see the values in the spreadsheet changing one at a time, but the chart never updates (I see the full curve, i.e. the state before the first line of code is executed). When I ctrl-break and put the code in debug state, the chart updates to the point where the code was interrupted.
I thought that the chart.refresh would have done the trick - and added the doevents in for good measure when that didn't work - but no luck. Changing the sleep call to the Excel-native Application.Wait call doesn't help either (and is too slow in any case).
Any ideas?
I think it may not be a problem with the chart; it may be a problem with the updating system. If Application.ScreenUpdating = True (which it is by default), then here are some things you can try:
Try adding the "DoEvents" to your loop like explained here.
Try adding "Calculate" to the function. For example, write the code ActiveSheet.Calculate.
Let me know how those go.

How to change drop down value permanently

As I tried to create a automation for my company I stumbled across difficulty I cannot surpass. I have read many articles on this and other sites however I did not found answer.
So basically I have a dropdown list triggered by click on internet application my company uses. User has to change value from "Read" to "All" for 10 positions. And then application acknowledges the change and allows for form to be saved.
As I managed to write a code which clicks on specific field to activate a dropdown, and choose correct option from this dropdown. However when I loop it through all of those 10 dropdowns, not effect is visible.
There is a funny part. Whenever I put a breakpoint on "debug.print objinputs.outerHTML" and then allow for macro to continue the changes takes places and everything is allright. But without breakpoint something is wrong and all the values goes back to "Read". Have anyone knows what might be the issue here?
If you need any more informations please let me know.
Set ifrm = Nothing
Do Until ifrm.Length > 0
Set ifrm = IE.Document.getElementsByTagName("iframe")(0).contentDocument.getElementsByTagName("iframe")(1).contentDocument.getElementsByTagName("iframe")(0).contentDocument.getElementsByTagName("iframe")(0).contentDocument.getElementsByTagName("tbody")(znacznik - 1).Document.getElementsByTagName("td")
Loop
Counter = 1
For Each ele In ifrm
Worksheets("Test").Cells(Counter, 1) = ele.outerHTML
Counter = Counter + 1
If InStr(ele.outerHTML, "<td title=" & Chr(34) & "Read") Then
ele.Click
Do Until Not ele.Busy And ele.readyState = READYSTATE_COMPLETE: Loop
Set objInputs = Nothing
Set objInputs = IE.Document.getElementsByTagName("iframe")(0).contentDocument.getElementsByTagName("iframe")(1).contentDocument.getElementsByTagName("iframe")(0).contentDocument.getElementsByTagName("iframe")(0).contentDocument.getElementsByTagName("tbody")(znacznik - 1).Document.getElementsByTagName("select")(3)
objInputs.Value = "All"
Debug.Print objInputs.Value
Do Until Not IE.Busy And IE.readyState = READYSTATE_COMPLETE: Loop
End If
Next
I found a solution.
It is not the most elegant one but it works and is correct enough for my needs.
I have added
CreateObject("WScript.Shell").Popup "Just Wait", 1, "Waiting"
after value change and it did the trick. I would love to know why does it happen, and why does this "trick" solves the issue.

deactivate Excel VBA userform

I am having some macro in Excel vba and in that I am performing some functions on the excel sheets which takes around 30 seconds to complete. So I want to show a user form with a progress bar during that span of time.
I tried using userform.show in very start of the function and userform.hide at the end but I found that No action can be performed in background.
So just want to know if there is any turn around to let the processing be done in the background while the form is being displayed.
Many thanks :)
Private Sub CommandButton1_Click()
'--------------Initialize the global variables----------------
UserForm1.Show
nameOfSheet2 = "Resource Level view"
nameOfSheet3 = "Billable Hours"
nameOfSheet4 = "Utilization"
'-------------------------------------------------------------
Dim lastRow, projectTime, nonProjectTime, leaveAndOther
Dim loopCounter, resourceCounter
lastRow = 0
projectTime = 0
nonProjectTime = 0
leaveAndOther = 0
resourceCounter = 2
Set workbook1 = Workbooks.Open(File1.Value)
Sheet3Creation
Sheet2Creation
Sheet4Creation
UserForm1.Hide
End Sub
The usage of Progress Bar is to show the progress of currently running code. And I wouldn't know if anyone want to do anything with the sheet while the code is running...
Anyway if you want to interact with the sheet while Form is displaying you may try to add the following code:
UserForm.Show vbvModeless
And to update a Modeless form you must add DoEvents within your subroutine.
When you want to close the form at the end, do this:
UserForm.Unload
Here is what I would do:
Click a button to run your macro
Private Sub Button1_Click()
Call userform.show vbMmodeless
End Sub
Private Sub UserForm_activate()
Call Main '-- your macro name
End Sub
Sub Main()
'-- your code
DoEvents '-- to update the form *** important
useroform.Unload
End Sub
After OP showed his code:
Why do we need a progress bar?
When macros take a long time to run, people get nervous. Did it crash? How much longer will it take? Do I have time to run to the bathroom? Relax...
In your case I do not really see that you are using any sort of heaving codes running at the background. So adding a progress bar could make your code slow as to update it, you may be calling an extra loop... check this reference article if you really want to have the progress bar :),
You can also use Application.StatusBar to display a message.
The other is to use Timer or a littel bit more technical way would be to wrap system timer ticks and refresh/update form accordingly. In VBA Excel we don't get that lucky as for C# or VB..
VBA Macro On Timer style to run code every set number of seconds, i.e. 120 seconds.
How do I show a running clock in Excel?
How do you test running time of VBA code?

Resources