PowerPoint VBA selecting only some shapes - vba

I am trying to make a subroutine in PowerPoint with VBA that will take 9 pictures that have been previously inserted and resized, and arrange them into 3 columns and 3 rows. I cannot figure out how to select a picture, move it, then select the next picture on the slide.
My VBA knowledge is limited mostly to using Excel, and since PowerPoint 2010 does not have a "Record Marco" button, this is my current pathetic attempt.
Sub ArrangeIn3x3()
Dim sldTemp As Slide
Dim lngTemp As Long
Dim lngCount As Long
For Each sldTemp In ActivePresentation.Slides
For lngCount = sldTemp.Shapes.Count To 1 Step -1
With sldTemp.Shapes(lngCount)
.LockAspectRatio = msoTrue
.Width = 3 * 72
.Left = (x + 0.2) * 72
x = x + 1
.Top = (y + 0.75) * 72
y = y + 1
End With
Next
Next
End Sub
Thank You.

This has your 3x3 grid hardcoded; you could extend it to take the number of rows/columns as parameters, to work out the size of the first image and supply that automatically, and any number of other enhancements, but this should do what you're after:
Sub NineUp()
Dim sngInterRowSpace As Single
Dim sngInterColSpace As Single
Dim lRowCount As Long
Dim lColCount As Long
Dim sngLeft As Long
Dim sngTop As Long
Dim sngImageWidth As Single
Dim sngImageHeight As Single
Dim lShapeCount As Long '
' Define spacing between rows/columns
sngInterRowSpace = 0.2 * 72
sngInterColSpace = 0.2 * 72
' change these dimensions to match those
' of your images
sngImageWidth = 2.36 * 72
sngImageHeight = 2.17 * 72
lShapeCount = 1
sngTop = sngInterRowSpace
For lRowCount = 1 To 3
sngLeft = sngInterColSpace
For lColCount = 1 To 3
With ActivePresentation.Slides(1).Shapes(lShapeCount)
.Left = sngLeft
.Top = sngTop
sngLeft = sngLeft + sngImageWidth + sngInterColSpace
lShapeCount = lShapeCount + 1
End With
'sngTop = sngTop + sngImageHeight + sngInterRowSpace
Next
sngTop = sngTop + sngImageHeight + sngInterRowSpace
Next
End Sub

Related

Adding horizontal lines to plot VBA

I need to change my y-axis to Logarithmic scale, however, after doing so the horizontal lines are removed, i.e. the lines indicating which level the y-axis is at. Is there a way to add this with VBA? I used
.Axes(xlValue).ScaleType = xlScaleLogarithmic
.Axes(xlValue).LogBase = 2.7
to change the y-axis to log, however, now it looks something like this
and I want it to like something like this (this is another plot, I just want to illustrate how I want the horizontal lines).
Note that the horizontal lines are only removed when I change to log scale.
Here is the code that I
Dim dataSheet As Worksheet
Set dataSheet = Sheets("cudfFactor")
Dim CurrentChart As Chart
Dim Subtitle As Variant
Set CurrentChart = ActiveSheet.Shapes.AddChart2(227, xlLine).Chart
Dim startRow As Integer
Dim endRow As Integer
startRow = 1
endRow = dataSheet.Range(dataSheet.Cells(2, 2), dataSheet.Cells(2, 2)).End(xlDown).Row
With CurrentChart
With .Parent
.Width = 430
.Height = 290
.left = 500
.top = topD
End With
.ChartArea.ClearContents
.ChartTitle.Text = "Accumulated Top Quintile Log Returns"
Dim i As Integer
For i = 1 To 6
.SeriesCollection.NewSeries
.FullSeriesCollection(i).Name = dataSheet.Cells(startRow, i + 1)
.FullSeriesCollection(i).XValues = dataSheet.Range(dataSheet.Cells(startRow + 1, 1), dataSheet.Cells(endRow, 1))
.FullSeriesCollection(i).Values = dataSheet.Range(dataSheet.Cells(startRow + 1, i + 1), dataSheet.Cells(endRow, i + 1))
Next i
.SetElement (msoElementLegendBottom)
.Axes(xlValue).MaximumScale = 300
.Axes(xlValue).MinimumScale = 80
.Axes(xlValue).ScaleType = xlScaleLogarithmic
.Axes(xlValue).LogBase = 2.7
End With
If you need the data, please let me know.
Thanks in advance for any help.

Powerpoint Macros: Column(unknown member) error at Columns.Width Formatting code

I'm new to coding directly in the Powerpoint Macros interface, while learning formatting I hit a roadblock.
Slide 10 of Presentation has a 5 by 7 table. I would like to adjust the width of column 3.
The code I use is:
Sub FormatTables()
Dim s As Slide
Dim oSh As Shape
Dim oTbl As Table
Dim lRow As Long
Dim lCol As Long
Dim ThisRow As Integer
Set s = ActivePresentation.Slides(10)
s.Select
For Each oSh In s.Shapes
If oSh.HasTable Then
Set oTbl = oSh.Table
oTbl.Columns(1).Width = 72 * 0.5
oTbl.Columns(2).Width = 72 * 0.5
oTbl.Columns(3).Width = 72 * 0.5
For lRow = 1 To oTbl.Rows.Count
For lCol = 1 To oTbl.Columns.Count
With oTbl.Cell(lRow, lCol).Shape.TextFrame.TextRange
.Font.Name = "Georgia"
.Font.Size = 8
End With
Next
Next
End If
Next
The error I get is: "Columns (unknown member): Integer out of range. 3 is not the valid range of 1 to 2.
Also, the width of columns 1 and 2 are adjusting successfully, but the macro hits the error at the line of code of Column 3.
Is there anything I missed?

Show dimensions of Excel shape in column widths and row heights vba

I have a spreadsheet that involves the user resizing some rectangular shapes, which are set on a background of an Excel grid with column width = row height = 10pixels. The purpose of this background is to give a scale to the plan, which is made by the shapes; in this case, one column or row represents 10cm - there is a thick border after every 10 cells to represent a metre:
When the user resizes the rectangle, I would like the text inside the rectangle to display the dimensions, according to the scale of the plan. I have read many articles about how the shapes dimensions are provided in points, and the columns and rows in pixels (or a unit based on the font), and have found the conversion function between them, but it does not seem to give the results I would expect - the values for the width and height depend on the level of zoom, giving smaller and smaller results as I zoom out, even though the displayed pixel width remains the same.
Is there a way to consistently convert the pixel units of the grid to the points unit of the shapes such that I can essentially count how many column widths and row heights comprise the shape dimensions? Here is the macro I have written so far:
Option Explicit
Dim sh As Shape
Dim dbPx_Per_Unit As Double
Dim strUnit As String
Dim UserSelection As Variant
Dim strText As String
Dim strWidth As String
Dim strHeight As String
Sub LabelShapeSize()
Set UserSelection = ActiveWindow.Selection
'is selection a shape?
On Error GoTo NoShapeSelected
Set sh = ActiveSheet.Shapes(UserSelection.Name)
On Error Resume Next
'pixels are the units for the columns and rows
'dbPx_Per_Unit = InputBox("there are this many pixels per unit:", "Conversion Rate", 10)
dbPx_Per_Unit = 100
'strUnit = InputBox("Unit Name:", "Units", "M")
strUnit = "M"
With sh
'Width and length is measured in points, so we need to convert the points to pixels to get the actual size
strWidth = Format(Application.ActiveWindow.PointsToScreenPixelsX(.Width) / dbPx_Per_Unit, "#,##0.0")
strHeight = Format(Application.ActiveWindow.PointsToScreenPixelsY(.Height) / dbPx_Per_Unit, "#,##0.0")
'this is our message that will be in the shape
strText = strWidth & strUnit & " x " & strHeight & strUnit
With .TextFrame2
.VerticalAnchor = msoAnchorMiddle
With .TextRange.Characters
.ParagraphFormat.FirstLineIndent = 0
.ParagraphFormat.Alignment = msoAlignCenter
.Text = strText
'I'll sort something out for dark shapes at some point, but for now let's just write in black ink
With .Font
.Fill.Visible = msoTrue
.Fill.ForeColor.ObjectThemeColor = msoThemeColorDark1
.Fill.Solid
.Size = 10
End With
End With
End With
End With
Exit Sub
'No shape error
NoShapeSelected:
MsgBox "You must select a shape to calculate dimensions!", vbCritical, "Object not set to an instance of a Nobject"
End Sub
****** for completeness, here is the final script I wrote implementing the solution in the answer below ******
Option Explicit
Dim sh As Shape
Dim db_Cols_Per_Unit As Double
Dim strUnit As String
Dim strText As String
Dim userSelection As Variant
Dim ws As Worksheet
Dim clrBackground As Long
Dim leftCol As Integer
Dim colWidth As Integer
Dim topRow As Integer
Dim rowHeight As Integer
Sub LabelShapeSize()
Set userSelection = ActiveWindow.Selection
Set ws = ActiveSheet
db_Cols_Per_Unit = 10
strUnit = "M"
'is selection a shape?
On Error GoTo NoShapeSelected
Set sh = ActiveSheet.Shapes(userSelection.Name)
On Error Resume Next
topRow = 1
rowHeight = 0
leftCol = 1
colWidth = 0
With sh
While ws.Cells(1, leftCol).Left <= .Left 'Move left until we find the first column the shape lies within
leftCol = leftCol + 1
Wend
While ws.Cells(1, leftCol + colWidth).Left <= .Left + .Width 'Continue moving left until we find the first column the shape does not lie within
colWidth = colWidth + 1
Wend
While ws.Cells(topRow, 1).Top <= .Top 'Move down until we find the first row the shape lies within
topRow = topRow + 1
Wend
While ws.Cells(topRow + rowHeight, 1).Top <= .Top + .Height 'Continue moving down until we find the first row the shape does not lie within
rowHeight = rowHeight + 1
Wend
'this is our message that will be in the shape
strText = Format(colWidth / db_Cols_Per_Unit & strUnit, "#,##0.0") & " x " & rowHeight / Format(db_Cols_Per_Unit, "#,##0.0") & strUnit
clrBackground = .Fill.ForeColor.RGB
With .TextFrame2
.VerticalAnchor = msoAnchorMiddle
With .TextRange.Characters
.ParagraphFormat.FirstLineIndent = 0
.ParagraphFormat.Alignment = msoAlignCenter
.Text = strText
With .Font
.Fill.Visible = msoTrue
.Fill.ForeColor.RGB = ContrastColor(clrBackground)
.Fill.Solid
.Size = 10
End With
End With
End With
End With
Exit Sub
'No shape error
NoShapeSelected:
MsgBox "You must select a shape to calculate dimensions!", vbCritical, "Object not set to an instance of a Nobject"
End Sub
Function ContrastColor(clrBackground As Long) As Long
Dim brightness As Integer
Dim luminance As Double
Dim r As Integer
Dim g As Integer
Dim b As Integer
r = clrBackground Mod 256
g = (clrBackground \ 256) Mod 256
b = (clrBackground \ 65536) Mod 256
luminance = ((0.199 * r) + (0.587 * g) + (0.114 * b)) / 255
If luminance > 0.5 Then
brightness = 0
Else
brightness = 255
End If
ContrastColor = RGB(brightness, brightness, brightness)
End Function
thanks to #Gacek answer in this question for the luminance function.
I believe your best bet would be to make use of the Left, Top, Width, and Height cell properties. They'll tell you the value in Excel's weird format (the same units as used by the shapes), so you won't need to do any conversions.
The downside is that as far as I know of, there's no way to get the row/column that exists at a given top/left value, so you need to search through all the rows/columns until you find the one that matches your shape's boundaries.
Here's a quick example (there's probably an off-by-one error in here somewhere)
Dim UserSelection As Variant
Dim ws As Worksheet
Dim sh As Shape
Dim leftCol As Integer
Dim colWidth As Integer
Dim topRow As Integer
Dim rowHeight As Integer
Set ws = ActiveSheet
Set UserSelection = ActiveWindow.Selection
Set sh = ActiveSheet.Shapes(UserSelection.Name)
leftCol = 1
colWidth = 0
While ws.Cells(1, leftCol).Left <= sh.Left 'Move left until we find the first column the shape lies within
leftCol = leftCol + 1
Wend
While ws.Cells(1, leftCol + colWidth).Left <= sh.Left + sh.width 'Continue moving left until we find the first column the shape does not lie within
colWidth = colWidth + 1
Wend
topRow = 1
rowHeight = 0
While ws.Cells(topRow, 1).Top <= sh.Top 'Move down until we find the first row the shape lies within
topRow = topRow + 1
Wend
While ws.Cells(topRow + rowHeight, 1).Top <= sh.Top + sh.height 'Continue moving down until we find the first row the shape does not lie within
rowHeight = rowHeight + 1
Wend
MsgBox "Shape is " & colWidth & " columns wide by " & rowHeight & " rows high"

Select shapes within a range and align them

I want to write a function where I can select one shape after which a macro aligns all the shapes that are within a 'short range' of the selected shape.
Therefore I wrote the following code that selects all the object within a range:
Sub Shape_Dimensions()
Dim L As Long
Dim T As Long
Dim H As Long
Dim W As Long
With ActiveWindow.Selection
If .Type = ppSelectionShapes Then
L = .ShapeRange.Left
T = .ShapeRange.Top
H = .ShapeRange.Height
W = .ShapeRange.Width
Else
MsgBox "You have not selected an OBJECT in PowerPoint to dimension."
Exit Sub
End If
End With
'Set range for selection
TopRange = L + 30
DownRange = T + H + 20
'Left and right are 0 - 600
End Sub
Now the final step I want to take is select all shapes that are within the top range and down range and align them with the top of the selected box. Any thoughts on how I should proceed?
Sub Shape_Align()
Dim L As Long
Dim T As Long
Dim H As Long, TopRange As Long, DownRange As Long
Dim W As Long, s As Shape, n As String
With ActiveWindow.Selection
If .Type = ppSelectionShapes Then
L = .ShapeRange.Left
T = .ShapeRange.Top
H = .ShapeRange.Height
W = .ShapeRange.Width
n = .ShapeRange.Name
Else
MsgBox "You have not selected an OBJECT in PowerPoint to dimension."
Exit Sub
End If
End With
'Set range for selection
TopRange = L + 30
DownRange = T + H + 20
'Left and right are 0 - 600
For Each s In ActiveWindow.View.Slide.Shapes
If s.Name <> n Then
'in scope for lining up?
If Abs(s.Top - T) < 60 Then
s.Top = T
End If
End If
Next s
End Sub

Excel VBA moving Shapes to a column

I am having a problem with a program for my Excel VBA course. I have written a program to add 5 each of lines, rectangles, ovals and triangles to a worksheet this is the btnAddShapes click event. In the cmdAlignRectangles click event I am trying to take only the rectangles that were added and align them all in the C column. I have used a For Each loop to select all the shapes on the sheet, the For Each loop structure is required for the assignment. Then I used an If/Then statement to select the shape Type msoShapeRectangle. I used the name that I assigned in when creating the rectangles such as "Box1" using the counter I to iterate through each rectangle, it is this statement that is giving me an error saying that the item with that name was not found. I must use the Left property of the Range and Shape objects to move the rectangles.? Any help or guidance would be greatly appreciated.
Private Sub btnAddShapes_Click()
Randomize
For I = 1 To 5
ActiveSheet.Shapes.AddShape(msoShapeRectangle, 50, 100, 100, 65).Select
With Selection
.Name = "Box" & I
.Left = Int(422 * Rnd)
.Top = Int(422 * Rnd)
End With
ActiveSheet.Shapes.AddLine(10 + I * (Rnd * 133), 50 + I * (Rnd * 133), 125 + I * (Rnd * 133), 250 + I * (Rnd * 133)).Select
With Selection
.Name = "Line" & I
End With
ActiveSheet.Shapes.AddShape(msoShapeOval, 275, 240, 108, 44).Select
With Selection
.Name = "Oval" & I
.Left = Int(444 * Rnd)
.Top = Int(444 * Rnd)
End With
ActiveSheet.Shapes.AddShape(msoShapeIsoscelesTriangle, 514, 220, 93, 71).Select
With Selection
.Name = "Triangle" & I
.Left = Int(377 * Rnd)
.Top = Int(377 * Rnd)
End With
Next I
End Sub
Private Sub btnRemoveShapes_Click()
Dim sh As Shape
For Each sh In ActiveSheet.Shapes
If Not (sh.Type = msoOLEControlObject Or sh.Type = msoFormControl Or sh.Type = msoTextBox) Then sh.Delete
Next sh
End Sub
Private Sub cmdAlignRectangles_Click()
Dim allRectangles As Shapes
Dim sh As Shape
Dim I As Integer
Set allRectangles = ActiveSheet.Shapes
I = 1
For Each sh In allRectangles
If sh.Type = msoShapeRectangle Then
ActiveSheet.Shapes("Box" & I).Left = Cells(I, 3).Left
End If
I = I + 1
Next
End Sub
The error is that in the creation loop you create 4 shapes for each 1, I going from 1 to 5. On the other hand, in the alignment loop you iterate one I for each shape. Therefore, when I reaches 6 (with the 6th shape), the object named "Box6" does not exist.
A simpler way to achieve this would be to modify our test by examining the name of the shape, like this, for example:
If sh.Type = msoShapeRectangle And InStr(sh.Name, "Box") = 1 Then
sh.Left = Cells(I, 3).Left
End If
p.s. you can also drop the first part of the test