Pause VBA until #GETTING DATA Power Pivot is complete - excel

I've found a lot of questions and answers about issues that feel very close to what I'm working on, but not quite. I have an Excel workbook with a large Data Model connected to two slicers. I need to cycle through every entry in the slicer, allow the workbook to catch up on loading a large number of cube formulas, then copy one particular worksheet over into another.
I've written VBA which does all of this, but I can't for the life of me get the VBA to wait for the workbook to finish uploading before it continues with the rest of the script. I can rule out background refresh-based solutions, which don't apply to OLAP. Various solutions I've found online which recommend waiting for calculations to be complete don't seem to work, the script just barrels right through those lines. The only solution I've seen which seems to apply here involved identifying every cell which would be updated as a result of the slicer change and looping through them until they no longer say #GETTING DATA. For my workbook, this would be hundreds of cells to identify and check and feels very unsustainable.
Even telling the script to Applcation.Wait seems to wait for the selected amount of time during which the workbook pauses getting data.
Setting different values of a slicer connected to a Data Model and automating some output feels like it should be such a common task that we have a solution for it. Any help would be much appreciated.
Running Office 365
Sub generate_all_forecasts()
'Cycle through all products and push forecast values to fcst_output'
Application.ScreenUpdating = True
Dim SC_products As SlicerCache
Dim selection, product_array As Variant
Dim push As Boolean
Set SC_products = ThisWorkbook.SlicerCaches("Slicer_PRODUCT_GROUPING_WRITTEN") 'The product slicer on the Inputs worksheet'
product_array = Range("product_array") 'Named range product_array on Tbl_Codes worksheet'
For Each p In product_array 'For each product'
push = WorksheetFunction.Index(Range("fcst_push_array"), WorksheetFunction.Match(p, product_array, 0)) 'Check if the product has been selected for this run'
If push = True Then
If p = "Major Medical Plan" Then 'If "Major Medical" '
selection = Array("[Query1 1].[PRODUCT_GROUPING_WRITTEN].&[Major Medical Plan - CMM]", _
"[Query1 1].[PRODUCT_GROUPING_WRITTEN].&[Major Medical Plan - GMM]") 'selection will be both CMM and GMM'
Else
selection = Array("[Query1 1].[PRODUCT_GROUPING_WRITTEN].&[" & p & "]") 'Otherwse selection is the single product'
End If
SC_products.VisibleSlicerItemsList = selection 'Change slicer to current selection'
'This is where the script needs to pause until #GETTING DATA is complete'
Application.Run "push_to_output" 'Run the forecast update macro'
End If
Next p
Worksheets("Fcst_Output").Range("B2:B1381").Value = "" 'Clear prior month's comments'
Application.ScreenUpdating = True
End Sub
Solutions which have not worked: Wait time after change slicer in
power pivot, Getting vba to wait before proceeding, Wait
until Application.Calculate has finished
The "solution" I really don't want to use: Force VBA to wait until
power pivot finishes refreshing

Thanks to Tragamor for linking to a thread where they already had a working answer. I included the following immediately after the slicer selection in my VBA and it appears to properly wait until all data fetching is complete:
Dim CalculationState As Long
With Application
CalculationState = .Calculation
.Calculation = xlCalculationAutomatic
.CalculateUntilAsyncQueriesDone
Do Until .CalculationState = xlDone
DoEvents
Loop
.Calculation = CalculationState
End With

Find your Query Properties, and set 'Enable background refresh' to False (Microsoft use True as default).
Then in your code you need to call for RefreshAll and wait for the data to load with the DoEvents. If you want your update of data to run on open you can use this in 'ThisWorkbook' Object.
Private Sub Workbook_Open()
For Each q In ThisWorkbook.Connections
q.Refresh
DoEvents
Next
End Sub

Related

Is there a way to freeze, snapshot, and chart streaming RTD data?

I have real-time data streaming from another application into Excel and making =RTD() calls successfully in a complex workbook.
Trying to (a) compare historical data as of a moment in time to current/live data, and, (b) run stats on snapshots taken. Say I have RTD data in 4 rows and 8 columns A1:H4.
The "freeze": Is it possible to push a button (to run a macro) and render, say, row 2 (A2:H2) 'inert', so it's no longer RTD? Basically, copy then-current values and paste them into the same cells (A2:H2) as values, removing the RTD formulas so that the data stops changing at that moment.
The "snapshot": Make a copy of the 3rd row, A3:H3, and paste it into A5:H5 periodically by timer, and have the copy become 'inert', i.e., no longer RTD or linked to RTD in row 5 while row 3 remains RTD. And place a timestamp for this action in cell I5 next to the copy.
This should also theoretically enable me to chart the snapshot data. I can't get RTD data to chart - expected that chart would update as streaming data changed, but charts won't display at all. So that's a secondary but related issue. It would appear that if I can snapshot the data and make it inert, I should be able to chart.
I've tried all kinds of manual processes from paste special/values, go to / special / formulas, highlight, etc. to using a third party tool. I've looked for VBA macros and found one which I'm pasting below, but I can't get it to work (the macro runs on cells to which it's directed, does not error out, but nothing happens). And even if it did work, it would only solve part of the problem as per above.
I do not speak VBA but write enough code in other languages that I should be able to figure out what it does and mod it, if someone is able to offer the base functionality. Any help would be greatly appreciated.
'This code should go in a regular module sheet, and won't work properly if installed anywhere else. _
The next (Dim) statement must occur before any subs or functions.
Dim NextTime As Double
Sub RecordData()
Dim Interval As Double
Dim cel As Range, Capture As Range
Interval = 5 'Number of seconds between each recording of data
Set Capture = Worksheets("Sheet1").Range("A1:A5") 'Capture this column of data
With Worksheets("Sheet2") 'Record the data on this worksheet
Set cel = .Range("A2") 'First timestamp goes here
Set cel = .Cells(.Rows.Count, cel.Column).End(xlUp).Offset(1, 0)
cel.Value = Now
cel.Offset(0, 1).Resize(1, Capture.Cells.Count).Value = Application.Transpose(Capture.Value)
End With
NextTime = Now + Interval / 86400
Application.OnTime NextTime, "RecordData"
End Sub
Sub StopRecordingData()
On Error Resume Next
Application.OnTime NextTime, "RecordData", , False
On Error GoTo 0
The above code is initiated when the workbook is opened, and stopped when the workbook is closed by code in ThisWorkbook code pane.
'These subs must go in ThisWorkbook code pane. They won't work at all if installed anywhere else!
Private Sub Workbook_BeforeClose(Cancel As Boolean)
StopRecordingData
End Sub
Private Sub Workbook_Open()
RecordData
End Sub
End Sub

VBA Enable Events not working as expected

I have a worksheet_change macro embedded in two sheets within my workbook. They are there to prevent anyone making changes to the sheets. However, I still want the data within the sheets to be refreshed every so often. This does not work.
Two sheets within the workbook are connected via a query to another workbook. Essentially those sheets are a copy of the sheets within the other workbook.
I have embedded Code1 into the two worksheets. This is to prevent anyone making changes to the worksheet but still allow them to view the sheet and copy data from it. It brings up an message box and then undoes the change made by the user. This works fine and I am happy with it.
At the same time I want to be able to refresh the workbook so that the connected sheets are up to date with respect to the other workbook that they are connected to.
To do this I have added a button into the workbook called "Refresh". This button calls Code2. This was done with the intention of disabling events so that the worksheet_change macro is paused to allow for the data to be refreshed.
However, this does not work as the worksheet_change macro still works. I.e after clicking the button, the workbook is refreshed and then any update is undone and the message box is displayed - which isn't what I need.
CODE1
Private Sub Worksheet_Change(ByVal Target As Range)
Dim KeyCells As Range
' The variable KeyCells contains the cells that will
' cause an alert when they are changed.
Set KeyCells = Range("A1:Z1000")
If Not Application.Intersect(KeyCells, Range(Target.Address)) _
Is Nothing Then
With Application
.EnableEvents = False
.Undo
.EnableEvents = True
End With
' Display a message when one of the designated cells has been
' changed.
' Place your code here.
MsgBox "DO NOT MODIFY THIS SHEET - Any necessary modifications should be made in 'Master Invoice Template' and this sheet will automatically be updated!"
End If
End Sub
CODE2
Sub refresh()
On Error GoTo ErrorHandler
Application.EnableEvents = False
ThisWorkbook.RefreshAll
ErrorHandler:
Application.EnableEvents = True
End Sub
I have scoured the internet for a solution and pretty much everything that I find points me in the direction of enableevents=false, but as described in my post this does not work. Do I need to change the method of solving my problem or am I doing something wrong within my code?
I suspect the undo line of code is causing the problem, but I am not sure!
Any help would be greatly appreciated!
I think I have figured out what was wrong with the code; correct me if I am wrong. The data was taking too long to refresh when Code2 was ran. This meant that the Application.EnableEvents = Ture in Code2 took effect before the data could be fully refreshed and when it finally did complete its update, the Worksheet_Change event was triggered.
I tried using DoEvents after the RefreshAll command but this didn't work either. I have used what I found in this post to work around the problem and the refresh button now works!
Specifically the code that helped is below: I replaced Code2 with this:
Sub Refresh_All_Data_Connections()
For Each objConnection In ThisWorkbook.Connections
'Get current background-refresh value
bBackground = objConnection.OLEDBConnection.BackgroundQuery
'Temporarily disable background-refresh
objConnection.OLEDBConnection.BackgroundQuery = False
'Refresh this connection
objConnection.Refresh
'Set background-refresh value back to original value
objConnection.OLEDBConnection.BackgroundQuery = bBackground
Next
MsgBox "Finished refreshing all data connections"
End Sub
Please let me know if my logic in explaining why the code didn't work is correct - I am still new to VBA and would like to understand the problem fully!

How do I make VBA complete it's previous command (Refreshing using Bex analyser 7) before moving on to the next one?

I am attempting to create a macro which opens up a file refreshs a query and will then save and close. Currently the macro initiates the refresh however it moves on to the Save and close command before it has finished and therefore nothing changes. I have seen that there are ways off 'pausing' or 'sleeping' for a period of time to allow the command to be completed however I wish to expand this macro to opening multiple workbooks with queries which take differing times to refresh so therefore that would be a last resort. What I have currently utilizes DoEvents however this doesn't seem to be working either.
Note: The refresh works through SAP Bex analyser 7.
My code:
Sub OpenAndRefresh()
Workbooks.Open "QueryRefresh.xls", UpdateLinks:=False
Workbooks("QueryRefresh.xls").Activate
Run "BExAnalyzer.XLA!SAPBEXrefresh", True
DoEvents
Workbooks("QueryRefresh.xls").Close SaveChanges:=False
End Sub
Any help or guidance would be greatly appreciated.
I have a macro for this purpose, and I've never had this issue:
Public Sub Refresh_All()
Dim filepathstr As String
Dim filename As String
Dim wbk As Workbook
filepathstr = Sheet1.Range("filepath").Value
For Each cell In Sheet1.Range("workbooks")
If Not cell.Value = "" Then
filename = cell.Value
Set wbk = Workbooks.Open(filepathstr & filename)
''''**REFRESH**''''''
SAPBexrefresh (True)
Application.DisplayAlerts = False
wbk.Save
wbk.Close False
Application.DisplayAlerts = True
End If
Next cell
End Sub
The main difference here code-wise is that I'm calling the refresh macro directly rather than using the Run command.
EDIT: To make this work you also need to add the "BExAnalyzer" Reference in your project. If you're not familiar with adding references, you need to go into Tools --> References, then click on "BExAnalyzer" out of the long list of available references.
I'm not sure why this would make any difference but as I say, I've always found that my macro finishes refreshing (even when this takes up to 15 minutes) before continuing. I'm also using BEx 7

Launch a different sheet by Spin button

want to use a Spin button (ActiveX Control) to show a previous or next sheet. When the button is clicked the event is successfully triggered and the desired sheet is activated but it holds some elements (commands, graphs, etc.,) from the original sheet and shows these as an appending picture.
Sample code for Down button event :
Private Sub SpinButton_JumpToWeek_SpinDown()
Dim sh_num As String
Dim tmp_num As Integer
Application.ScreenUpdating = False
Application.EnableEvents = False
SpinButton_JumpToWeek.Value = Range("B27").Value - 1
tmp_num = SpinButton_JumpToWeek.Value
' Activate desired KTx sheet
sh_num = "KT" & tmp_num
Range("F27").Value = "" 'reset to blank
Sheets(sh_num).Activate
Application.ScreenUpdating = True
Application.EnableEvents = True
End Sub
To override this effect I have to manually select (activate) another sheet and then again select the desired sheet. I tried also to automatize this workaround with a macro, but unfortunately it does not work.
It is interesting that this problem do not occur if I execute code in Debug mode (using breakpoint and the stepping line by line).
Surprisingly, I do not have such problem if I try to show the previous/next sheet by writing a value (sheet name index) into a defined cell (i.e. using the Worksheet_Change event). The desired page is correctly shown. See photos.
Sample code for this evententer image description here:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim sh_num As String
Application.ScreenUpdating = False
Application.EnableEvents = False
If Range("F27").Value > 0 Then
' Activate desired KTx sheet
sh_num = "KT" & Range("F27").Value
Range("F27").Value = "" 'reset to blank
Sheets(sh_num).Activate
End If
Application.ScreenUpdating = True
Application.EnableEvents = True
End Sub
I need to use the Spin button because it is faster and allow me to skip some sheets (for example if these do not have data).
The problem occurs in both 32 and 64-bit Excel.
Do somebody an idea why this problem is occurring and a workaround? Do I have to change some settings or system properties in Excel? Any help would be highly appreciated.
#mehow
I append my commenst here due to large number of words.
I followed your suggestion and tried the example of a UserForm with inserted active control “Microsoft Office Spreadsheet”. I found out that this would not be a good solution for my case, because the response of such sheet is relatively slow (even on a fast PC like mine) when user inserts values into cells. Furthermore, this would greatly complicate my fairly simple *.xlsm workbook, which has more than 50 sheets (1 sheet for each week, the contents of these sheets are then linked to a main sheet), and completely meets my basic necessities (with the exception of this spin button of course).
In my opinion there is probably necessary to manipulate some kind of system property (like for e.g. Application.ScreenUpdating trick), but I do not have enough VBA knowledge to find it.
To clearly explain my question I would need to share my sample workbook, but I do not know how to upload it to this forum. Unfortunately, I am not able upload/show images successfully on this forum (due to my low rating), which would also help a lot.
BTW, I cannot see images on other questions of this forum, too. . Could this problem occur due to missing plugins in a web browser?
You can use a simple trick... before the "Application.screenupdating = true" you can insert the two lines:
ActiveWindow.SmallScroll Down:=-100
ActiveWindow.SmallScroll Up:=100

Loop Through Visible Worksheets with Moved Charts

Morning
I need some help and not really sure where to begin.
I have an excel workbook, which populates a sheet of data from a SQL stored procedure. There are then a series of pivots off the data, all easy so far. I then create charts off those pivots and move them to their own sheets.
What I want to achieve is a rolling dashboard of those charts, say every 30 seconds, change sheet. I have tried a few things, grabbed a few ideas off this site, but for some reason, the sheets with the charts on will not loop with the others, so in effect I only see my data page and pivot page. Below is some code that I tried to modify for my purpose.
Sub TabShow()
Dim i As Integer
Dim Pause As Double
Pause = 3 'Pause delay
Loops = 3 'How many loops do you want to do
For j = 1 To Loops
For i = 1 To Worksheets.Count
Worksheets(i).Select 'Select the next worksheet
x = Timer
While Timer - x < Pause 'This does the pausing
Wend
Next i
Next j
End Sub
So if anyone has any code that loops through visible sheets including ones of moved charts, I would be eternally grateful.
Cheers
Time arithmetics: in VBA, time is stored in Date, being Date binary equal to Double, being each unit one day. So, Pause = 3 means 3 days!!! Better:
Dim Pause As Date
Pause = #0:0:30# 'Time constant
I recommend also using Now instead of Timer.
Program flow: while running a macro, Excel become irresponsive, unless you use DoEvents:
While Timer - x < Pause 'This does the pausing
DoEvents
Wend
That will ensure your workbook will allow user interaction while macro is running.
However, I do not recommend keeping a macro running while working on it. Check Application.OnTime for a better approach.

Resources