I'm writing a code to populate a data table then take and save an image of it by calling a module someone else made. My portion of the code is able to create the image and the sub that gets called works for creating images for other tables, but I think something is missing in my portion. This is the for loop to cycle through different product lines and create images of their tables.
For i = 0 To UBound(allLines)
Cells(bcell, 2) = Cells(product, 2)
Cells(bcell, 3) = Cells(product, 3)
Cells(bcell, 4) = Cells(product, 4)
Cells(bcell, 5) = Cells(product, 5)
Cells(ecell, 2) = Cells(product, 6)
Cells(ecell, 3) = Cells(product, 7)
Cells(ecell, 4) = Cells(product, 8)
Cells(ecell, 5) = Cells(product, 10)
Range(Cells(acell, 1), Cells(ecell, 5)).Select
Selection.Copy
Range("M14").Select
ActiveSheet.Pictures.Paste.Select
myfilename = Year(Now) & " " & MonthName(Month(Now)) & " " & allLines(i) & " Production Status Metrics" ' & ".jpg"
EndFilePath = "C:\Users\*******\Documents\**********\TEST FILES\" & myfilename 'edited for privacy
Call ExportMyPicture(allLines(i), EndFilePath) 'Module: Export_Cells_to_File
Range("A1").Select
acell = acell + 5
ecell = ecell + 5
bcell = bcell + 5
product = product + 1
Next
This is the sub that gets called up to the line that gives me an error
Sub ExportMyPicture(SelectedLine As String, EndFilePath As String)
Dim MyChart As String, MyPicture As String
Dim PicWidth As Long, PicHeight As Long
Application.ScreenUpdating = False
MyPicture = Selection.Name
With Selection
PicHeight = .ShapeRange.Height
PicWidth = .ShapeRange.Width
End With
Charts.Add
ActiveChart.Location Where:=xlLocationAsObject, Name:=SelectedLine & " Actions" 'This line gives the error
The error message reads object variable or with block variable not set
It seems that you are trying to make an embedded chart. In this case the chart has a container object called a chart object which sits between the chart and the containing sheet. Rather than creating a chart and then adjusting its location, you can add it as a chart object in the target sheet. Something like:
Sub test()
Dim mySheet As Worksheet
Set mySheet = Sheets(1)
Dim PicWidth As Long, PicHeight As Long
PicWidth = 200
PicHeight = 100
Dim CO As ChartObject
Dim CH As Chart
Set CO = mySheet.ChartObjects.Add(10, 10, PicWidth, PicHeight)
Set CH = CO.Chart
CH.ChartType = xlXYScatter
CO.Activate
End Sub
The 4 parameters of the Add method are left, top. width, height.
Related
I have several sheets in my workbook which contains data to plot, every time I run a new analysis a new sheet is generated.
On my first sheet I plot all the data in the same graph, so to avoid re plotting all the series every time I append a new sheet I would like to just add a new series.
I thought that should be simple, but it is not for two reasons: When I first create the chart it adds somewhere between 1 and 9 series automatically:
Set myChart = ws.Shapes.AddChart.Chart
myChart.ChartType = xlXYScatterLinesNoMarkers
why does this generate any random series?
also if I delete the graph because I want to rerun one analysis, the graph will then be called 2 and so on... So I tried to give it a name and refer to its name instead, however that does not work:
Set myChart = ws.ChartObjects(ws.Name)
So in the first sheet(Orginal) I plot all data in the workbook, and in the rest I just plot the data for the current sheet as seen below. I use the same code function for both cases, where i just pass the argument all as true(orginal sheet) or false(sheet 1.....300)
Below is the code:
Sub createChart(ws As Worksheet, Optional all As Boolean = False)
Dim lastRow As Long
Dim myChart As Chart
Dim temp As Integer
Dim n As Integer
On Error Resume Next
' Delete the charts, just in case
If ws.ChartObjects.Count > 0 Then ' And Not all Then
ws.ChartObjects.Delete
End If
'If ws.ChartObjects.Count = 0 Then
Set myChart = ws.Shapes.AddChart.Chart
myChart.Name = ws.Name
'Else
'Set myChart = ws.ChartObjects(ws.Name) '''Fails why commented out
'End If
myChart.ChartType = xlXYScatterLinesNoMarkers
myChart.SetElement (msoElementPrimaryCategoryGridLinesMinor)
myChart.SetElement (msoElementPrimaryValueGridLinesMinorMajor)
myChart.SetElement (msoElementLegendBottom)
myChart.SetElement (msoElementChartTitleCenteredOverlay)
myChart.Parent.width = 800 ' px width graph
myChart.Parent.height = 500 ' px height graph
' it adds mysterious sometimes several random series, so we need to delete those that does not match sheet name
For n = myChart.SeriesCollection.Count To 0 Step -1
If Not SheetExists(myChart.SeriesCollection(n).Name) Then
myChart.SeriesCollection(n).Delete
End If
Next n
'*******************************************************************
'**************** FIRST PAGE CHART *********************************
'*******************************************************************
If all Then
Dim wsOther As Worksheet
Dim i As Integer
Dim fixRange As Boolean
Dim skipGraph As Boolean
fixRange = True
myChart.HasLegend = True
myChart.Legend.Position = xlLegendPositionRight
myChart.Parent.Top = 120
myChart.Parent.Left = 450
For Each wsOther In ThisWorkbook.Worksheets
If wsOther.Name <> ws.Name Then
lastRow = getLastRow(wsOther, 1)
skipGraph = False
'******* we only add graphs if it is not before ******************
If myChart.SeriesCollection.Count > 0 Then
For n = myChart.SeriesCollection.Count To 1 Step -1
If myChart.SeriesCollection(n).Name = wsOther.Name Then
skipGraph = True
Exit For
End If
Next n
End If
If Not skipGraph Then
With myChart.SeriesCollection.NewSeries
.Values = "=" & wsOther.Name & "!$E$2:$E$" & lastRow
.Name = wsOther.Name
.XValues = "=" & wsOther.Name & "!$B$2:$B$" & lastRow
End With
End If
If fixRange Then
' Range on axis
myChart.Axes(xlPrimary).MinimumScale = CDate(Application.WorksheetFunction.Min(Range(wsOther.Name & "!$B$2:$B$" & lastRow).Value2))
myChart.Axes(xlPrimary).MaximumScale = CDate(Application.WorksheetFunction.Max(Range(wsOther.Name & "!$B$2:$B$" & lastRow).Value2))
myChart.Axes(xlValue, xlPrimary).ScaleType = xlLogarithmic
fixRange = False
End If
End If
Next
'*******************************************************************************************************
'****************** SINGLE CHART ***********************************************************************
'*******************************************************************************************************
Else
myChart.HasLegend = False
myChart.Parent.Top = 40
myChart.Parent.Left = 300
lastRow = getLastRow(ws, 1)
With myChart.SeriesCollection.NewSeries
.Values = "=" & ws.Name & "!$E$2:$E$" & lastRow
.XValues = "=" & ws.Name & "!$B$2:$B$" & lastRow
End With
' Range on axis
myChart.Axes(xlPrimary).MinimumScale = CDate(Application.WorksheetFunction.Min(Range(ws.Name & "!$B$2:$B$" & lastRow).Value2))
myChart.Axes(xlPrimary).MaximumScale = CDate(Application.WorksheetFunction.Max(Range(ws.Name & "!$B$2:$B$" & lastRow).Value2))
End If
' *********************************************************************
' ******************* Sizing ******************************************
' *********************************************************************
With myChart.PlotArea
temp = .Top
temp = .height
.Top = 70
.height = 420
End With
'really dirty and crappy formatting of title
myChart.ChartTitle.Text = "Faraday Torr"
'X axis name
myChart.Axes(xlCategory, xlPrimary).HasTitle = True
myChart.Axes(xlCategory, xlPrimary).AxisTitle.Characters.Text = "Time [s]"
'y-axis name
myChart.Axes(xlValue, xlPrimary).HasTitle = True
myChart.Axes(xlValue, xlPrimary).AxisTitle.Characters.Text = "Pressure[Torr]"
Set myChart = Nothing
Set wsOther = Nothing
ws.Select
ws.Range("A1").Select
End Sub
I am currently working on the following code which is searching through all tabs in an excel workbook, selects all currencies greater a certain threshold in a defined column "J" and if criteria is met the line containing the currency that is greater threshold is pasted in a new created tab called "summary".
Now my question is:
1. Is there any chance to make this code more interactive? What I would like to do, is to add an inputbox in which the user is typing his threshold (in my example 1000000) and this threshold is used for looping through all tabs.
2. It would be great to get an input box like "select column containing currency", as column "J" won't be set all time, it could also be another column ("I", "M" etc) however this will be the same for all sheets then.
3. Any chance to select certain sheets within workbook (STRG + "sheetx" "sheety" etc....) which are then pasted into my loop and all others are neglected?
Any help, especially for my issues within question 1 and 2 is appreciated. Question 3 would only be a "nice-to-have" thing
Option Explicit
Sub Test()
Dim WS As Worksheet
Set WS = Sheets.Add
WS.Name = "Summary"
Dim i As Long, j As Long, lastRow As Long
Dim sh As Worksheet
With Sheets("Summary")
.Cells.Clear
End With
j = 2
For Each sh In ActiveWorkbook.Sheets
If sh.Name <> "Summary" Then
lastRow = sh.Cells(sh.Rows.Count, "A").End(xlUp).Row
For i = 4 To lastRow
If sh.Range("J" & i) > 1000000 Or sh.Range("J" & i) < -1000000 Then
sh.Range("a" & i & ":n" & i).Copy Destination:=Worksheets("Summary").Range("A" & j)
Sheets("Summary").Range("N" & j) = sh.Name
j = j + 1
End If
Next i
End If
Next sh
Sheets("Summary").Columns("A:N").AutoFit
End Sub
You may want to try this
Option Explicit
Sub Test()
Dim WS As Worksheet
Dim i As Long, j As Long, lastRow As Long
Dim sh As Worksheet
Dim sheetsList As Variant
Dim threshold As Long
Set WS = GetSheet("Summary", True)
sheetsList = Array("STRG","sheetx","sheety") '<--| fill this array with the sheets names to be looped through
threshold = Application.InputBox("Input threshold", Type:=1)
j = 2
For Each sh In ActiveWorkbook.Sheets(sheetsList)
lastRow = sh.Cells(sh.Rows.Count, "A").End(xlUp).Row
For i = 4 To lastRow
If sh.Range("J" & i) > threshold Or sh.Range("J" & i) < -threshold Then
sh.Range("a" & i & ":n" & i).Copy Destination:=WS.Range("A" & j)
WS.Range("N" & j) = sh.Name
j = j + 1
End If
Next i
Next sh
WS.Columns("A:N").AutoFit
End Sub
Function GetSheet(shtName As String, Optional clearIt As Boolean = False) As Worksheet
On Error Resume Next
Set GetSheet = Worksheets(shtName)
If GetSheet Is Nothing Then
Set GetSheet = Sheets.Add(after:=Worksheets(Worksheets.count))
GetSheet.Name = shtName
End If
If clearIt Then GetSheet.UsedRange.Clear
End Function
You can set a UserForm as input into the program - something like what follows. You only need to run the 'CreateUserForm' sub once to get the UserForm1 event handlers set up in your spreadsheet. Once that's done you can run the 'Test' to see the UserForm1 itself. You can edit the event handlers to check the user input or reject it if need be. Also once the UserForm1 is set up you can move the various labels and listboxes around and, of course, create new ones. It should look like this:
You can select as many sheets as required from the last listbox and the selections will be added to a vba Collection. See the MsgBox at the beginning of your code and play with entering values/selections into the user box to see what it does.
The UserForm handler that's called when you press the okay button will save the selections to global variables so that they can be picked up in the code.
Option Explicit
' Global Variables used by UserForm1
Public lst1BoxData As Variant
Public threshold As Integer
Public currencyCol As String
Public selectedSheets As Collection
' Only need to run this once. It will create UserForm1.
' If run again it will needlessly create another user form that you don't need.
' Once it's run you can modify the event handlers by selecting the UserForm1
' object in the VBAProject Menu by right clicking on it and selecting 'View Code'
' Note that you can select multiple Sheets on the last listbox of the UserForm
' simply by holding down the shift key.
Sub CreateUserForm()
Dim myForm As Object
Dim X As Integer
Dim Line As Integer
'This is to stop screen flashing while creating form
Application.VBE.MainWindow.Visible = False
Set myForm = ThisWorkbook.VBProject.VBComponents.Add(3)
'Create the User Form
With myForm
.Properties("Caption") = "Currency Settings"
.Properties("Width") = 322
.Properties("Height") = 110
End With
' Create Label for threshold text box
Dim thresholdLabel As Object
Set thresholdLabel = myForm.Designer.Controls.Add("Forms.Label.1")
With thresholdLabel
.Name = "lbl1"
.Caption = "Input Threshold:"
.Top = 6
.Left = 6
.Width = 72
End With
'Create TextBox for the threshold value
Dim thresholdTextBox As Object
Set thresholdTextBox = myForm.Designer.Controls.Add("Forms.textbox.1")
With thresholdTextBox
.Name = "txt1"
.Top = 18
.Left = 6
.Width = 75
.Height = 16
.Font.Size = 8
.Font.Name = "Tahoma"
.borderStyle = fmBorderStyleSingle
.SpecialEffect = fmSpecialEffectSunken
End With
' Create Label for threshold text box
Dim currencyLabel As Object
Set currencyLabel = myForm.Designer.Controls.Add("Forms.Label.1")
With currencyLabel
.Name = "lbl2"
.Caption = "Currency Column:"
.Top = 6
.Left = 100
.Width = 72
End With
'Create currency column ListBox
Dim currencyListBox As Object
Set currencyListBox = myForm.Designer.Controls.Add("Forms.listbox.1")
With currencyListBox
.Name = "lst1"
.Top = 18
.Left = 102
.Width = 52
.Height = 55
.Font.Size = 8
.Font.Name = "Tahoma"
.borderStyle = fmBorderStyleSingle
.SpecialEffect = fmSpecialEffectSunken
End With
' Create Label for sheet text box
Dim sheetLabel As Object
Set sheetLabel = myForm.Designer.Controls.Add("Forms.Label.1")
With sheetLabel
.Name = "lbl3"
.Caption = "Select Sheets:"
.Top = 6
.Left = 175
.Width = 72
End With
'Create currency column ListBox
Dim sheetListBox As Object
Set sheetListBox = myForm.Designer.Controls.Add("Forms.listbox.1")
With sheetListBox
.Name = "lst3"
.Top = 18
.Left = 175
.Width = 52
.Height = 55
.Font.Size = 8
.MultiSelect = 1
.Font.Name = "Tahoma"
.borderStyle = fmBorderStyleSingle
.SpecialEffect = fmSpecialEffectSunken
End With
'Create Select Button
Dim selectButton As Object
Set selectButton = myForm.Designer.Controls.Add("Forms.commandbutton.1")
With selectButton
.Name = "cmd1"
.Caption = "Okay"
.Accelerator = "M"
.Top = 30
.Left = 252
.Width = 53
.Height = 20
.Font.Size = 8
.Font.Name = "Tahoma"
.BackStyle = fmBackStyleOpaque
End With
' This will create the initialization sub and the click event
' handler to write the UserForm selections into the global
' variables so they can be used by the code.
myForm.CodeModule.InsertLines 1, "Private Sub UserForm_Initialize()"
myForm.CodeModule.InsertLines 2, " me.lst1.addItem ""Column I"" "
myForm.CodeModule.InsertLines 3, " me.lst1.addItem ""Column J"" "
myForm.CodeModule.InsertLines 4, " me.lst1.addItem ""Column M"" "
myForm.CodeModule.InsertLines 5, " me.lst3.addItem ""Sheet X"" "
myForm.CodeModule.InsertLines 6, " me.lst3.addItem ""Sheet Y"" "
myForm.CodeModule.InsertLines 7, " lst1BoxData = Array(""I"", ""J"", ""M"")"
myForm.CodeModule.InsertLines 8, "End Sub"
'add code for Command Button
myForm.CodeModule.InsertLines 9, "Private Sub cmd1_Click()"
myForm.CodeModule.InsertLines 10, " threshold = CInt(Me.txt1.Value)"
myForm.CodeModule.InsertLines 11, " currencyCol = lst1BoxData(Me.lst1.ListIndex)"
myForm.CodeModule.InsertLines 12, " Set selectedSheets = New Collection"
myForm.CodeModule.InsertLines 13, " For i = 0 To Me.lst3.ListCount - 1"
myForm.CodeModule.InsertLines 14, " If Me.lst3.Selected(i) = True Then"
myForm.CodeModule.InsertLines 15, " selectedSheets.Add Me.lst3.List(i)"
myForm.CodeModule.InsertLines 16, " End If"
myForm.CodeModule.InsertLines 17, " Next"
myForm.CodeModule.InsertLines 18, " Unload Me"
myForm.CodeModule.InsertLines 19, "End Sub"
'Add form to make it available
VBA.UserForms.Add (myForm.Name)
End Sub
' This is your code verbatim except for now
' the UserForm is shown for selecting the
' 1) currency threshold, 2) the column letter
' and 3) the sheets you want to process.
' The MsgBox just shows you what you've
' selected just to demonstrate that it works.
Sub Test()
Dim WS As Worksheet
Set WS = Sheets.Add
WS.Name = "Summary"
Dim i As Long, j As Long, lastRow As Long
Dim sh As Worksheet
With Sheets("Summary")
.Cells.Clear
End With
'**** Start: Running & Checking UserForm Output ****
UserForm1.Show
Dim colItem As Variant
Dim colItems As String
For Each colItem In selectedSheets:
colItems = colItems & " " & colItem
Next
MsgBox ("threshold=" & threshold & vbCrLf & _
"currencyCol=" & currencyCol & vbCrLf & _
"selectedSheets=" & colItems)
'**** End: Running & Checking UserForm Output ****
j = 2
For Each sh In ActiveWorkbook.Sheets
If sh.Name <> "Summary" Then
lastRow = sh.Cells(sh.Rows.Count, "A").End(xlUp).row
For i = 4 To lastRow
If sh.Range("J" & i) > 1000000 Or sh.Range("J" & i) < -1000000 Then
sh.Range("a" & i & ":n" & i).Copy Destination:=Worksheets("Summary").Range("A" & j)
Sheets("Summary").Range("N" & j) = sh.Name
j = j + 1
End If
Next i
End If
Next sh
Sheets("Summary").Columns("A:N").AutoFit
End Sub
The following code works for my purposes except the selection of single tabs to loop through:
Option Explicit
Sub Test()
Dim column As String
Dim WS As Worksheet
Dim i As Long, j As Long, lastRow As Long
Dim sh As Worksheet
Dim sheetsList As Variant
Dim threshold As Long
Set WS = GetSheet("Summary", True)
threshold = Application.InputBox("Input threshold", Type:=1)
column = Application.InputBox("Currency Column", Type:=2)
j = 2
For Each sh In ActiveWorkbook.Sheets
If sh.Name <> "Summary" Then
lastRow = sh.Cells(sh.Rows.Count, "A").End(xlUp).Row
For i = 4 To lastRow
If sh.Range(column & i) > threshold Or sh.Range(column & i) < -threshold Then
sh.Range("a" & i & ":n" & i).Copy Destination:=WS.Range("A" & j)
WS.Range("N" & j) = sh.Name
j = j + 1
End If
Next i
End If
Next sh
WS.Columns("A:N").AutoFit
End Sub
Function GetSheet(shtName As String, Optional clearIt As Boolean = False) As Worksheet
On Error Resume Next
Set GetSheet = Worksheets(shtName)
If GetSheet Is Nothing Then
Set GetSheet = Sheets.Add(after:=Worksheets(Worksheets.Count))
GetSheet.Name = shtName
End If
If clearIt Then GetSheet.UsedRange.Clear
End Function
I have written this code to take data from one workbook, put it into an array, then place the data in an empty row in another workbook. It works until it gets to i=25 in the For loop where it adds the hyperlink. The hyperlink is actually correctly added, and functions properly, but when I step through the line it gives me a "Application-Defined or Object-Define Error" even though the line added the hyperlink correctly.
Any help would be greatly appreciated. I've been stuck on this for a few days, and have tried many adjustments.
Private Sub CopyDataToMatrix()
'This macro copies the data from the process sheet & automatically pastes it into
'the matrix.
Dim wb1 As Workbook
Dim wb2 As Workbook
Dim ws1 As Worksheet
Dim ws2 As Worksheet
Dim Data(1 To 26)
Dim EmptyRow As Range
Dim strSearch As String
Dim rngSearch As Range
Dim rowNum As Integer
Set wb1 = ActiveWorkbook
Set wb2 = Workbooks.Open("***ForPrivacy***")
Set ws1 = wb1.Sheets("ProcessData")
Set ws2 = wb2.Sheets("2016")
'Put all of the data into an array:
Data(1) = ws1.Range("B57").Value
Data(2) = ws1.Range("B3").Value
Data(3) = ws1.Range("B4").Value
Data(4) = ws1.Range("B5").Value
Data(5) = ws1.Range("F7").Value
Data(6) = ws1.Range("B6").Value
Data(7) = ws1.Range("B7").Value
Data(8) = ws1.Range("F8").Value
Data(9) = ws1.Range("B8").Value
Data(10) = ws1.Range("B9").Value
Data(11) = ws1.Range("B10").Value
Data(12) = ws1.Range("F9").Value
Data(13) = ws1.Range("F4").Value
Data(14) = ws1.Range("F5").Value
Data(15) = ws1.Range("F6").Value
Data(16) = ws1.Range("G4").Value
Data(17) = ws1.Range("G5").Value
Data(18) = ws1.Range("G6").Value
Data(19) = ws1.Range("H4").Value
Data(20) = ws1.Range("H5").Value
Data(21) = ws1.Range("H6").Value
Data(22) = ws1.Range("I4").Value
Data(23) = ws1.Range("I5").Value
Data(24) = ws1.Range("I5").Value
Data(25) = Left(wb1.Name, 8)
'IM MATRIX:
'Look to see if the row already exists in IM Matrix with the current file name, and if so overwrite it:
strSearch = Left(wb1.Name, 8)
Set rngSearch = ws2.Range("Y:Y")
If Application.CountIf(rngSearch, strSearch) > 0 Then
rowNum = Application.Match(strSearch, rngSearch, 0)
With ws2
Set EmptyRow = .Cells(rowNum, 1)
For i = LBound(Data) To 24
EmptyRow.Offset(0, i - 1).Value = Application.Index(Data, i)
Next i
For i = 25 To 25
EmptyRow.Offset(0, i - 1).Value = ws2.Hyperlinks.Add(EmptyRow.Offset(0, i - 1), wb1.FullName, , "Click to go to IML file.", Data(i))
Next i
End With
'If the file name isn't already in IM Matrix, then enter data in new row:
Else
With ws2
Set EmptyRow = .Cells(.Rows.Count, 1).End(xlUp).Offset(1)
For i = LBound(Data) To 24
EmptyRow.Offset(0, i - 1).Value = Application.Index(Data, i)
Next i
For i = 25 To 25
**''HERE IS WHERE THE CODE BUGS:**
**EmptyRow.Offset(0, i - 1).Value = ws2.Hyperlinks.Add(EmptyRow.Offset(0, i - 1), wb1.FullName, , "Click to go to IML file.", Data(i))**
Next i
End With
End If
'Close & save IM Matrix file:
wb2.Close SaveChanges:=True
End Sub
Here is the solution that worked with the help of #JMichael:
Private Sub CopyDataToMatrix()
'This macro copies the data from the process sheet & automatically pastes it into
'the matrix.
Dim wb1 As Workbook
Dim wb2 As Workbook
Dim ws1 As Worksheet
Dim ws2 As Worksheet
Dim Data(1 To 26)
Dim EmptyRow As Range
Dim strSearch As String
Dim rngSearch As Range
Dim rowNum As Integer
Set wb1 = ActiveWorkbook
Set wb2 = Workbooks.Open("***ForPrivacy***")
Set ws1 = wb1.Sheets("ProcessData")
Set ws2 = wb2.Sheets("2016")
'Put all of the data into an array:
Data(1) = ws1.Range("B57").Value
Data(2) = ws1.Range("B3").Value
Data(3) = ws1.Range("B4").Value
Data(4) = ws1.Range("B5").Value
Data(5) = ws1.Range("F7").Value
Data(6) = ws1.Range("B6").Value
Data(7) = ws1.Range("B7").Value
Data(8) = ws1.Range("F8").Value
Data(9) = ws1.Range("B8").Value
Data(10) = ws1.Range("B9").Value
Data(11) = ws1.Range("B10").Value
Data(12) = ws1.Range("F9").Value
Data(13) = ws1.Range("F4").Value
Data(14) = ws1.Range("F5").Value
Data(15) = ws1.Range("F6").Value
Data(16) = ws1.Range("G4").Value
Data(17) = ws1.Range("G5").Value
Data(18) = ws1.Range("G6").Value
Data(19) = ws1.Range("H4").Value
Data(20) = ws1.Range("H5").Value
Data(21) = ws1.Range("H6").Value
Data(22) = ws1.Range("I4").Value
Data(23) = ws1.Range("I5").Value
Data(24) = ws1.Range("I5").Value
Data(25) = Left(wb1.Name, 8)
'IM MATRIX:
'Look to see if the row already exists in IM Matrix with the current file name, and if so overwrite it:
strSearch = Left(wb1.Name, 8)
Set rngSearch = ws2.Range("Y:Y")
If Application.CountIf(rngSearch, strSearch) > 0 Then
rowNum = Application.Match(strSearch, rngSearch, 0)
With ws2
Set EmptyRow = .Cells(rowNum, 1)
For i = LBound(Data) To 24
EmptyRow.Offset(0, i - 1).Value = Application.Index(Data, i)
Next i
ws2.Hyperlinks.Add EmptyRow.Offset(0, 24), wb1.FullName, , "Click to go to IML file.", Data(25)
End With
'If the file name isn't already in IM Matrix, then enter data in new row:
Else
With ws2
Set EmptyRow = .Cells(.Rows.Count, 1).End(xlUp).Offset(1)
For i = LBound(Data) To 24
EmptyRow.Offset(0, i - 1).Value = Application.Index(Data, i)
Next i
ws2.Hyperlinks.Add EmptyRow.Offset(0, 24), wb1.FullName, , "Click to go to IML file.", Data(25)
End With
End If
'Close & save IM Matrix file:
wb2.Close SaveChanges:=True
End Sub
Based on the code that I got when I recorded creating a hyperlink seems like you need to just remove everything before ws2.Hyperlinks.... The hyperlink creation code contains the cell to put the link in, so I think it inherently fills the .Value for the cell.
Make sure to update the code in that covers the case that Application.CountIf(rngSearch, strSearch) > 0 returns as True since it's the trying to do the same thing.
You could also drop the For loop around adding the hyperlink since you're not really looping. You can either just increment i before the hyperlink creation, or just hardcode the values.
Whole Code Explained:
I have this code that saves a txt file as a Microsoft Excel Comma Separated Values File (.csv) then opens a blank template excel file with a sheet named Graphs. It then copies the sheet with all the data from the csv file into the template excel file, renames it to "data" Then deletes the csv after close. The code then Inserts a chart in the "graph" sheet. Next it finds the total number of rows used and number of columns used for references for the ranges in the graphs and then for later formulas. This data is Acceleration from a accelerometer at a specific frequency. Therefor there is a lot of data, 8193 rows! The data lay out is top row labels (hz, Part1, 2...), Column A is frequencys, and all other cells from B2:whatever is accelerometer readings.
The Problem is it takes 83.22 seconds
to do the following loop, which inserts the average formula:
Do While i <= LastRow
'Assign Range To Take Average
CellLeft = wbtempXl.Worksheets("Data").Cells(i, 2)
CellRight = wbtempXl.Worksheets("Data").Cells(i, LastColumn)
AvgRange = wbtempXl.Worksheets("Data").Range(CellLeft, CellRight)
Average = appXL.WorksheetFunction.Average(AvgRange)
wbtempXl.Worksheets("Data").Cells(i, LastColumn + 1).Value = Average
i = i + 1
Loop
After this Average formula I am adding peak finding logic to find the peaks and troughs in the data, but this step alone takes a minute and a half. Is there a fast, better way of doing this? Looping formulas that is.
Note: I can not just have the formulas in the template. The test could include 12 parts or 100 parts. Each part has its own column and the frequency is in the rows of column A. The rest of the Rows is acceleration readings per frequency. Would post picture but not allowed to yet.
Full Code:
Public Sub btn_Do_Click(sender As Object, e As EventArgs) Handles btn_Do.Click
Dim FileTXT As String = cbo_FileList.Text
Dim folderpath As String = "C:\Users\aholiday\Desktop\Data Dump"
Dim txtpath As String = folderpath & "\" & FileTXT & ".txt"
Dim csvpath As String = "C:\Temp\" & FileTXT & ".csv"
Dim FinalFile As String = "C:\Users\aholiday\Desktop\Test"
Try
File.Copy(txtpath, csvpath)
Catch
MsgBox("Please Choose File")
Exit Sub
End Try
appXL = CreateObject("Excel.Application")
appXL.Visible = True
wbcsvXl = appXL.Workbooks.Open(csvpath)
wbtempXl = appXL.Workbooks.Open(FinalFile)
wbcsvXl.Worksheets(FileTXT).Copy(After:=wbtempXl.Worksheets("Graphs"))
wbtempXl.Worksheets(FileTXT).Name = ("Data")
'Close Objects
wbcsvXl.Close()
File.Delete(csvpath)
'Release Objects
wbcsvXl = Nothing
' Declare Varables
Dim Chart As Excel.Chart
Dim ChartXL As Excel.ChartObjects
Dim ThisChart As Excel.ChartObject
Dim SerCol As Excel.SeriesCollection
Dim Series As Excel.Series
Dim xRange As Excel.Range
Dim xCelltop As Excel.Range
Dim xCellBottom As Excel.Range
Dim yRange As Excel.Range
Dim yCelltop As Excel.Range
Dim yCellBottom As Excel.Range
Dim CellRight As Excel.Range
Dim CellLeft As Excel.Range
Dim AvgRange As Excel.Range
Dim Average As Double
Dim LastRow As Long
Dim LastColumn As Long
Dim i As Integer
' Set i integer
i = 2
'Make Chart
ChartXL = wbtempXl.Worksheets("Graphs").ChartObjects
ThisChart = ChartXL.Add(0, 0, 800, 400)
Chart = ThisChart.Chart
Chart.ChartType = Excel.XlChartType.xlXYScatterSmoothNoMarkers
With ThisChart.Chart
.HasTitle = True
.ChartTitle.Characters.Text = "RF Graph"
' X,Y title??????
End With
'Count Rows Used
'Find last Row Used
With wbtempXl.Worksheets("Data")
LastRow = .UsedRange.Rows.Count
End With
'Count Columns Used
'Find Last Column Used
With wbtempXl.Worksheets("Data")
LastColumn = .UsedRange.Columns.Count
End With
Do Until i > LastColumn
'Excel Chart X Axis Values
xCelltop = wbtempXl.Worksheets("Data").Cells(2, 1)
xCellBottom = wbtempXl.Worksheets("Data").Cells(LastRow, 1)
xRange = wbtempXl.Worksheets("Data").Range(xCelltop, xCellBottom)
'Excel Chart Y Axis Values
yCelltop = wbtempXl.Worksheets("Data").Cells(2, i)
yCellBottom = wbtempXl.Worksheets("Data").Cells(LastRow, i)
yRange = wbtempXl.Worksheets("Data").Range(yCelltop, yCellBottom)
'Label Part in Data Sheet
wbtempXl.Worksheets("Data").Cells(1, i).Value = ("Rotor " & i - 1)
'Add New Series to Chart
SerCol = Chart.SeriesCollection
Series = SerCol.NewSeries
'Rename and Assign Values
With Series
.Name = ("Rotor " & i - 1)
Series.XValues = xRange
Series.Values = yRange
End With
Chart.Refresh()
i = i + 1
Loop
'Add Average Column Label
wbtempXl.Worksheets("Data").Cells(1, LastColumn + 1).Value = "Average"
'Rest i integer
i = 2
Do While i <= LastRow
'Assign Range To Take Average
CellLeft = wbtempXl.Worksheets("Data").Cells(i, 2)
CellRight = wbtempXl.Worksheets("Data").Cells(i, LastColumn)
AvgRange = wbtempXl.Worksheets("Data").Range(CellLeft, CellRight)
Average = appXL.WorksheetFunction.Average(AvgRange)
wbtempXl.Worksheets("Data").Cells(i, LastColumn + 1).Value = Average
i = i + 1
Loop
'Release Objects
wbtempXl = Nothing
appXL = Nothing
GC.Collect()
Me.Close()
End Sub
I'd suggest you put formulas in the cells with code then convert to values if required:
With wbtempXl.Worksheets("Data")
formularange = .Range(.Cells(i, LastColumn + 1), .Cells(LastRow, LastColumn + 1))
End With
formularange.FormulaR1C1 = "=AVERAGE(RC2:RC[-1])"
formularange.Value2 = formularange.Value2
I'm noob in vba (Excel macros). I need to add somes charts automatically in the same WorkSheet. This is my code:
Sub runChart()
Dim xchart As Chart
Dim nameSheet As String
nameSheet = ActiveSheet.Name
Dim x As Integer
Dim firstIndex As Integer
Dim firstValue As Integer
Dim actualValue As Integer
Dim actualIndex As Integer
Dim rChart1 As Range
Dim rChart2 As Range
MsgBox nameSheet
firstIndex = 2
actualIndex = 2
firstValue = Cells(2, 1)
actualValue = Cells(2, 1)
Do
Do
actualIndex = actualIndex + 1
actualValue = Sheets(nameSheet).Cells(actualIndex, 1)
Loop Until firstValue <> actualValue
Set rChart1 = Range(Sheets(nameSheet).Cells(firstIndex, "E"), Sheets(nameSheet).Cells(actualIndex - 1, "E"))
Set rChart1 = Union(rChart1, Range(Sheets(nameSheet).Cells(firstIndex, "J"), Sheets(nameSheet).Cells(actualIndex - 1, "J")))
Dim nameChart As String
nameChart = CStr(Sheets(nameSheet).Cells(firstIndex, 5)) & " - " & Sheets(nameSheet).Cells(actualIndex, 5) & " " & CStr(Sheets(nameSheet).Cells(firstIndex, 1))
Set xchart = Charts.Add
With xchart
.Name = nameChart
.ChartType = xlColumnClustered
.SetSourceData rChart1
.Location Where:=xlLocationAsObject, Name:=nameSheet
'position and size chart
.ChartArea.Top = 10 'this position is a example
.ChartArea.Left = 1700 'this position is a example
.ChartArea.Height = 400 'this size is a example
.ChartArea.Width = 750 'this size is a example
End With
firstValue = Sheets(nameSheet).Cells(actualIndex, 1)
firstIndex = actualIndex
Loop Until (Sheets(nameSheet).Cells(actualIndex, 1) = vbNullString)
End Sub
So, my problem happens is in .ChartArea.left = 1700. The program says :
The specified dimension is not valid for the current chart type
anyone has any idea what 's happening? Thanks for your time :)
The ChartArea is the overall rectangle containing the chart within its parent ChartObject (the shape that contains the embedded chart). The position and size of the ChartArea are read only. But that's okay, you want to position and resize the ChartObject, which is the chart's .Parent.
With xchart
'position and size chart
.Parent.Top = 10 'this position is a example
.Parent.Left = 1700 'this position is a example
.Parent.Height = 400 'this size is a example
.Parent.Width = 750 'this size is a example
End With