display a series of images in Excel 2010 using VBA - image

As part of a larger project I need to display a series of images within the one macro. When I run this it displays the final image after the last msgbox, with the others layered underneath:
Sub Macro4()
Dim x As Integer
Dim Pic As Object
Dim picname As String
For x = 1 To 7
picname = ThisWorkbook.Path & "/" & "pic" & x & ".png"
ActiveSheet.Pictures.Insert(picname).Select
MsgBox (x)
Next x
End Sub
The Msgbox command is there to slow the process down so that I can see, or in this case not see, the pictures change.
The images are called pic1.png, pic2.png etc
How do I get the separate images to show during the macro?
RE-EDIT:
So here is the picture function and the larger function which plays a randomised piece of Musique Concret.
Private Declare Function PlaySound Lib "winmm.dll" _
Alias "PlaySoundA" (ByVal lpszName As String, _
ByVal hModule As Long, ByVal dwFlags As Long) As Long
showpic gives an image relative to the pitch of the sound being played.
Function showpic(value)
Dim v As Integer
v = value
picname = ThisWorkbook.Path & "/" & "pic" & v & ".png"
ActiveSheet.Shapes.AddPicture (picname), True, True, a1, a1, 170, 170
DoEvents
End Function
play runs a series of specific sound files generated by a randomised process choosing instrument and pitch and creating the requisite filename. The "piece" runs for 'notes' seconds and is triggered by a separate macro that changes the value in a given 'cell' to match the 'condition'.
Function play(Cell, condition, notes)
Dim WAVFile As String
Dim x As Integer
Dim y As Integer
Dim z As Integer
Dim c As String
Dim d As String
Dim currentcell As String
Dim pitchcell As String
Const SND_SYNC = &H0
Const SND_ASYNC = &H1
Const SND_FILENAME = &H20000
On Error GoTo ErrHandler
For x = 1 To notes
If Evaluate(Cell.value & condition) Then
y = x + 65
z = x + 39
c = Chr(y)
d = Chr(z)
If y > 90 Then c = "A" & d
currentcell = c & 6
pitchcell = c & 3
showpic (Sheets("Sheet1").Range(pitchcell).value)
WAVFile = ThisWorkbook.Path & "/" & Sheets("Sheet1").Range(currentcell).Text & ".wav"
Call PlaySound(WAVFile, 0&, SND_SYNC)
End If
Next x
ErrHandler:
play = False
Exit Function
End Function
I have three problems:
It doesn't show the first image file
It performs a 'calculate' or F9 at the beginning so re-randomises the piece and doesn't play the displayed sequence; I presume this is caused by the first DoEvents.
It now plays twice! However, the second time through it does show all the image files.

Edit:
Since my initial answer didn't work for you, how about achieving your results a completely different way. This method using Application.OnTime to re-run your sub and insert the next picture.
Sub NextPicture()
Static x As Integer
Dim pic As Object
Dim picname As String
'Reset x to 0 because I assumed you want to rotate through the pictures.
'If you want it to stop replace with If x = 7 then exit sub
If x = 7 Then x = 0
x = x + 1
picname = ThisWorkbook.Path & "/" & "pic" & x & ".png"
ActiveSheet.Pictures.Insert(picname).Select
'Using 00:00:05 = 5 seconds, change the amount to speed up or slow down the picture changes
Application.OnTime Now + TimeValue("00:00:05"), "NextPicture"
End Sub
Original Answer
I could not get your code to work, but I could if I used Shapes.AddPicture, which is actually more robust because you get/have to specify the location and size.
Syntax:
expression.AddPicture(Filename, LinkToFile, SaveWithDocument, Left, Top, Width, Height)
Example:
ActiveSheet.Shapes.AddPicture(picname, True, True, 100, 100, 70, 70)

Related

Improve Looping Efficiency in VBA

I have a For loop that loops through integers 1 to 9 and simply finds the bottom most entry that corresponds to that integer ( i.e. 1,1,1,2,3,4,5 would find the 3rd "1" entry) and inserts a blank row. I concatenate the number with a string "FN" that just corresponds to the application for this code, just to clarify. Anyway, it works well, but it lags quite a bit for only having to run through 9 integers. I was hoping someone would be able to help me debug to improve speed on this code. Thanks!
Bonus points if anyone can shed some light on a good way to populate the blank row being inserted with a formatted copy of the header of the page that spans ("A1:L1"). The code I attempted is commented out right before Next i.
Sub test()
Dim i As Integer, Line As String, Cards As Range
Dim Head As Range, LR2 As Long
For i = 1 To 9
Line = "FN" & CStr(i)
Set Cards = Sheets(1).Cells.Find(Line, after:=Cells(1, 1), searchdirection:=xlPrevious)
Cards.Rows.Offset(1).EntireRow.Insert
Cards.Offset(1).EntireRow.Select
' Range("A" & (ActiveCell.Row), "K" & (ActiveCell.Row)) = Range("A3:K3")
' Range("A" & (ActiveCell.Row), "K" & (ActiveCell.Row)).Font.Background = Range("A3:K3").Font.Background
Next i
End Sub
This works pretty fast for me
Sub Sample()
Dim i As Long, line As String, Cards As Range
With Sheets(1)
For i = 1 To 9
line = "FN" & i
Set Cards = .Columns(6).Find(line, LookIn:=xlValues, lookat:=xlWhole)
If Not Cards Is Nothing Then
.Range("A3:K3").Copy
Cards.Offset(1, -5).Insert Shift:=xlDown
End If
Next i
End With
End Sub
Before
After
Most of your improvements will come from altering the application environment variables with the appTGGL helper function but there are a few tweaks in the base code here.
Option Explicit
Sub ewrety()
Dim f As Long, fn0 As String, fndfn As Range
'appTGGL btggl:=false 'uncomment this when you are confident in it
With Worksheets(1).Columns("F")
For f = 1 To 9
fn0 = Format$(f, "\F\N0")
Set fndfn = .Find(What:=fn0, After:=.Cells(1), LookIn:=xlFormulas, LookAt:=xlWhole, _
SearchOrder:=xlByRows, SearchDirection:=xlPrevious, MatchCase:=False)
With fndfn
.Offset(1, -5).EntireRow.Insert Shift:=xlDown
.Parent.Range("A1:L1, XFC1").Copy Destination:=.Offset(1, -5)
End With
Next f
End With
appTGGL
End Sub
Public Sub appTGGL(Optional bTGGL As Boolean = True)
With Application
.ScreenUpdating = bTGGL
.EnableEvents = bTGGL
.DisplayAlerts = bTGGL
.AutoRecover.Enabled = bTGGL 'no interruptions with an auto-save
.Calculation = IIf(bTGGL, xlCalculationAutomatic, xlCalculationManual)
.CutCopyMode = False
.StatusBar = vbNullString
End With
Debug.Print Timer
End Sub

Creating a userform that prints textbox input to specific spots in the document

I was working on a system in VBA word. The goal of the system is to replace several different words in a document with input from a text box. So far I have a userform with 12 different text boxes each containing input from a user to replace words in the document. I made a button in the userform to print all the input from the textboxes to the document.
For each textbox I made the following code:
Sub FindAndReplaceAllStoriesHopefully()
Dim myStoryRange As Range
'
'
'Loop replaces everything with <KLANTNAAM> in the document
For Each myStoryRange In ActiveDocument.StoryRanges
With myStoryRange.Find
.Text = "<KLANTNAAM>"
.Replacement.Text = TextBox1.Value
.Wrap = wdFindContinue
.Execute Replace:=wdReplaceAll
End With
Do While Not (myStoryRange.NextStoryRange Is Nothing)
Set myStoryRange = myStoryRange.NextStoryRange
With myStoryRange.Find
.Text = "<KLANTNAAM>"
.Replacement.Text = TextBox1.Value
.Wrap = wdFindContinue
.Execute Replace:=wdReplaceAll
End With
Loop
Next myStoryRange
So far I did this for all 12 textboxes and it works but it isn't smooth. The
button upon getting clicked is calling the function with
Call FindAndReplaceAllStoriesHopefully
I have a few problems which I just cannot fix:
Once the button is clicked and some textboxes are not filled by the user, the marked words like <KLANTNAAM> are still replaced and removed from the document.
The performance of the macro is not great since the same code is copied 12 times.
Once the button is clicked, there is no easy way for the user to undo mistakes typed in the userform since the results are already printed.
I was hoping to get some tips so I can finalize this application.
Something like this:
Private Sub CommandButton1_Click()
Dim numBlank As Long, n As Long, txt As String
Dim bookMarkName As String
numBlank = Me.CountBlanks
If numBlank > 0 Then
If MsgBox(numBlank & " entries are blank!. Continue?", _
vbExclamation + vbOKCancel) <> vbOK Then
Exit Sub
End If
End If
For n = 1 To 4
txt = Me.Controls("Textbox" & n).Text
bookMarkName = "BOOKMARK" & n
FindAndReplaceAllStoriesHopefully bookMarkName, txt
Next n
End Sub
Function CountBlanks() As Long
Dim n As Long, b As Long
b = 0
For n = 1 To 4
If Len(Me.Controls("Textbox" & n).Text) = 0 Then
b = b + 1
End If
Next n
CountBlanks = n
End Function

Resize and change the format of multiple pictures using Excel VBA

I have an excel worksheet with a lot of pictures with various sizes and formats. I want to use excel VBA to loop through all the pictures in the worksheet, and set each picture to the same width (214) and change the picture type to a JPEG after resizing (to keep the file size down). My pictures are located in various cells, and I don't want the picture locations to change (i.e. stay in the same cell). I'm new to VBA and tried the following - but it doesn't work. The debugger stops at the line where I'm trying to cut the picture.
Sub Macro6()
Dim p As Object
Dim iCnt As Integer
For Each p In ActiveSheet.Shapes
p.Width = 217.44
p.Cut
p.PasteSpecial Format:="Picture (JPEG)", Link:=False
iCnt = iCnt + 1
Next p
End Sub
It's not the cutting part that Excel doesn't like--it's the pasting part. Paste and PasteSpecial are methods you call with a worksheet object (where you're pasting to) instead of the image (the thing you're pasting). I don't know if you want to just shrink the width and hold the height constant or if you want to scale both dimensions evenly. If you want to scale both evenly, try this:
Sub Macro6()
Dim p As Object
Dim iCnt As Integer
Dim s As Double
Dim r As Range
For Each p In ActiveSheet.Shapes
s = 214 / p.Width
Set r = p.TopLeftCell
p.Width = 214
p.Height = p.Height * s
p.Cut
r.Select
ActiveSheet.PasteSpecial Format:="Picture (JPEG)", Link:=False
Application.CutCopyMode = False
iCnt = iCnt + 1
Next p
End Sub
If you're just trying to shrink the width and leave the height the same, try this:
Sub Macro6()
Dim p As Object
Dim iCnt As Integer
Dim r As Range
For Each p In ActiveSheet.Shapes
Set r = p.TopLeftCell
p.Width = 214
p.Cut
r.Select
ActiveSheet.PasteSpecial Format:="Picture (JPEG)", Link:=False
Application.CutCopyMode = False
iCnt = iCnt + 1
Next p
End Sub
The locations of your pictures should stay exactly the same if they were originally right at the corner of a cell. Otherwise, this will align the top left corner of the image to the nearest cell corner. The Application.CutCopyMode = False is good practice after pasting. It tells Excel to wipe the clipboard and go back to normal operation instead of waiting for you to paste again. Hope this helps.
Thanks for answering my question! Here's the code I ended up using based on your suggestions. The program took several minutes to run (had over 5000 pictures in the file - yikes!). However, it was worth the wait, because it shrunk the file size in half.
Sub all_pics_to_jpeg()
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Dim mypic As Shape
Dim picleft As Double
Dim pictop As Double
For Each mypic In ActiveSheet.Shapes
mypic.LockAspectRatio = msoTrue
If mypic.Width > mypic.Height Then
mypic.Width = 217.44
Else: mypic.Height = 157.68
End If
picleft = mypic.Left
pictop = mypic.Top
With mypic
.Cut
ActiveSheet.PasteSpecial Format:="Picture (JPEG)", Link:=False, _
DisplayAsIcon:=False
Application.CutCopyMode = False
Selection.Left = picleft
Selection.Top = pictop
End With
Next mypic
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
End Sub

Export pictures from excel file into jpg using VBA

I have an Excel file which includes pictures in column B and I want like to export them into several files as .jpg (or any other picture file format). The name of the file should be generated from text in column A. I tried following VBA macro:
Private Sub CommandButton1_Click()
Dim oTxt As Object
For Each cell In Ark1.Range("A1:A" & Ark1.UsedRange.Rows.Count)
' you can change the sheet1 to your own choice
saveText = cell.Text
Open "H:\Webshop_Zpider\Strukturbildene\" & saveText & ".jpg" For Output As #1
Print #1, cell.Offset(0, 1).text
Close #1
Next cell
End Sub
The result is that it generates files (jpg), without any content. I assume the line Print #1, cell.Offset(0, 1).text. is wrong.
I don't know what I need to change it into, cell.Offset(0, 1).pix?
Can anybody help me? Thanks!
If i remember correctly, you need to use the "Shapes" property of your sheet.
Each Shape object has a TopLeftCell and BottomRightCell attributes that tell you the position of the image.
Here's a piece of code i used a while ago, roughly adapted to your needs. I don't remember the specifics about all those ChartObjects and whatnot, but here it is:
For Each oShape In ActiveSheet.Shapes
strImageName = ActiveSheet.Cells(oShape.TopLeftCell.Row, 1).Value
oShape.Select
'Picture format initialization
Selection.ShapeRange.PictureFormat.Contrast = 0.5: Selection.ShapeRange.PictureFormat.Brightness = 0.5: Selection.ShapeRange.PictureFormat.ColorType = msoPictureAutomatic: Selection.ShapeRange.PictureFormat.TransparentBackground = msoFalse: Selection.ShapeRange.Fill.Visible = msoFalse: Selection.ShapeRange.Line.Visible = msoFalse: Selection.ShapeRange.Rotation = 0#: Selection.ShapeRange.PictureFormat.CropLeft = 0#: Selection.ShapeRange.PictureFormat.CropRight = 0#: Selection.ShapeRange.PictureFormat.CropTop = 0#: Selection.ShapeRange.PictureFormat.CropBottom = 0#: Selection.ShapeRange.ScaleHeight 1#, msoTrue, msoScaleFromTopLeft: Selection.ShapeRange.ScaleWidth 1#, msoTrue, msoScaleFromTopLeft
'/Picture format initialization
Application.Selection.CopyPicture
Set oDia = ActiveSheet.ChartObjects.Add(0, 0, oShape.Width, oShape.Height)
Set oChartArea = oDia.Chart
oDia.Activate
With oChartArea
.ChartArea.Select
.Paste
.Export ("H:\Webshop_Zpider\Strukturbildene\" & strImageName & ".jpg")
End With
oDia.Delete 'oChartArea.Delete
Next
This code:
Option Explicit
Sub ExportMyPicture()
Dim MyChart As String, MyPicture As String
Dim PicWidth As Long, PicHeight As Long
Application.ScreenUpdating = False
On Error GoTo Finish
MyPicture = Selection.Name
With Selection
PicHeight = .ShapeRange.Height
PicWidth = .ShapeRange.Width
End With
Charts.Add
ActiveChart.Location Where:=xlLocationAsObject, Name:="Sheet1"
Selection.Border.LineStyle = 0
MyChart = Selection.Name & " " & Split(ActiveChart.Name, " ")(2)
With ActiveSheet
With .Shapes(MyChart)
.Width = PicWidth
.Height = PicHeight
End With
.Shapes(MyPicture).Copy
With ActiveChart
.ChartArea.Select
.Paste
End With
.ChartObjects(1).Chart.Export Filename:="MyPic.jpg", FilterName:="jpg"
.Shapes(MyChart).Cut
End With
Application.ScreenUpdating = True
Exit Sub
Finish:
MsgBox "You must select a picture"
End Sub
was copied directly from here, and works beautifully for the cases I tested.
''' Set Range you want to export to the folder
Workbooks("your workbook name").Sheets("yoursheet name").Select
Dim rgExp As Range: Set rgExp = Range("A1:H31")
''' Copy range as picture onto Clipboard
rgExp.CopyPicture Appearance:=xlScreen, Format:=xlBitmap
''' Create an empty chart with exact size of range copied
With ActiveSheet.ChartObjects.Add(Left:=rgExp.Left, Top:=rgExp.Top, _
Width:=rgExp.Width, Height:=rgExp.Height)
.Name = "ChartVolumeMetricsDevEXPORT"
.Activate
End With
''' Paste into chart area, export to file, delete chart.
ActiveChart.Paste
ActiveSheet.ChartObjects("ChartVolumeMetricsDevEXPORT").Chart.Export "C:\ExportmyChart.jpg"
ActiveSheet.ChartObjects("ChartVolumeMetricsDevEXPORT").Delete
Dim filepath as string
Sheets("Sheet 1").ChartObjects("Chart 1").Chart.Export filepath & "Name.jpg"
Slimmed down the code to the absolute minimum if needed.
New versions of excel have made old answers obsolete. It took a long time to make this, but it does a pretty good job. Note that the maximum image size is limited and the aspect ratio is ever so slightly off, as I was not able to perfectly optimize the reshaping math. Note that I've named one of my worksheets wsTMP, you can replace it with Sheet1 or the like. Takes about 1 second to print the screenshot to target path.
Option Explicit
Private Declare PtrSafe Sub keybd_event Lib "user32" (ByVal bVk As Byte, ByVal bScan As Byte, ByVal dwFlags As Long, ByVal dwExtraInfo As Long)
Sub weGucciFam()
Dim tmp As Variant, str As String, h As Double, w As Double
Application.PrintCommunication = False
Application.EnableEvents = False
Application.Calculation = xlCalculationManual
Application.ScreenUpdating = False
If Application.StatusBar = False Then Application.StatusBar = "EVENTS DISABLED"
keybd_event vbKeyMenu, 0, 0, 0 'these do just active window
keybd_event vbKeySnapshot, 0, 0, 0
keybd_event vbKeySnapshot, 0, 2, 0
keybd_event vbKeyMenu, 0, 2, 0 'sendkeys alt+printscreen doesn't work
wsTMP.Paste
DoEvents
Const dw As Double = 1186.56
Const dh As Double = 755.28
str = "C:\Users\YOURUSERNAMEHERE\Desktop\Screenshot.jpeg"
w = wsTMP.Shapes(1).Width
h = wsTMP.Shapes(1).Height
Application.DisplayAlerts = False
Set tmp = Charts.Add
On Error Resume Next
With tmp
.PageSetup.PaperSize = xlPaper11x17
.PageSetup.TopMargin = IIf(w > dw, dh - dw * h / w, dh - h) + 28
.PageSetup.BottomMargin = 0
.PageSetup.RightMargin = IIf(h > dh, dw - dh * w / h, dw - w) + 36
.PageSetup.LeftMargin = 0
.PageSetup.HeaderMargin = 0
.PageSetup.FooterMargin = 0
.SeriesCollection(1).Delete
DoEvents
.Paste
DoEvents
.Export Filename:=str, Filtername:="jpeg"
.Delete
End With
On Error GoTo 0
Do Until wsTMP.Shapes.Count < 1
wsTMP.Shapes(1).Delete
Loop
Application.PrintCommunication = True
Application.EnableEvents = True
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
Application.StatusBar = False
End Sub
Thanks for the ideas! I used the above ideas to make a macro to do a bulk file conversion--convert every file of one format in a folder to another format.
This code requires a sheet with cells named "FilePath" (which must end in a "\"), "StartExt" (original file extension), and "EndExt" (desired file extension). Warning: it doesn't ask for confirmation before replacing existing files with the same name and extension.
Private Sub CommandButton1_Click()
Dim path As String
Dim pathExt As String
Dim file As String
Dim oldExt As String
Dim newExt As String
Dim newFile As String
Dim shp As Picture
Dim chrt As ChartObject
Dim chrtArea As Chart
Application.ScreenUpdating = False
Application.DisplayAlerts = False
'Get settings entered by user
path = Range("FilePath")
oldExt = Range("StartExt")
pathExt = path & "*." & oldExt
newExt = Range("EndExt")
file = Dir(pathExt)
Do While Not file = "" 'cycle through all images in folder of selected format
Set shp = ActiveSheet.Pictures.Insert(path & file) 'Import image
newFile = Replace(file, "." & oldExt, "." & newExt) 'Determine new file name
Set chrt = ActiveSheet.ChartObjects.Add(0, 0, shp.Width, shp.Height) 'Create blank chart for embedding image
Set chrtArea = chrt.Chart
shp.CopyPicture 'Copy image to clipboard
With chrtArea 'Paste image to chart, then export
.ChartArea.Select
.Paste
.Export (path & newFile)
End With
chrt.Delete 'Delete chart
shp.Delete 'Delete imported image
file = Dir 'Advance to next file
Loop
Application.ScreenUpdating = True
Application.DisplayAlerts = True
End Sub
Here is another cool way to do it- using en external viewer that accepts command line switches (IrfanView in this case) :
* I based the loop on what Michal Krzych has written above.
Sub ExportPicturesToFiles()
Const saveSceenshotTo As String = "C:\temp\"
Const pictureFormat As String = ".jpg"
Dim pic As Shape
Dim sFileName As String
Dim i As Long
i = 1
For Each pic In ActiveSheet.Shapes
pic.Copy
sFileName = saveSceenshotTo & Range("A" & i).Text & pictureFormat
Call ExportPicWithIfran(sFileName)
i = i + 1
Next
End Sub
Public Sub ExportPicWithIfran(sSaveAsPath As String)
Const sIfranPath As String = "C:\Program Files\IrfanView\i_view32.exe"
Dim sRunIfran As String
sRunIfran = sIfranPath & " /clippaste /convert=" & _
sSaveAsPath & " /killmesoftly"
' Shell is no good here. If you have more than 1 pic, it will
' mess things up (pics will over run other pics, becuase Shell does
' not make vba wait for the script to finish).
' Shell sRunIfran, vbHide
' Correct way (it will now wait for the batch to finish):
call MyShell(sRunIfran )
End Sub
Edit:
Private Sub MyShell(strShell As String)
' based on:
' http://stackoverflow.com/questions/15951837/excel-vba-wait-for-shell-command-to-complete
' by Nate Hekman
Dim wsh As Object
Dim waitOnReturn As Boolean:
Dim windowStyle As VbAppWinStyle
Set wsh = VBA.CreateObject("WScript.Shell")
waitOnReturn = True
windowStyle = vbHide
wsh.Run strShell, windowStyle, waitOnReturn
End Sub

Identical Macros Speed Difference

I have 2 workbooks that contain the same macro. In one workbook the macro runs super fast, less than a second. In the other it takes almost 30 seconds to run. I'm using Excel 2003. The page breaks are off in both workbooks. I don't know what could be causing one to run slower than the other. Any ideas?
Sub viewFirst()
Dim dataSheet As Worksheet, inputSheet As Worksheet, projectID As Long
Dim projectRow As Long, lLastRec As Long, inputLastRow As Long, dataLastRow As Long, x As Long, sh As Shape
Worksheets("Input").Select
ActiveSheet.Protect "", UserInterfaceOnly:=True
Range("a1").Select
ActiveSheet.Pictures.Insert ("working.jpg")
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Application.EnableEvents = False
Set inputSheet = Worksheets("Input")
Set dataSheet = Worksheets("Database")
With inputSheet
inputLastRow = .Cells(.Rows.Count, "A").End(xlUp).Offset(1, 0).Row - 1
End With
With dataSheet
dataLastRow = .Cells(.Rows.Count, "A").End(xlUp).Offset(1, 0).Row - 1
lLastRec = dataLastRow - 1
End With
With inputSheet
.Range("currentProject").Value = 1
projectID = .Range("currentProject").Value
projectRow = projectID + 1
For x = 1 To inputLastRow
If Range("b" & x).HasFormula Then
x = x + 1
End If
If x > inputLastRow Then
Exit For
End If
If Not Range("b" & x).HasFormula Then
.Range("b" & x).Value = dataSheet.Cells(projectRow, 2 + x)
End If
Next x
.Range("d125").Value = dataSheet.Cells(projectRow, 2 + 149)
.Range("d128").Value = dataSheet.Cells(projectRow, 2 + 150)
.Range("d131").Value = dataSheet.Cells(projectRow, 2 + 151)
.Range("d134").Value = dataSheet.Cells(projectRow, 2 + 152)
.Range("d137").Value = dataSheet.Cells(projectRow, 2 + 153)
.Range("d140").Value = dataSheet.Cells(projectRow, 2 + 154)
End With
With ActiveSheet
For Each sh In .Shapes
If sh.Type = msoPicture Then
ActiveSheet.Unprotect ""
sh.Delete
ActiveSheet.Protect "", UserInterfaceOnly:=True
End If
Next sh
End With
Range("b5").Select
Application.EnableEvents = True
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
End Sub
-EDIT-
osknows, thanks for the response. Just to clarify, the workbooks are never open at the same time, and again the workbooks are identical except for the data on the dataSheet - the dataSheet where the macro runs slowly has 35 Rows x 204 Columns, the dataSheet that runs quickly has 56 Rows X 156 Columns. I am going to search for hidden columns or non-blank cells on the input sheet.
Without seeing the 2 workbooks it's difficult to tell. The best advice is to measure exactly the speed of your code by...
In a module decare
Public Declare Function GetTickCount Lib "kernel32" () As Long
then in your code between certain lines of code place
dtStart = GetTickCount
dtline2 = GetTickCount
dtline3 = GetTickCount
dtline4 = GetTickCount
..
etc
the number of ticks between dtStart and dtline2 equals dtline2 - stStart etc
Also a number of factors that could slow things down:
inputLastRow = .Cells(.Rows.Count, "A").End(xlUp).Offset(1, 0).Row - 1
could include many rows that seem blank but aren't.
Set inputSheet = Worksheets("Input") & Set dataSheet = Worksheets("Database") may be massive complex ranges
For Each sh In .Shapes could include many duplicate shapes over each other that look identical
You have undefined ranges & sheets that if you have multiple workbooks open and using them while code runs then workbooks/worksheets/ranges are not explicitly defined. (Eg .Range versus Range) Get into the habit of using the full path to a range Filepath/Workbook/Sheet/Range or cell etc using With statements
eg
With ThisWorkbook
With SheetXYZ
With .range("XYZ1")
End with
End With
End With
or
With ThisWorkbook
With SheetXYZ.range("XYZ1")
.formula = "=Now()"
End With
End With
Also check out this handy site Excel Pages
On the slower machine, unload any Add-ins. If you have an add-in with a global change event, that will fire every time any worksheet changes, and could be causing the slow down. You're writing to the spreadsheet quite a bit, so it would be called a lot.
Instead of writing cell-by-cell, consider building an array (2-dimensions, lower bound of 1) and write all the data to the cell in one big swoop. Here's an example of how that works
Sub WriteOnce()
Dim aReturn() As Double
Dim i As Long, j As Long
Const lLASTROW As Long = 10
Const lLASTCOL As Long = 5
ReDim aReturn(1 To lLASTROW, 1 To lLASTCOL)
For i = 1 To lLASTROW
For j = 1 To lLASTCOL
aReturn(i, j) = Rnd
Next j
Next i
Sheet1.Range("A1").Resize(UBound(aReturn, 1), UBound(aReturn, 2)).Value = aReturn
End Sub
Since I'm only accessing the worksheet once, any event handlers will only fire once.

Resources