I have the following dataset:
I am trying to code a macro that builds a chart for each of the locations. I have created code that creates a new workbook, names the sheet, can create the first chart for Location 1, but I need the code to then loop back through and do the same for Location 2, Location 3, etc. Here is a sample chart below:
The hard part - Sites (Column A) will change. Some months I may have up to Location 10. I need the code to be dynamic enough to create a chart for each unique Site. As you'll see in the code, I'm creating a new workbook, creating the chart in the old file, and cut/paste into a tab in the new workbook. I then rename the worksheet based on Chart Title. I then need the code to loop back to the beginning and repeat the process for each unique location in Column A.
Here is the code:
Sub ChartBuilder()
Dim Wb As Workbook
Set Wb = ActiveWorkbook
Workbooks.Add
ActiveWorkbook.SaveAs Filename:=Wb.Path & "\Outputs.xlsx"
ActiveSheet.Name = "Results"
Wb.Activate
Sheets("Sheet1").Select
'88888 Loop ends below and Loop should come back here
ActiveSheet.Shapes.AddChart2(227, xlLine).Select
With ActiveChart
'Needs to be dynamic in both Chart Title Name and Data Range
'Column A is the Location Name - will have duplicates
'Column C has the weeks. Weeks are limited to Week 1, Week 2, Week 3, Week 4
'Column E thru I are the data columns that need to be displayed.
.ChartTitle.Text = ActiveSheet.Range("A2")
.SetSourceData Source:=Range("Sheet1!$C$2:$C$5,Sheet1!$E$2:$I$5")
ActiveChart.PlotBy = xlColumns 'Chart was flipping and I couldn't figure out why, so wrote code to flip it
Set Srs1 = ActiveChart.SeriesCollection(1)
Srs1.Name = ActiveSheet.Range("$E$1")
Set Srs2 = ActiveChart.SeriesCollection(2)
Srs2.Name = ActiveSheet.Range("$F$1")
Set Srs3 = ActiveChart.SeriesCollection(3)
Srs3.Name = ActiveSheet.Range("$G$1")
Set Srs4 = ActiveChart.SeriesCollection(4)
Srs4.Name = ActiveSheet.Range("$H$1")
Set Srs5 = ActiveChart.SeriesCollection(5)
Srs5.Name = ActiveSheet.Range("$I$1")
'Resizes chart
With ActiveChart.Parent
.Height = 300
.Width = 600
.Top = 100
.Left = 100
End With
End With
'Copy to new tab, name tab same as Chart Title
'Loop back to beginning for next filter
Dim OutSht As Worksheet
Dim Chart As ChartObject
Dim PlaceInRange As Range
Workbooks("Outputs.xlsx").Activate
Set OutSht = ActiveWorkbook.Sheets("Results") '<-- Output sheet
Set PlaceInRange = OutSht.Range("B2:J21") '<-- Output location
Wb.Activate
For Each Chart In Sheets("Sheet1").ChartObjects '<-- Loop charts
Chart.Cut 'Cut/paste charts
OutSht.Paste PlaceInRange
Next Chart
Workbooks("Outputs.xlsx").Activate
Worksheets("Results").Activate
ActiveSheet.Name = ActiveChart.ChartTitle.Text
Sheets.Add.Name = "Results"
'88888 Loop back to beginning
ActiveWorkbook.SaveAs Filename:=Wb.Path & "\" & Format(Now, "yyyymmdd") & " Outputs.xlsx"
Kill Wb.Path & "\Outputs.xlsx"
Wb.Activate
End Sub
The following code assumes that there are always four weeks per location. I'm not sure why the original code created an "Outputs.xlsx", just to subsequently delete it for a "YYYYMMDDOutputs.xlsx". I just went straight to the dated file name. I also did away with the "Results" tab and just made each chart it's own tab.
Quarterback Subroutine ChartAllLocations:
Public Sub ChartAllLocations()
Dim location As String, WB As Workbook, ws As Worksheet
Dim resultsWB As Workbook, data As Range, currLocation As Range
Dim headers As Range
Set WB = ThisWorkbook
Set ws = WB.Worksheets("Data")
Set resultsWB = ResultsWorkbook(WB.path)
Set headers = ws.Range("E1:I1")
locIdx = 2
Do
Set data = ws.Cells(locIdx, 1).Resize(4, 9)
ChartBuilder2 resultsWB, data, headers
locIdx = locIdx + 4
Loop While ws.Cells(locIdx, 1).Value <> ""
resultsWB.Worksheets("Sheet1").Delete
End Sub
Function for new Workook, ResultsWorkbook:
Private Function ResultsWorkbook(path As String) As Workbook
Dim output As Workbook
Dim ws As Worksheet
Set output = Workbooks.Add
output.SaveAs filename:=path & "\" & Format(Now, "yyyymmdd") & " Outputs.xlsx"
Set ResultsWorkbook = output
End Function
Function for building each chart ChartBuilder2:
Public Sub ChartBuilder2(WB As Workbook, data As Range, hdrs As Range)
Dim Chrt As Chart
Set Chrt = WB.Charts.Add(After:=WB.Worksheets(WB.Worksheets.Count))
Chrt.Name = data.Cells(1, 1)
Chrt.HasTitle = True
Chrt.ChartTitle.Text = data.Cells(1, 1)
Chrt.SetSourceData Source:=data.Cells(1, 5).Resize(4, 5)
Chrt.ChartType = xlLine
Chrt.PlotBy = xlColumns
Chrt.FullSeriesCollection(1).XValues = _
"={""Week 1"",""Week 2"",""Week 3"",""Week 4""}"
Chrt.Axes(xlValue).TickLabels.NumberFormat = "0%"
For srsIdx = 1 To 5
Chrt.SeriesCollection(srsIdx).Name = hdrs.Cells(1, srsIdx).Value
Next srsIdx
End Sub
Related
I have a challenge on achieving the below project, kindly please assist:
I have four source workbooks with names(GK,SK,RJ and TB).
Each workbook(GK,SK,RJ and TB) have three worksheets with the same names(products, channels, and sales).
I have destination workbook called consolidated workbook with the same worksheets names(products, channels, and sales) like those of the four source workbooks.
All workbooks(source + destinations) are in the same folder.
Iam requesting VBA code that will copy data from each worksheet of all the four source workbooks and transfer/paste the data to worksheets in consolidated workbook based on the rows that were not previously copied from the last copy event.
Currently I have the below code but whenever I ran it copies everything from worksheets on the source workbooks and paste to worksheets in consolidated workbook which result to duplicated data.
All the source workbook have worksheets with the "DATE" as a first column in each worksheet table column.
Destination workbook also have the same worksheet names and the same columns structure on each worksheet are the same as of those source worksheet.
Kindly advise what should I amend so that the code will that will copy data from each worksheet of all the four source workbooks and transfer/paste the data to worksheets in consolidated workbook based on the rows that were not previously copied from the last copy event.
Sub Copy_From_All_Workbooks()
Dim wb As String, i As Long, sh As Worksheet
Application.ScreenUpdating = False
wb = Dir(ThisWorkbook.Path & "\*")
Do Until wb = ""
If wb <> ThisWorkbook.Name Then
Workbooks.Open ThisWorkbook.Path & "\" & wb
For Each sh In Workbooks(wb).Worksheets
sh.UsedRange.Offset(1).Copy '<---- Assumes 1 header row
ThisWorkbook.Sheets(sh.Name).Cells(Rows.Count, 1).End(xlUp).Offset(1).PasteSpecial xlPasteValues
Application.CutCopyMode = False
Next sh
Workbooks(wb).Close False
End If
wb = Dir
Loop
Application.ScreenUpdating = True
End Sub
Kindly please see the amended code:
Sub Copy_From_All_Workbooks()
Dim wb As String, i As Long, sh As Worksheet, fndRng As Range,
start_of_copy_row As Long, end_of_copy_row As Long, range_to_copy As
Range
Application.ScreenUpdating = False
wb = Dir(ThisWorkbook.Path & "\*")
Do Until wb = ""
If wb <> ThisWorkbook.Name Then
Workbooks.Open ThisWorkbook.Path & "\" & wb
For Each sh In Workbooks(wb).Worksheets
On Error Resume Next
sh.UsedRange.Offset(1).Copy '<---- Assumes 1 header row
Set fndRng = sh.Range("A:A").Find(date_to_find,LookIn:=xlValues,
searchdirection:=xlPrevious)
If Not fndRng Is Nothing Then
start_of_copy_row = fndRng.Row + 1
Else
start_of_copy_row = 2 ' assuming row 1 has a header you want to ignore
End If
end_of_copy_row = sh.Cells(sh.Rows.Count, "A").End(xlUp).Row
Set range_to_copy = Range(start_of_copy_row & ":" & end_of_copy_row)
latest_date_loaded = Application.WorksheetFunction.Max(ThisWorkbook.Sheets(sh.Name).Range("A:A"))
ThisWorkbook.Sheets(sh.Name).Cells(Rows.Count, 1).End(xlUp).Offset(1).PasteSpecial xlPasteValues
On Error GoTo 0
Application.CutCopyMode = False
Next sh
Workbooks(wb).Close False
End If
wb = Dir
Loop
Application.ScreenUpdating = True
End Sub
Kindly please see how consolidated workbook appear(the sheet names and column format are exactly the same as of the source workbooks.)
CONSOLIDATED WORKBOOK
The following line can be used to find the latest date loaded on your consolidated sheet:
latest_date_loaded = Application.WorksheetFunction.Max(ThisWorkbook.Sheets(sh.Name).Range("A:A"))
The following lines can be used on a worksheet (sh) to create a range (for copying) that starts after the latest_date_loaded down to the bottom of the table. You'll therefore need to ensure this is in date order.
Dim fndRng As Range, start_of_copy_row As Long, end_of_copy_row As Long, range_to_copy As Range
date_to_find = latest_date_loaded
Set fndRng = sh.Range("A:A").Find(date_to_find, LookIn:=xlValues, searchdirection:=xlPrevious)
If Not fndRng Is Nothing Then
start_of_copy_row = fndRng.Row + 1
Else
start_of_copy_row = 2 ' assuming row 1 has a header you want to ignore
End If
end_of_copy_row = sh.Cells(sh.Rows.Count, "A").End(xlUp).Row
Set range_to_copy = Range(start_of_copy_row & ":" & end_of_copy_row)
EDIT
Here is a rework of your code, using some of the lines/ideas I've mentioned above.
Sub Copy_From_All_Workbooks()
'declarations
Dim wb As String, i As Long, sh As Worksheet, fndRng As Range, _
start_of_copy_row As Long, end_of_copy_row As Long, range_to_copy As _
Range, latest_date_loaded As Date, consolidated_wb As Workbook
'turn off screen updating for user experience
'Application.ScreenUpdating = False
'set a reference to the consolidated workbook
Set consolidated_wb = ThisWorkbook
'read parent folder of consolidated workbook
wb = Dir(consolidated_wb.Path & "\*")
'perform this loop until no more files
Do Until wb = ""
'make sure it doesn't try to open consolidated workbook (again)
If wb <> consolidated_wb.Name Then
'open found source workbook
Workbooks.Open consolidated_wb.Path & "\" & wb
'cycle through each sheet (sh)
For Each sh In Workbooks(wb).Worksheets
'on that sheet, find the latest date already existing
latest_date_loaded = Application.WorksheetFunction.Max(consolidated_wb.Sheets(sh.Name).Range("A:A"))
'find the last occurence of that date in column A
Set fndRng = sh.Range("A:A").Find(latest_date_loaded, LookIn:=xlValues, _
searchdirection:=xlPrevious)
'if you find that date already then..
If Not fndRng Is Nothing Then
'set the top row to where you found it, plus one
start_of_copy_row = fndRng.Row + 1
Else
'otherwise, it's a new sheet, start on row two
start_of_copy_row = 2 ' assuming row 1 has a header you want to ignore
End If
'find the end of the table, using column A's contents
end_of_copy_row = sh.Cells(sh.Rows.Count, "A").End(xlUp).Row
'make sure there's something to copy
If end_of_copy_row >= start_of_copy_row Then
'create a reference to the block of cells to copy
Set range_to_copy = sh.Range(start_of_copy_row & ":" & end_of_copy_row)
'copy that range
range_to_copy.Copy
'paste them, values only
consolidated_wb.Sheets(sh.Name).Cells(Rows.Count, 1).End(xlUp).Offset(1).PasteSpecial xlPasteValues
'clear copy markings from screen
Application.CutCopyMode = False
Else
'otherwise, do nothing here
End If
Next sh
'close the source workbook
Workbooks(wb).Close False
End If
'get next potential filename
wb = Dir
Loop
'turn back on screen updating
Application.ScreenUpdating = True
End Sub
I have got a worksheet with some data. I store that data in an array and then I want to create a new worksheet and save the data into a new worksheet.
Right now I'm creating a new sheet in the workbook of origin data like this:
Sub New_workbook()
Dim sh as Worksheet, origin as Worksheet, arr
origin = Sheets("OriginSheet")
sh = ActiveSheet
somedata = origin.Range("A1:C").Value
ReDim arr(1 To 100, 1 To 3)
For i = 1 To 100
arr(i, 1) = somedata(i, 1)
arr(i, 2) = somedata(i, 2)
arr(i, 3) = somedata(i, 3)
Next i
sh.Range("A2").Resize(UBound(arr), UBound(arr, 2)).Value = arr
End Sub
and instead of sh = ActiveSheet, I would like to have something like sh = NewWorkbook("Name_of_new_workbook") and create a workbook in the directory of OriginSheet workbook or given path and fill it with arr values. How can I do this in VBA?
If you are looking to copy all the data in your source range, it isn't necessary to store that data in an array first. Just Set your range and make the value of the destination range equal the value of the source range. Try something like this:
Sub CopyRangeIntoNewWorkbook()
'disabling screen update and calculation to speed things up
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Dim wb As Workbook, wb_new As Workbook
Dim ws As Worksheet
Dim rng As Range
Set wb = ActiveWorkbook
Set ws = ActiveSheet
'set the rng for which you want to copy the values
Set rng = ws.Range("A1:C10")
'set wb_new to newly added wb
Set wb_new = Workbooks.Add()
'specify the top left cell of the range you want to have populated in the new wb
wb_new.Sheets(1).Range("A1").Resize(rng.Rows.Count, rng.Columns.Count).Value2 = rng.Value2
'save file, here using path of your original wb'
wb_new.SaveAs Filename:=wb.path & "\wb_new.xlsx"
'closing the new file
wb_new.Close saveChanges:=False
'enabling screen update and automatic calculation again
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
End Sub
The most eficient way to copy a sheet content in a new workbook should be the next one:
Sub New_workbook()
Dim origin As Worksheet
Set origin = Sheets("OriginSheet") 'an object must be Set
origin.Copy 'this will create a new workbook with the content of the copied sheet
ActiveWorkbook.saveas origin.Parent.path & "\" & "Name_of_new_workbook" & ".xlsx", xlWorkbookDefault
End Sub
If needing to keep only columns "A:C", you can add the next code lines:
Dim sh As Worksheet, lastCol As Long
Set sh = ActiveWorkbook.Worksheets(1)
lastCol = sh.cells.SpecialCells(xlCellTypeLastCell).Column
If lastCol <= 3 Then Exit Sub
If lastCol = 4 Then sh.cells(1, 4).EntireColumn.Delete: Exit Sub
sh.Range(sh.cells(1, 4), sh.cells(1, lastCol)).EntireColumn.Delete
I want to export data from master Excel to CSV files.
As you can see, first row is header, and every two rows will be copied into new CSV file. The CSV file is named by City (from row without diacritics) and by predefined words.
Here is my macro:
Sub Macro1()
Dim newWB As Workbook
Dim myWS As Worksheet
Dim lastRow As Long
Set myWS = ThisWorkbook.Worksheets("ExportSamospravy")
With myWS
lastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
For x = 2 To lastRow
Set newWB = Workbooks.Add
newWB.Sheets("Hárok1").Rows(1) = myWS.Rows(1).Value
newWB.Sheets("Hárok1").Rows(2) = myWS.Rows(x).Value
newWB.Sheets("Hárok1").Rows(3) = myWS.Rows(x).Value
newWB.SaveAs Filename:="C:\Users\andre\OneDrive\prispevky\smart-cities\data\1-2021\Export\EconomySamospravy\" & Range("I2").Value & "EconomySamospravy", FileFormat:=xlCSV
Columns(9).EntireColumn.Delete
Columns(9).EntireColumn.Delete
newWB.Close True
Next x
End With
End Sub
My issue is that only row 2019 is multiplied into second row. Also it has issue with saving.
I found solution
For x = 2 To lastRow Step 2
n = x + 1
Set newWB = Workbooks.Add
Now it exports perfectly!
Given a workbook like this one:
I need to add the logo from cell A2 - worksheet A, in the footer of worksheets B,C.
Here's the code I've found and modified a little bit but it is not working.
Sub Logo()
Dim printWorksheet As Worksheet
Dim logoShape As Shape
Dim tempImageFile As String
Set printWorksheet = ThisWorkbook.ActiveSheet
Set logoShape = ThisWorkbook.Sheets("A").Shapes("myLogo")
tempImageFile = Environ("temp") & Application.PathSeparator & "image.jpg"
Save_Object_As_Picture logoShape, tempImageFile
With printWorksheet.PageSetup
.RightHeaderPicture.FileName = tempImageFile
.RightHeader = "&G"
End With
I have found a solutions (http://www.vbforums.com/showthread.php?538529-Export-an-Image-from-Excel-Sheet-to-Hard-Drive), that I have adopted to this task.
The key is, that a chart object can be exported as a picture, so the original shape is copied into a chart.
The chart is created, used, and deleted.
The ShapeExportAsPicture has two arguments: the shape, that is to be exported as picture and the full path where to store it.
Sub Logo()
Dim printWorksheet As Worksheet
Dim logoShape As Shape
Dim tempImageFile As String
Set printWorksheet = ThisWorkbook.ActiveSheet
Set logoShape = ThisWorkbook.Sheets("A").Shapes("myLogo")
logoShape.Visible = True
tempImageFile = Environ("temp") & Application.PathSeparator & "image.jpg"
Call ShapeExportAsPicture(logoShape, tempImageFile)
With printWorksheet.PageSetup
.RightFooterPicture.Filename = tempImageFile
.RightFooter = "&G"
End With
logoShape.Visible = False
End Sub
Private Sub ShapeExportAsPicture(pShape As Shape, sPathImageLocation As String)
Dim sTempChart As String
Dim shTempSheet As Worksheet
Set shTempSheet = pShape.Parent
Charts.Add 'Add a temporary chart
ActiveChart.Location Where:=xlLocationAsObject, Name:=shTempSheet.Name
Selection.Border.LineStyle = 0
sTempChart = Selection.Name & " " & Split(ActiveChart.Name, " ")(2)
With shTempSheet
'Change the dimensions of the chart to the size of the original shape
With .Shapes(sTempChart)
.Width = pShape.Width
.Height = pShape.Height
End With
pShape.Copy 'Copy the shape
With ActiveChart 'Paste the shape into the chart
.ChartArea.Select
.Paste
End With
'export the chart
.ChartObjects(1).Chart.Export Filename:=sPathImageLocation, FilterName:="jpg"
.Shapes(sTempChart).Delete 'Delete the chart.
End With
End Sub
I am trying to compile data from one large workbook (that will be downloaded once a month) to one that is more concise. I will be pulling in new data every month. I will know the name of the source file and it's location.
Below is the code I am trying to run. It appears to run without errors (going thru all the FOR's and Do Until's) but is just not moving the data from the source file to the destination file. The variable information I am using is column O starting on line 14 of the destination WB. I am trying to sort thru column A of the source WB for some text and the variable from the destination WB. If I have a match I am trying to offset from the matching cell (down 3 rows and right 2 columns) and copy that information to an offset cell on the destination WB (left 4 columns on the same row). Also copying from down 10 rows and right 2 columns on the source to down row 1 and left 4 columns on the destination.
Sub Get_Scorecard()
Dim SourceFile As String
Dim DestFile As String
Dim SourceWB As Workbook
Dim SourceWS As Worksheet
Dim DestWB As Workbook
Dim DestWS As Worksheet
Dim path As String
Dim Msg As String
Dim SCount As Long
Dim sourcestart As Range
Dim TechName As String
'Set starting cell on Dest WS
Range("O14").Activate
Application.ScreenUpdating = False
Application.DisplayAlerts = False
Application.EnableEvents = False
'Set all the WB's and WS's
path = Application.ThisWorkbook.path & "\"
SourceFile = path & "March Test.xlsx"
DestFile = path & "JobSteps 2019 Test.xlsm"
Set SourceWB = Application.Workbooks.Open(SourceFile)
Set SourceWS = SourceWB.Sheets(1)
Set DestWB = Application.Workbooks.Open(DestFile)
Set DestWS = DestWB.Sheets(1)
'Start in O14 on the Dest WS and loop down till column O is empty
Do Until IsEmpty(ActiveCell.Value)
TechName = ActiveCell.Value
DestStart = ActiveCell.Address
'Start in Cell A2 on the soure WS and search for tech from Dest WS
For SCount = 2 To 700
If SourceWS.Range("A" & SCount).Text = "Provisioning*" & _
TechName & "*" Then
'copy info from 2 offset cells from SourceWS to 2 offset cells on DestWS
'I am offseting 4 columns to left on the DestWS just to see if they appear
DestWS.Range(DestStart).Offset(0, -4).Value = SourceWS.Range(SourceWS.Range _
("A" & SCount).Address).Offset(3, 2).Text
DestWS.Range(DestStart).Offset(-1, -4).Value = SourceWS.Range(SourceWS.Range _
("A" & SCount).Address).Offset(10, 2).Text
End If
Next SCount
'Offset active cell on DestWS by 4 rows
ActiveCell.Offset(4, 0).Activate
Loop
'Close SourceWB
SourceWB.Close
Application.ScreenUpdating = True
Application.DisplayAlerts = True
Application.EnableEvents = True
Range("A1").Activate
End Sub