Excel VBA timer performance differs depending on which sheet is active? - vba

I have a workbook with many sheets, each sheet has varying number of equations that inter-link with each other. I have a workbook wide VBA timer that is supposed to run every 5 seconds.
When I am active on a sheet that doesn't have a lot of formulas on it, it seems to run exactly once every 5 seconds... However, if I then change the active sheet to a more "busy" sheet, the vba timer just doesn't go off... Or if it does go off it was like minutes later. It is not until I switch to a less busy sheet again and then the timer runs magically like normal without having to reset anything.
I do not have any special VBA code specifically on the busy sheets... and I can't see why the VBA timer code won't run consistently across all sheets? If the timer is to be affected, it should be affected across ALL sheets and not only certain ones.
Here is the VBA timer code:
Sub TimerTick()
On Error GoTo ErrorHandler
If toggletimer = True Then
RunMyCode
runWhen_ES = Now() + TimeValue("00:00:05")
Application.OnTime EarliestTime:=runWhen_ES, Procedure:="TimerTick", Schedule:=True
End If
ErrorHandler:
End Sub

The issue is Excel has a hard time running a macro and letting other processes run. Add this after you specify your wait condition:
DoEvents
Excel will release the resources and threading used for running the macro instead of churning on it the whole time the wait is going on. This should allow your spreadsheet to function normally between wait cycles.

Related

Power Query Tables won't download data until VBA finishes executing

I have a spreadsheet with 4 worksheets, each one connected to a power query which connects to an OLEDB database and runs a select to import data into the workbook.
I am trying to automate the refresh of the queries.
I have VBA in the workbook which
- disables background refresh,
- runs Workbook.RefreshAll,
- resets the background refresh to true (as I need this for the automation software to be able to run it in the first place) and
- then waits 30 seconds to give the automation software a bit of time before it starts saving the workbook.
All the software does is call the macro in the workbook and then save following execution.
The queries correctly execute one after the other and I can tell the data in the tables has refreshed.
The power query confirmation that the rows have been downloaded happens after the VBA completes executing, so the automation software saves it before this is done and Excel reports the download as having failed. This isn't a problem when everything works, but we won't know when the download has truly failed, unless I add in some sort of a row count comparison which is really a workaround rather than an actual solution.
I have tried creating two subs in the workbook, one to do the refresh and then another which calls the first sub and then waits 30 seconds, I was hoping the download would complete after the first sub finishes executing, but this doesn't happen. I have tried refreshing one connection at a time instead of using RefreshAll, tried refreshing the actual power query tables instead of the connections and have tried refreshing with background query set to true and then looping until the refresh is marked as complete (which works if you step through it, but crashes if you just run the VBA in full).
I also tried saving the spreadsheet at the end of the VBA, but it still waits until the save is complete to update the power query status.
My latest VBA:
Sub Workbook_RefreshAll()
Application.DisplayAlerts = False
For Each objConnection In ThisWorkbook.Connections
'Temporarily disable background-refresh
objConnection.OLEDBConnection.BackgroundQuery = False
objConnection.OLEDBConnection.MaintainConnection = False
Next
ActiveWorkbook.RefreshAll
For Each objConnection In ThisWorkbook.Connections
'Re-enable background-refresh
objConnection.OLEDBConnection.BackgroundQuery = True
objConnection.OLEDBConnection.MaintainConnection = True
Next
newHour = Hour(Now())
newMinute = Minute(Now())
newSecond = Second(Now()) + 30
waitTime = TimeSerial(newHour, newMinute, newSecond)
Application.Wait waitTime
End Sub
(I can't remember whether the maintain connection parameter actually helped as I as trying to force the connection to drop after each refresh, but it works so haven't taken it out.)
Instead of Application.Wait try the following:
Do until now() >= waittime
DoEvents
Loop

VBA: To calculate the worksheet in background, while message userform is active

I wrote a loop procedure in vba, which pastes a lot of data each time to the new worksheet. Actually it pastes the formulas that get calculated. It takes excel approximately 40 seconds to get all calculations done. Unfortunately the next loop does not wait 40 seconds and fills with the data the next sheet. It takes a lot of resources. To make a loop to wait a little bit, but still to perform calculations I used userform, which appears for 40 seconds, than it will be unloaded and is used in the next loop. The purpose of the userform is to allow the calculations to get done before the calculations in the next sheet begin. This strategy has a drawback, since when the useform is active, the calculations in the background freeze. One can change the options of the useform to showmodal false, but then I cannot upload and unload it each time as new loop begins. Besides I am not sure weather the formulas a still calculated in the inactive sheet. I have to mention that the formulas in the sheet each time activate an excel add-in and I cannot make an add in to wait with calculations, even if the option manual calculations is turned on.
If anybody knows how to postpone the loop but not to make complete excel application wait (Application.wait or ontime methods do not work) I will appreciate the help a lot. I tried also to active and deactivate events, switch to manual calculation and back. Also this does not help and all data a calculated at once.
The method described in the other post did not worked for me and all formulas are still calculated at the same time:
Application.Calculate
If Not Application.CalculationState = xlDone Then
DoEvents
End If
My original code that copies formulas to the new worksheet:
For raw_i = 1 To 3
Set o = GetObject("d:\formulas.xlsx")
Set Sheet_i = Worksheets.Add(after:=Worksheets(Worksheets.Count))
Sheet_i.Cells(1, 1).Formula = o.Worksheets("Sheet1").Cells(raw_i, 1).Formula
Set o = Nothing
LoadForm
next raw_i
VBA doesn't support multiple process threads - there is one execution route and if you pause it, then you pause the whole execution. It's all or nothing.
Best hope you've got is a While Loop:
While Application.CalculationState <> xlDone
DoEvents
Wend
Which will loop until calculations have finished.
In response to your most recent edit:
Try changing the main loop to:
For raw_i = 1 To 3
Set o = GetObject("d:\formulas.xlsx")
Set Sheet_i = Worksheets.Add(after:=Worksheets(Worksheets.Count))
Sheet_i.Cells(1, 1).Formula = o.Worksheets("Sheet1").Cells(raw_i, 1).Formula
Sheet_i.Calculate
DoEvents
Set o = Nothing
Next

Running a VBA Excel Macro and getting screen flickering even after rightly setting Application.ScreenUpdating to True and False

I have written a macro which is quite big and has several Sub functions. The macro pulls external data and perform Calculations on them and produces results on the sheet along with a graph.
The problem is that the macro runs absolutely fine with smaller data. But when I pull a databank of say 1500 entries, the screen starts flickering and the program hangs. It doesn't even give any error message.
Had it been any faults in loop, the program shouldn't have worked for 200 entries. But it does for a lot of datas except the very long ones. Also the size of arrays used are set up to 2000, and Application.ScreenUpdating has been turned False at the start and True at the end in every Sub.
Another important information as to how the program works:
The codes are connected to a Userform. So if one optionbutton and another entry is true, a particular process (Sub) is happening one at a time. So for each sub I have given Application.ScreenUpdating as False and True. Technically out of 15 subs, perhaps only 5 are running at a time in a module. I have kept every case in the same module as it is easier to handle that way. However, few subs run at a time.

VBA powerpoint, run timed task/macro in background across presentation

I am trying to build a presentation with a counter/number in the footer which evolves with time (as a function of some external data set present in an Excel spreadsheet). I developed the VBA code for loading data from the spreadsheet, built a footer in the Slide Master and a macro for updating the counter in it (and even graph some data in the footer), but I am unable to build the mechanism for updating the counter - e.g. every 2 seconds - across the presentation (by that I mean that the update has to happen independently of the active/view slide). I've seen a few threads with similar topics [ref, ref], which were not really answered or active, hence my question: is there a way to build a background timer for launching macros in VBA/Powerpoint?
Thank you and best regards,
Cedric
Excel has an Application.OnTime event to handle this but I can't see one in Powerpoint. Not sure what your setup is but if you have an Excel sheet open as well you could use Application.OnTime from Excel to run the Macro in Powerpoint
Something like
Excel
Application.OnTime Now + TimeValue("00:00:15"), "my_Procedure"
Public Sub my_procedure()
Dim PPObj As Object
Set PPObj = CreateObject("PowerPoint.application")
PPObj.Run "myPPTFile.ppt!MyMacro"
End Sub

Run macro if button is not clicked in certain amount of time

I have 3 spreadsheets that I am auto-opening every morning using the task scheduler. Upon opening, I have used VBA to automatically update, save, and then close each file.
The code to do this works perfectly, but causes some hassle if I want to open the spreadsheets to edit them (I have to open them specially to not run the macro and therefore automatically close). I want to be able to open the spreadsheets normally for editing without them closing automatically.
A possible solution is to have a MsgBox pop up. If the MsgBox is not acknowledged within 15 seconds (or so), then the file automatically closes. If the MsgBox is acknowledged, then the file does not close.
Does anyone know how to do this?
First
Create a sub routine with name (Close) with following code
Unload UserForm1
Second : call that routine after 15 seconds
Private Sub UserForm_Initialize()
tmeKill = Time + TimeValue("00:00:15")
Application.OnTime tmeKill, "Close"
End Sub