Create multiple line charts using Macro in Excel - excel

I have created a macro which creates line chart for each row. Below is the code.
The data is in following format:
Format in which I have the data:
Sub createChartWithLoop()
Dim wks As Worksheet
Set wks = Worksheets("Sheet1")
Dim chrt As Chart
Set chrt = wks.Shapes.AddChart.Chart
Dim chartRange As Range
Set chartRange = wks.Range("A1:A4")
With chrt
.ChartType = xlLine
.SetSourceData Source:=Range(wks.Range("A" & chartRange.Row & ":D" & chartRange.Row).Address)
For i = chartRange.Row + 1 To chartRange.Rows.Count
.SeriesCollection.NewSeries
.SeriesCollection(i).Name = wks.Cells(i, 1)
.SeriesCollection(i).Values = wks.Range("B" & i & ":D" & i)
Next i
End With
End Sub
However, now I have a little different requirement. Assume I have 30 rows same format.
Requirement: I have to consider the first 3 rows as 1 set. 2nd 3 rows as 2nd Set and so on. So here effective we will have 10 set (30 rows/3). Each set should give me a chart.
Right now, I'm getting a chart with 3 lines (3 records). Above is my code. Can anyone help on the above request?

I wrote a procedure which will plot a stated number of charts each using a stated number of rows. Then I wrote a short procedure that shows how to have it create 10 charts with three rows each.
Here is the procedure to create nCharts charts, using nRows rows for each:
Sub CreateChartWithLoop(nCharts As Long, nRows As Long)
Dim wks As Worksheet
Set wks = ActiveSheet
Dim DataRange As Range
Set DataRange = ActiveSheet.UsedRange
Dim iChart As Long
For iChart = 1 To nCharts
Dim cht As Chart
Set cht = wks.Shapes.AddChart(xlLine, DataRange.Width, DataRange.Rows(1 + (iChart - 1) * nRows).Top).Chart
Do Until cht.SeriesCollection.Count = 0
cht.SeriesCollection(1).Delete
Loop
Dim iRow As Long
For iRow = 1 To nRows
With cht.SeriesCollection.NewSeries
.Name = "=" & DataRange.Cells(iRow + (iChart - 1) * 3, 1).Address(, , , True)
.Values = DataRange.Rows(iRow + (iChart - 1) * 3).Offset(, 1).Resize(, DataRange.Columns.Count - 1)
End With
Next
cht.HasTitle = True
cht.ChartTitle.Text = "Chart " & iChart
Next
End Sub
Here's the procedure that calls the main procedure, telling it to make 10 charts with 3 rows each:
Sub Create10ChartsWith3RowsEach()
CreateChartWithLoop 10, 3
End Sub

Related

Excel VBA code to copy and paste table rows to PowerPoint until specific row height is met

I already have a working code that copies a table from Excel to PowerPoint and creates more slides and tables (splits the large table into multiple ones) if the sum of the row heights reaches a certain threshold in Excel:
Sub PowerPointTableSplit()
Dim rng As Range
Dim PowerPointApp As Object
Dim myPresentation As Object
Dim mySlide As Object
Dim myShape As Object
Dim lCol As Long
Dim lRow As Long
Dim LastRow As Long
Dim i As Long
Dim j As Integer
Dim rngH As Range
Dim wss As Worksheet
Set wb = Workbooks("Automation Tool.xlsm")
Set ws = wb.Sheets("630")
Set rngH = ws.Range("A1:AB1") 'Header Row (same for all tables)
LastRow = ws.Range("A" & ws.Rows.Count).End(xlUp).Row
With ws.Range("A1:AB" & LastRow)
.Font.Name = "Arial"
.Font.Size = 6
End With
ws.Range("A1:A" & LastRow).EntireRow.AutoFit
i = 2
Set wss = wb.Worksheets.Add
Set PowerPointApp = GetObject(class:="PowerPoint.Application")
Set myPresentation = PowerPointApp.ActivePresentation
'Loop through excel table and cut it after X rows to make it fit on PowerPoint, Copy & Paste table
'wss is a temporary excel sheet to store every X rows (will be deleted at the end)
Do While i <= LastRow
Z = 1 'counter to check row height in excel
RowHeight = 0
Do Until RowHeight > 600
RowHeight = RowHeight + ws.Rows(Z).Height
Z = Z + 1
Loop
j = Application.Min(i + Z, LastRow)
Union(rngH, ws.Range("A" & i, ws.Range("AB" & j))).Copy
wss.Range("A1").PasteSpecial Paste:=xlPasteColumnWidths
wss.Range("A1").PasteSpecial Paste:=xlPasteValues
wss.Range("A1").PasteSpecial Paste:=xlPasteAllUsingSourceTheme
sld.Shapes.PasteSpecial DataType:=ppPasteHTML, Link:=msoFalse
wss.Range("A1:AB" & j - i + 2).Copy
Set sld = myPresentation.Slides.Add(myPresentation.Slides.Count + 1, ppLayoutBlank)
i = j + 1
Loop
'Delte temporary excel sheet wss as not needed anymore
Application.DisplayAlerts = False
wss.Delete
Application.DisplayAlerts = True
Set wss = Nothing
End Sub
After this I am using a code to strech the table in PowerPoint to the whole available space on the slide, this also makes it tricky for me to get the real row height in PowerPoint using the code above as I would have to check the real height after streching the table.
Sub AutoFitTables()
Dim s As Slide
Dim oSh As Shape
Dim oTbl As Table
Dim lRow As Long
Dim lCol As Long
ScreenUpdating = False
For Each s In ActivePresentation.Slides
'ActivePresentation.Slides(Slide.SlideIndex(s)).Select
ActivePresentation.Slides(s.SlideIndex).Select
For Each oSh In s.Shapes
If oSh.HasTable Then
oSh.Left = 0 * 28.3
oSh.Top = 1.5 * 28.3
oSh.Width = 33.867 * 28.35
oSh.ZOrder msoSendToBack
Set oTbl = oSh.Table
For lRow = 1 To oTbl.Rows.Count
For lCol = 1 To oTbl.Columns.Count
With oTbl.Cell(lRow, lCol).Shape
.TextFrame.TextRange.Font.Name = "Arial"
.TextFrame.TextRange.Font.Size = 6
.TextFrame2.VerticalAnchor = msoAnchorMiddle
oTbl.Rows(lRow).Height = 0.5
End With
Next lCol
Next lRow
End If
Next oSh
Next s
ActivePresentation.Slides(1).Select
End Sub
However, since I still want to copy a much wider table, the format in the table shifts as soon as I copy it into PowerPoint, so that the table is too large to be seen completely, the code does not work.
I already have an idea, but the implementation fails. I want to copy the table row after row and after each copy check the total row height in PowerPoint and jump to the next page if the row height of 450 is reached. For this the following code comes into question:
sld.Shapes(X).Table.Rows(Y).Height 'with X and Y looping
I would be thankful for any help and hints.

Is there a way to list chart numbers based on position in the spreadsheet? (Not in sorted order?)

The following macro lists all the chart numbers of by worksheet but it order them and this is not how the charts appear in the sheet.
Sub ListChartNames()
Dim Cht As ChartObject
Dim i As Integer
i = 1
For Each Cht In ActiveSheet.ChartObjects
Cells(i, 1) = Cht.Chart.Name
i = i + 1
Next Cht
End Sub
For example, I have a chart in E6:L17 (let's call this one Chart 1) and another in N6:U17 (let's call this one Chart 11).
Then, I move down to two charts in E19:L30 (let's call this one Chart 400) and another in N19:U30 (let's call this one Chart 2).
Then, I move down to two charts in E32:L43 (let's call this one Chart 3) and another in N32:U43 (let's call this one Chart 12)
Then, I move down to only 1 chart in E45:L56 (let's call this one Chart 13)
Then, I back to two charts in E58:L69 and another in N58:U69 (let's call these Chart 15 and Chart 16)
and so on.....
The above charts are all in columns E through U. But then there is another set in columns Y through AO in same patter and again in AS through BI, etc.
I have like 500 charts and I'd like a macro to list them starting in the first set of columns (E through L) but list them from top to bottom, let to right.
So, the results based on the above would be for columns F through U
Chart 1
Chart 11
Chart 400
Chart 2
Chart 3
Chart 12
Chart 13
Chart 15
Chart 16
The macro above lists the charts in a sorted order which is not what I need.
This also doesn't answer the question: Select chart object based on position in sheet (VBA)
Does this give you what you need?
Sub list_charts_in_top_left_to_bottom_right()
Dim ws As Worksheet, outputsh As Worksheet, last_cell As Range, oChartObj As Object
Set ws = ThisWorkbook.Sheets("SheetWithChartsOnIt")
Set outputsh = ThisWorkbook.Sheets("SheetToWriteTo")
outputsh.Range("A:A").ClearContents
outputsh.Range("A1") = "Output:"
If ws.ChartObjects.Count = 0 Then
outputsh.Range("A2") = "No charts found"
Exit Sub
End If
Debug.Print "Charts found: " & ws.ChartObjects.Count
Set last_cell = ws.Range("A1")
'find bounds of range by expanding last_cell with each chart
For Each oChartObj In ws.ChartObjects
With oChartObj
If .TopLeftCell.Row > last_cell.Row Then Set last_cell = ws.Cells(.TopLeftCell.Row, last_cell.Column)
If .TopLeftCell.Column > last_cell.Column Then Set last_cell = ws.Cells(last_cell.Row, .TopLeftCell.Column)
End With
Next
Debug.Print "Bounds of range: $A$1:" & last_cell.Address
Dim area_to_examine As Range
For col = 5 To last_cell.Column Step 21 'start with column 5 (E) and then jump 21 columns at a time
Set area_to_examine = Range(Columns(col), Columns(col + 17))
Debug.Print "Examining: " & area_to_examine.Address
For Each rw In Intersect(area_to_examine, ws.Range("A1", last_cell.Address).Rows)
For Each cl In rw.Cells
For Each oChartObj In ws.ChartObjects
With oChartObj
If .TopLeftCell.Row = cl.Row And .TopLeftCell.Column = cl.Column Then
outputsh.Cells(outputsh.Rows.Count, "A").End(xlUp).Offset(1) = .Name
Debug.Print .Name
End If
End With
Next
Next
Next
Next
End Sub
This is an alternative method. It's still not using a sort algo, but uses a workaround which (does waste a little time but) should be massively quicker than scanning every cell in the sheet:
Sub list_charts_in_top_left_to_bottom_right_v2()
Dim ws As Worksheet, outputsh As Worksheet, chartCount As Long, x As Long, y As Long, maxZ As Long
Set ws = ThisWorkbook.Sheets("SheetWithChartsOnIt")
Set outputsh = ThisWorkbook.Sheets("SheetToWriteTo")
outputsh.Range("A:A").ClearContents
outputsh.Range("A1").Value = "Chart"
chartCount = ws.ChartObjects.Count
ReDim arrChartlist(chartCount, 1)
If chartCount = 0 Then
outputsh.Range("A2") = "No charts found"
Exit Sub
End If
maxZ = 0
For x = 0 To chartCount - 1
With ws.ChartObjects(x + 1)
arrChartlist(x, 0) = .Name
arrChartlist(x, 1) = (((.TopLeftCell.Column - 2) \ 19) * chartCount * chartCount) + (.TopLeftCell.Column * chartCount) + .TopLeftCell.Row
If maxZ < arrChartlist(x, 1) Then maxZ = arrChartlist(x, 1)
End With
Next
For x = 0 To maxZ
For y = 0 To chartCount - 1
If x = arrChartlist(y, 1) Then
outputsh.Cells(outputsh.Rows.Count, "A").End(xlUp).Offset(1).Value = arrChartlist(y, 0)
End If
Next
Next
End Sub

Adding new series to a graph automatically

I'm trying to create a dynamic Scatterchart in the worksheet("Graphs") using a button.
The Seriesname has to be equal to Worksheets("VS_P240_X").Cells(1,i), where i is a counter for the columns.
The XValues have to be equal to Worksheets("VS_P240_X").Range(cells(3,i).cells(1000,i)).
The YValues have to be equal to Worksheets("VS_P240_Y").Range(cells(3,i).cells(1000,i)).
When I Update the workbook the counter i will change, and I want that the chart will automatically update with the new series. I wrote this code but it is not working, do you have some suggestions?
Private Sub CommandButton5_Click()
'Graph generation NON COMPLETO
Dim i As Integer
Dim Chart1 As Chart
Set Chart1 = Sheets("Graphs").ChartObjects("Chart 1").Chart
For i = 1 To Lastcolumn
With Chart1
.ChartType = xlXYScatter
.SeriesCollection.NewSeries
'Change to what your series should be called
.SeriesCollection(i).Name = Worksheets("VS_P240_X").Cells(1, i).Value
.SeriesCollection(i).XValues = Worksheets("VS_P240_X").Range(Cells(3, i), Cells(1000, i))
.SeriesCollection(i).Values = Worksheets("VS_P240_Y").Range(Cells(3, i), Cells(1000, i))
End With
Next i
End Sub
To avoid confusion/bugs, it's best to specify a worksheet every time you use Range or Cells()
Something like this should work:
Private Sub CommandButton5_Click()
Dim i As Long, Lastcolumn As Long 'use long not integer
Dim Chart1 As Chart, wsX As Worksheet, wsY As Worksheet
Set wsX = Worksheets("VS_P240_X")
Set wsY = Worksheets("VS_P240_Y")
Set Chart1 = Sheets("Graphs").ChartObjects("Chart 1").Chart
Do While Chart1.SeriesCollection.Count > 0 'remove any existing data
Chart1.SeriesCollection(1).Delete
Loop
Chart1.ChartType = xlXYScatter 'do this outside of the loop...
Lastcolumn = wsX.Cells(1, wsX.Columns.Count).End(xlToLeft).Column
For i = 1 To Lastcolumn
With Chart1.SeriesCollection.NewSeries
.Name = wsX.Cells(1, i).Value
.XValues = wsX.Range(wsX.Cells(3, i), wsX.Cells(1000, i))
.Values = wsY.Range(wsY.Cells(3, i), wsY.Cells(1000, i))
End With
Next i
End Sub

Looping through multiple tables which vary in length

I have the following table:
And a macro that loops through the first section of the table (rows 6-7) in order to create the Pie-Charts on the right. My target now is to loop through all other tables automatically as well. The next one would be in row11 and create a new Pie Chart for that row, then the next table (rows 15-16) and so on. The header of each table is always red. The problem is that the length of the tables vary, meaning for example in the table1 ("Build", A5:K7) there can be 2 rows like here or 50, but each time I need one PieChart for each row.
Currently I have the following working code for Table1 ("Build" A6:K79) to create the 2 PieCharts automatically, but Im unsure how to make one loop for all tables on the sheet.
Dim rownumber As Integer
Dim LabelRange As Range
Dim ValueRange As Range
Dim Chart As ChartObject
Dim LeftIndent As Long
Dim TopIndent As Long
Dim InhaltsRangeString As String
Dim LetzteZeile As Long
'Intialpositionen für Graphen
LeftIndent = 726
TopIndent = 60
rownumber = 6 'Anfang der Buildtabelle in Reihe 6 (Spalte 1)
Set LabelRange = ThisWorkbook.Worksheets("Testplan Überblick").Range("C5, E5, G5, I5")
Set TPsheet = Worksheets("Testplan Überblick")
Set ValueRange = Union(TPsheet.Cells(rownumber, 3), TPsheet.Cells(rownumber, 5), TPsheet.Cells(rownumber, 7), TPsheet.Cells(rownumber, 9))
'Loop through table 1 which always starts at row 6 (unlike the others which have no set starting point cause the ones before can vary in length!)
For rownumber = 6 To LetzteZeileFunktion Step 1 '"LetzteZeileFunktion" gives me the long value of the last row filled in table 1
Set Chart = Sheets("Testplan Überblick").ChartObjects.Add(Left:=180, Width:=270, Top:=7, Height:=210)
With Chart
.Chart.SetSourceData Source:=ValueRange
.Chart.ChartType = xlPie
.Chart.HasTitle = True
.Chart.SetElement (msoElementChartTitleAboveChart)
.Chart.ChartTitle.Text = Sheets("Testplan Überblick").Cells(rownumber, 1).Value
.Chart.FullSeriesCollection(1).XValues = LabelRange
.Left = LeftIndent
.Top = TopIndent
.Name = Sheets("Testplan Überblick").Cells(rownumber, 1).Value
End With
TopIndent = TopIndent + 225
Next rownumber
End Sub
Any ideas on how to loop through all the tables even though they can all differ in length (amount of rows filled with content for charts) would be greatly appreciated!
Cheers
Use the text in one of the headers to identify the start of the data rows and a blank in column A to end. I have used "testfall qty" in column B.
Option Explicit
Sub CreateCharts()
Const DATA = "Testplan Überblick"
Const ROW_START = 5
Const POSN_LEFT = 726
Const POSN_TOP = 60
Const COL = "B"
Const HEADER = "testfall qty"
Dim wb As Workbook, ws As Worksheet
Dim rngLabel As Range, rngValue As Range
Dim iRow As Long, iLastRow As Long, count As Integer
Dim oCht As ChartObject, sColA As String, bflag As Boolean
bflag = False
Set wb = ThisWorkbook
Set ws = wb.Sheets(DATA)
' scan down the sheet
iLastRow = ws.Cells(Rows.count, "A").End(xlUp).Row
For iRow = ROW_START To iLastRow
' look for Testfall Qty as header
sColA = ws.Cells(iRow, 1)
If LCase(ws.Cells(iRow, COL)) = HEADER Then
'set ranges
Set rngLabel = ws.Range("C1, E1, G1, I1").Offset(iRow - 1)
bflag = True
ElseIf Len(sColA) > 0 And bflag Then
' create chart
Set rngValue = ws.Range("C1, E1, G1, I1").Offset(iRow - 1)
Set oCht = ws.ChartObjects.Add(Left:=180, _
Width:=270, Top:=7, Height:=210)
With oCht
.Left = POSN_LEFT
.Top = POSN_TOP + (count * 255)
.Name = sColA
With .Chart
.SetSourceData Source:=rngValue
.SeriesCollection(1).XValues = rngLabel
.ChartType = xlPie
.HasTitle = True
.SetElement msoElementChartTitleAboveChart
.ChartTitle.Text = sColA
End With
End With
count = count + 1
Else
' end of chart data
bflag = False
End If
Next
MsgBox count & " Charts created", vbInformation
End Sub

Invalid parameter error when method is called from another sub

I have the below code that creates charts from some worksheets and put the charts in their own worksheets. When I run the macro on it's own it works perfectly. When I use Call InsertDNCCharts from another macro I get a "Invalid Parameter" error on .Period = 7 from within the With tl block. Why is there a difference? If the code runs on its own shouldn't it run the same way when called from another sub?
Sub InsertDNCCharts()
Dim ws As Worksheet
Dim cws As Worksheet
Dim country As String
Dim lastrow As Long
Dim chrt As Shape
Dim chrtname As String
Dim xvalues As Range
Dim yvalues As Range
Dim tl As Trendline
For Each ws In ThisWorkbook.Worksheets
If Right(ws.Name, 6) = "_Chart" Then
country = Left(ws.Name, Len(ws.Name) - 6)
Set cws = ThisWorkbook.Worksheets(country)
lastrow = cws.Cells(Rows.count, "c").End(xlUp).Row
Set xvalues = cws.Range("c5:c" & lastrow)
Set yvalues = cws.Range("l5:l" & lastrow)
cws.Activate
Application.Union(xvalues, yvalues).Select
Set chrt = cws.Shapes.AddChart2(201, xlColumnClustered, Cells(5, 2).Left, Cells(5, 2).Top, 1000, 420)
chrt.Name = ws.Name
chrtname = chrt.Name
cws.Cells(5, 1).Select
With chrt.Chart
.Location Where:=xlLocationAsObject, Name:=ws.Name
.Axes(xlCategory).HasMajorGridlines = True
.Axes(xlCategory).HasMinorGridlines = False
.Axes(xlValue).HasMajorGridlines = True
.Axes(xlValue).HasMinorGridlines = False
.HasLegend = False
End With
ws.ChartObjects(chrtname).Activate
ActiveChart.ChartWizard Title:=country & " Daily New Cases (DNC)"
Set tl = ws.ChartObjects(chrtname).Chart.SeriesCollection(1).Trendlines.Add
With tl
.Type = xlMovingAvg
.Period = 7 '*******Error on this line. Debug says period=2, which is the default moving average period.
.DisplayEquation = False
.DisplayRSquared = False
.Format.Line.DashStyle = msoLineSysDot
.Format.Line.Weight = 3.5
.Format.Line.ForeColor.RGB = RGB(255, 0, 0)
.Format.Line.Style = msoLineSingle
End With
End If
Next ws
End Sub
If the chart in discussion (the created one) has at least 7 points, it is possible that the code is not referring to the appropriate chart, or the chart has not been created as necessary.
In order to check that, I would suggest you putting a break point on line With tl and visually check if the active chart is the one you need and if it looks as expected. It looks that the problem has to be before the line raising the error.

Resources