I'm just starting to work with charts in VBA. The manual seemed straight forward: All Charts are contained in Workbook.Charts and/or (well, not THAT clear for a beginner, as it turns out) Worksheets(x).ChartObjects. Only, I have now a graph in my workbook that I can not find in either collection.
Where is the object hiding?
I'm running a Little Marco, which should set the Colors of all Graphs according to my definitions. There are two Graphs in the workbook, on different spreadsheets. The second one (the one that is "vanished") is of type waterfall.
?ThisWorkbook.Charts = 0.
?ThisWorkbook.Worksheets("Graph").ChartObjects.Count = 1
?ThisWorkbook.Worksheets("Charts").ChartObjects.Count = 0
Well the first two results are fine, the third should have been a 1 as well.
I doubt any code would be usefull - since I'm may just be looking in the wrong Corners?
What is a safe way, to cycle through all Charts in a workbook? Cycling over each worksheet's Charts in chartobjects doesn'T do the Job.
Why can I not find the Chart in the worksheet? Is this some specialty of waterfall?
I'm sorry if this may seem like a very basic question, I'm just starting into Charts.
Edit: Added the Loop:
Public Sub hrFormatAllCharts()
Debug.Print ("hrFormatAllCharts: Enter")
Dim ws As Worksheet
Dim cht As ChartObject
For Each ws In ThisWorkbook.Worksheets
Debug.Print ("Charts in worksheet " & ws.Name & " : " & ws.ChartObjects.Count & ".")
For Each cht In ws.ChartObjects
Call hrFormatChart(cht.Chart)
Next cht
Next ws
Debug.Print ("hrFormatAllCharts: Exit")
End Sub
Bonus Point Question: Since I can see the Chart, is there a way through the GUI to ask the object "Where do you live?"?
PS: I verified that this issue is related to the Chart type (waterfall). I removed the waterfall and used another - now that other Chart is in the above collection. Deleted that one and put in again a waterfall: Can't find it in ChartObjects...
PPS: I found the following question in stack Overflow and now I wonder if it relates...: Error copying waterfall charts with Excel macro
Hey please find my proposition of looping through charts.
Sub LoopThroughCharts()
Dim charts As ChartObjects
Dim chart As ChartObject
Dim sheetIterator As Integer
Dim loopSheet As Worksheet
For sheetIterator = 1 To ThisWorkbook.Worksheets.Count()
Set loopSheet = ThisWorkbook.Worksheets(sheetIterator)
Debug.Print loopSheet.Name
Set charts = loopSheet.ChartObjects
For Each chart In charts
Debug.Print chart.Name
Next chart
Next sheetIterator
End Sub
Related
I have some Pie charts created using a macro. I need to change the macro so that it hides certain slices of the pie charts - this is easy enough to do manually by going to 'select chart data' and unticking the points you wish to hide. However I can't figure out which objects and properties that translates to in vba, and I can't go doing this manually every time this report is run.
NB the simpler method of editing the base data that the chart is looking at isn't an option.
Below is a code snippet I've been trying, which I will copy into the main macro once it's working. I think I'm pretty close, narrowing down to the specific point object I want to hide. Any help completing the last step will be much appreciated, as I can't figure what property to change to accomplish this.
Sub HideDataPoints()
'Finding out how to hide a slice from a pie chart
Dim sht As Worksheet, chtobj As ChartObject
Set sht = ActiveSheet
With sht
For Each chtobj In .ChartObjects
If chtobj.Chart.ChartTitle.Caption Like "*Task*" Then
With chtobj.Chart.FullSeriesCollection(1)
For a = 1 To .Points.Count
If .Points(a).HasDataLabel = False Then
ElseIf .Points(a).DataLabel.Caption Like "*Markham*" Then
'Point identified - how do I hide this slice from the chart?
End If
Next
End With
End If
Next
End With
End Sub
I have a difficult task. I am not sure if anyone out there can help me.
Please read my concerns:
#1 I have "2" workbooks name:
Unemployment_Rate
GDP_Annual_Growth_Rate_%
#2 in "Unemployment_Rate" workbook. I have "1" Master worksheet and "181" worksheet which is name after a country. Example Sheet #1 "Afghanistan"; Sheet #2 "Albania"; Sheet #3 "Algeria" and so on...
#3 In "GDP_Annual_Growth_Rate_%" workbook. I have "1" Navigation worksheet, "1" Master worksheet and "185 worksheet which is name after a country. Example Sheet #1 "Afghanistan"; Sheet #2 "Albania"; Sheet #3 "Algeria" and so on...
My problem here is this:
a) How can I create a chart using VBA to automatically get the data in each country worksheet from "Unemployment_Rate" and "GDP_Annual_Growth_Rate_%" workbook?
b) How can I get data from column "B" (which is the date column) and column "E" (which is the value column) in all country worksheet in Unemployment_Rate" workbook? And
c) How can I get data from column "B"(which is the date column) and column "E" (which is the value column) in all country worksheet in "GDP_Annual_Growth_Rate_%" workbook? See example photo of how the chart is displayed:
My Other Problem:
How can I paste each chart into each country worksheet in "Unemployment_Rate" workbook automatically using a VBA?
With lack of knowledge for excel. I don't know if this request can be accomplish. To me it sounds difficult. I am opened for a better outcome, once available.
Below you can view other supported images and excel files that relates with this project.
How can you all help me?
Thanks in advance
https://drive.google.com/file/d/1mmsL54lWBSVNsrP0INleJe-uW8U-2VVD/view
https://drive.google.com/file/d/1hbxg2eJEq0E6hBwMputV2lvOzeuNPkYu/view
Here's my take on this. Find something in common on all the sheets, loop through each sheet, perform a common/standardized operation on each sheet.
Sub Macro1()
Dim rs As Worksheet
For Each rs In ThisWorkbook.Worksheets
rs.Select
If rs.Name <> "Unemployment" Then
Columns("B:D").Select
Range("B5").Activate
ActiveSheet.Shapes.AddChart2(227, xlLine).Select
ActiveChart.SetSourceData Source:=Range(rs.Name & "!$B:$D")
ActiveChart.ChartType = xlColumnClustered
ActiveChart.FullSeriesCollection(1).ChartType = xlColumnClustered
ActiveChart.FullSeriesCollection(1).AxisGroup = 1
ActiveChart.FullSeriesCollection(2).ChartType = xlLine
ActiveChart.FullSeriesCollection(2).AxisGroup = 1
ActiveChart.FullSeriesCollection(1).AxisGroup = 2
ActiveChart.FullSeriesCollection(1).ChartType = xlLine
End If
Next rs
End Sub
Notice, I am skipping the first sheet, named 'Unemployment' because this sheet is different than all the others. Also, the code between the If...End If is simply a recorded macro. If it doesn't do exactly what you want, record a macro to do what you want, and copy/paste that code between the If...End If.
Let me know if you have any questions.
Based on your most recent question, here are my findings.
#1) In the workbook named 'GDP_Annual_Growth_Rate', you actually have a space after the tab named 'GDP Annual %' so the actual name is 'GDP Annual % '. I don't think you want that!
#2) You had to close your Sub with 'End Sub'. You ALWAYS need an 'End Sub' at the end.
#3) As you start to loop through the sheets, you start to slect charts and make modifications to those charts, like this: 'ActiveSheet.ChartObjects("Chart 1").Activate'. But, what if you don't have a chart on one sheet, you can't select a chart if it doesn't exist.
#4) Finally, rs sound like a records set in MS Access; that's my take on it. Why not declare a Worksheet as ws: 'Dim ws As Worksheet'
Here's some working code to get you started.
Sub DeleteallCharts()
Dim chtObj As ChartObject
Dim ws As Worksheet
For Each ws In ThisWorkbook.Worksheets
ws.Select
For Each chtObj In ActiveSheet.ChartObjects
chtObj.Delete
Next
Next ws
End Sub
Sub BuildAllCharts()
Dim ws As Worksheet
For Each ws In ThisWorkbook.Worksheets
ws.Select
If ws.Name <> "GDP Annual %" And ws.Name <> "Navigation" Then
Range("A:A,E:E").Select
Range("" & ws.Name & "[[#Headers],[12 Mth Rolling Avg.]]").Activate
ActiveSheet.Shapes.AddChart2(227, xlColumnClustered).Select
ActiveChart.SetSourceData Source:=Range("" & ws.Name & "!$A:$A," & ws.Name & "!$E:$E")
ActiveChart.FullSeriesCollection(1).Select
With Selection.Format.Fill
.Visible = msoTrue
.ForeColor.RGB = RGB(255, 0, 0)
.Transparency = 0
.Solid
End With
Range("" & ws.Name & "[[#Headers],[Country]]").Select
End If
Next ws
End Sub
Notice: the first Macro deletes all charts and the second Macro builds all charts. The only thing that may be a bit confusing is the '& ws.Name &' but you need this as you loop through all sheets, so you get dynamic references to each relative data set on each sheet. Record a Macro yourself, and you will see hot it is linked to one specific sheet.
Finally...if you want to create a Secondary Axis, follow the steps below.
Select a chart to open Chart Tools.
Select Design > Change Chart Type.
Select Combo > Cluster Column - Line on Secondary Axis.
Select Secondary Axis for the data series you want to show.
Select the drop-down arrow and choose Line.
Select OK.
I just created a sample for you, below. You exact requirement may be slightly different, but this should illustrate the point.
Here is a link for some really, really, really cool charting ideas.
https://peltiertech.com/Excel/Charts/ChartIndex.html
I am writing a chart formatting Excel add-in for my office.
To handle both embedded charts and chart sheets I wrote two loops in the first subroutine. Each time a chart is activated, a second subroutine is called to handle ActiveChart formatting.
Part of my goal is to apply data labels to line charts, however my code produces blank data labels as in figure 1 even when I have set DataLabel.ShowSeriesName = True. There are two cases where the labels are added correctly described below.
Option Explicit
Sub CESAR_style ()
Dim a as application
Dim wb As Workbook
Dim ws As Worksheet
Dim chtO As ChartObject
Dim cht As Chart
Set twb = ThisWorkbook
Set a = Application
' Turn off events
a.EnableEvents = False
' Loop through all chart sheets
For Each cht In a.Charts
cht.Activate
Call Format
Next
' Loop through all chart objects
For Each ws In ActiveWorkbook.Worksheets
For Each chtO In ws.ChartObjects
chtO.Activate
Call Format
Next
Next
a.EnableEvents = True
End Sub
Private Sub Format()
Dim i As Integer
Dim j As Integer
With ActiveChart
' Count the series in the chart
i = .SeriesCollection.Count
' Code here to add a new series used to make a 'Today' _
reference line dividing history and future
' Add series data labels, excluding the new series added above
' For each data series
For j = 1 To i
With .FullSeriesCollection(j)
' For Line charts
If .ChartType = xlLine Then
' Turn off leader lines for full series
.HasLeaderLines = False
' Add series data label to right of last point in series
With .Points(.Points.Count)
' If a label already exists remove it
If .HasDataLabel = True Then
.HasDataLabel = False
Else
End If
The code works properly to this point, but when .ApplyDataLabels runs, it creates blank data labels as seen in figure 1 [blank data labels]1
' Add a series data label
.ApplyDataLabels ShowSeriesName:=True, _
ShowValue:=False, _
HasLeaderLines:=False
End With
Else
' Code here to handle stacked area charts; working properly
End If
End With
Next
End With
End Sub
I have added a break point at .ApplyDataLabels. If I continue the code with F5 or the play button, it continues to give me blank labels. However, if i step the code with F8 or Step Into, the code executes successfully and I get the labels I want as seen in figure 2.
Correct data labels
A second confusing quality is when I move the .ApplyDataLabels segment to the CESAR_style subroutine, the code runs successfully.
I have tried delaying the code with sleep but without success.
Is there something I'm doing wrong in how I've set up the two subroutines?
Any help or insight is much appreciated. Let me know if additional information is needed to make the problem clearer.
Generally with VBA I find that if something works when you're stepping through it, but not when you're running it, it's a select/focus problem. When you're stepping through the code, you're likely selecting the worksheet the the chart is on so you can see what your code is doing, which is effectively doing a step for your macro. Try activating the worksheet the chart is on before activating the chart. If that doesn't work, try selecting a cell on the worksheet instead.
Updated & cross-posted from: http://www.ozgrid.com/forum/showthread.php?t=203827
My objective is to run an Excel macro from within PowerPoint. [All the macro does is change the row filtering for a data range in Excel, thus changing lines depicted on a chart].
So my PPT macro should (1) run the Excel macro which changes the chart, and then (2) update that chart in PPT which is linked to the Excel chart.
Here’s what I’ve tried:
Sub Test()
Excel.Application.Run "'" & "C:\myPath\" & "PPT Macro Test.xlsm'!Steps"
ActivePresentation.UpdateLinks
End Sub
It runs the “Steps” macro, updating the chart in Excel, but does not update the PPT chart.
So I adapted a technique from this post: How to update excel embedded charts in powerpoint? (hat tip brettdj).
Sub Test()
Excel.Application.Run "'" & "C:\myPath\" & "PPT Macro Test.xlsm'!Steps"
ChangeChartData
End Sub
Sub ChangeChartData()
Dim pptChart As Chart
Dim pptChartData As ChartData
Dim pptWorkbook As Object
Dim sld As Slide
Dim shp As Shape
For Each sld In ActivePresentation.Slides
For Each shp In sld.Shapes
If shp.HasChart Then
Set pptChart = shp.Chart
Set pptChartData = pptChart.ChartData
pptChartData.Activate
Set pptWorkbook = pptChartData.Workbook
On Error Resume Next
'update first link
pptWorkbook.UpdateLink pptWorkbook.LinkSources(1)
On Error GoTo 0
pptWorkbook.Close True
End If
Next
Next
Set pptWorkbook = Nothing
Set pptChartData = Nothing
Set pptChart = Nothing
End Sub
Now it works as hoped, but it pauses while it opens, saves & closes the workbook. It’s a fairly large file, so this is an unacceptable delay during a presentation. Is there a way to run a macro in an Excel workbook which is already open “behind the scenes”, without reopening and closing it?
Thanks in advance.
In my brief testing, assuming the workbook is already open, then the data should update in real-time based on the Excel procedure. You should not need to call the ChangeChartData procedure from PowerPoint at all.
Sub Test()
Excel.Application.Run "'" & "C:\myPath\" & "PPT Macro Test.xlsm'!Steps"
End Sub
This avoids the (presumably) resource-intensive task of the Save method against a very large Excel file, which when called from your PPT is being done against every chart, regardless of need, and which seems a very likely culprit for unnecessarily long runtime.
There may be some exceptions based on how the Test procedure is invoked from PowerPoint, and if you observe otherwise you should please add more detail (minimally: how the procedure is being run whilst the PPT is in Presentation Mode)
This answer is promising though, it has some apparent caveats (both files must be open, the Excel file should be the only Excel file open, etc.). I haven't tested other scenarios to see if it still works. It does appear to be working for me:
Set pres = Presentations("Chart.pptm") 'ActivePresentation, modify as needed.
' Make sure you reference correct shape name on the next line:
pres.Slides(1).Shapes("Chart1").LinkFormat.Update
In your implementation, perhaps:
For Each sld In ActivePresentation.Slides
For Each shp In sld.Shapes
If shp.HasChart Then
Set pptChart = shp.Chart
pptChart.LinkFormat.Update
End If
Next
Next
Regarding the Activate method of the ChartData object, MSDN Notes that:
You must call the Activate method before referencing this property; otherwise, an error occurs.
This is by design and "wont' be changed", but I've never spoke to anyone who understands why this is considered a good or desireable UI experience...
This self-answered question from a few years ago suggests you can avoid the Activate requirement, but I do not think this is accurate -- I can't replicate it and I can't find any other sources which indicate this can be done.
#David, thanks for the help. This (mostly) works:
Sub Test()
Excel.Application.Run "'" & "C:\myPath\" & "PPT Macro Test.xlsm'!Steps"
Slide1.Shapes(1).LinkFormat.Update
End Sub
Mostly. Your comments "it was working, then it wasn't, now it is" forced me into some troubleshooting. Here's the workaround:
Open the PPT file, click update links
Immediately, right click on the embedded/linked chart, select "Edit Data"
This opens the Excel file (NOT read-only)
Close Excel, without saving the file
Amazingly, it then runs by clicking the button in slideshow view, or stepping thru in the VB Explorer. Even more amazing, when it runs it doesn't open Excel--it just works in the background.
If I do NOT right click >> "Edit Data" first, it will ALWAYS open Excel & prompt for Read-Only/Notify/Cancel. Then I can't run the macro from PPT, and running it within Excel updates the chart only in Excel, not in PPT as well.
Alternately I tried "Slide1.Shapes(1).LinkFormat.AutoUpdate = ppUpdateOptionAutomatic" to see if that would set updating to automatic...it didn't.
If anyone can chime in with a fix to the workaround, I'd appreciate it. In the meantime, thanks David for your selfless perseverance, and I'll try to figure out how to give you credit for the answer.
I have a tangential question to this one: Using VBA Code how to export excel worksheets as image in Excel 2003?
Specifically, when the macro pastes the copy of the range to the chart, the image is blank, even though the copied range contains 5 charts and some formatted cells. When I perform the exact same steps manually it all works as expected.
Even weirder, I've recorded the whole process except the export step. When I run the recorded macro, it works. But when I copy the code from the recorded macro inside the For Each loop below, and tweak it to point to the sheet being worked by the macro (i.e. replacing "ActiveSheet" with "t") the macro doesn't work again.
I even went so far as to just invoke the recorded macro after using the For Each to move to each sheet, still getting a blank image pasted.
I'd appreciate any help on this.
My code:
Sub ExportCharts()
Dim Rng As Range
Dim S As Worksheet
Dim wb As Workbook
Set wb = ThisWorkbook
Dim EName As String
Dim CO As ChartObject
Dim C As Chart
Dim temp As String
Application.ScreenUpdating = False
'Iterate through the sheets in the workbook
For Each t In wb.Worksheets
'Capture the sheet
Set S = t
S.Activate
'Set the range to be exported
Set Rng = S.Range("A1:Z60")
'Copy range as picture onto Clipboard
Rng.Select
Rng.CopyPicture Appearance:=xlScreen, Format:=xlBitmap
'Build the chart/file name
EName = S.Name & " Quality Charts"
'Create an empty chart with exact size of range to be copied
S.Range("$AA$1:$AC$2").Select
ActiveSheet.Shapes.AddChart.Select
Set C = ActiveChart
temp = Right(C.Name, Len(C.Name) - 1 - Len(S.Name))
S.Shapes(temp).Height = Rng.Height
S.Shapes(temp).Width = Rng.Width
'Paste into chart area, export to file, delete chart
'C.Activate
With C
.Paste
.Export "\\COMPUTERNAME\Users\USERNAME\Desktop\My Documents\" & EName & ".jpg"
'Note the above is an actual hard coded path in my code (yes I want it hard coded)
End With
C.Delete
Next
Application.ScreenUpdating = True
End Sub
So I finally figured out the issue. The line turning off screen updating is the issue.
One would assume that's because I say copy as it appears on the screen (why MS didn't just allow for that command to use the display as it appeared at the last screen update is beyond me, but it's not exactly like Excel is bug free).
In any event, commenting out that line results in a good paste.
As a note for those who have more going on in there macros and want/need screen updates off in order to get a reasonable run speed, after I figured out the problem I tried reactivating screen updates before the copy as picture, then turning it off again immediately afterwards, and that worked.