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
Related
I'm trying to get a selection of shapes in order from right to left. I found a routine by John Wilson on vbaexpress on which I based my code.
The sorting works perfectly when I select item by item by clicking on the shapes but it doesn't respect the "visible order" of shapes if I select them by "lassoing" with my mouse.
In case of dragging my mouse over the shapes to select them, the routine should respect the visible order of shapes.
Thanks in advance.
Sub AlignFlush()
Dim oshpR As ShapeRange
Dim oshp As Shape
Dim L As Long
Dim rayPOS() As Single
Set oshpR = ActiveWindow.Selection.ShapeRange
ReDim rayPOS(1 To oshpR.Count)
'add to array
For L = 1 To oshpR.Count
rayPOS(L) = oshpR(L).Left
Next L
'sort
Call sortray(rayPOS)
'apply
For L = 1 To oshpR.Count
If L = 1 Then
Set oshp = Windows(1).Selection.ShapeRange(1)
PosTop = oshp.Top
PosNext = oshp.Left + oshp.Width
Else
Set oshp = Windows(1).Selection.ShapeRange(L)
oshp.Top = PosTop
oshp.Left = PosNext
PosNext = oshp.Left + oshp.Width
End If
Next L
End Sub
Sub sortray(ArrayIn As Variant)
Dim b_Cont As Boolean
Dim lngCount As Long
Dim vSwap As Long
Do
b_Cont = False
For lngCount = LBound(ArrayIn) To UBound(ArrayIn) - 1
If ArrayIn(lngCount) > ArrayIn(lngCount + 1) Then
vSwap = ArrayIn(lngCount)
ArrayIn(lngCount) = ArrayIn(lngCount + 1)
ArrayIn(lngCount + 1) = vSwap
b_Cont = True
End If
Next lngCount
Loop Until Not b_Cont
End Sub
Some comments on your existing code:
Array counts always start at 0 unless you use the Option Base statement to set it to a different number.
When you use ReDim, most of the time, you want to use the Preserve keyword, or the ReDim obliterates the existing array contents. But in this case, we know the array size ahead of time, so Preserve is not necessary.
You call sortray, but didn't include it in your listing. I've added a sorting routine.
But then you make no use of the sorted array in the section where you position the shapes.
Working macro (based on your description of what you mean by "visible order" being the left-to-right sequence):
Since you use the left position of the leftmost shape to apply to the others, here's a simpler way to do that:
Sub AlignFlushLeftWithLeftmostShape()
Dim ShpCount As Long
Dim oshpR As ShapeRange
Dim L As Long
Dim rayPOS() As Single
Set oshpR = ActiveWindow.Selection.ShapeRange
ShpCount = oshpR.Count
ReDim rayPOS(ShpCount - 1)
For L = 0 To ShpCount - 1
rayPOS(L) = oshpR(L + 1).Left
Next L
Call BubbleSort(rayPOS)
For x = 1 To ShpCount
oshpR(x).Left = rayPOS(0)
Next x
End Sub
Sub BubbleSort(arr)
Dim lTemp As Long
Dim i As Long
Dim j As Long
Dim lngMin As Long
Dim lngMax As Long
lngMin = LBound(arr)
lngMax = UBound(arr)
For i = lngMin To lngMax - 1
For j = i + 1 To lngMax
If arr(i) > arr(j) Then
lTemp = arr(i)
arr(i) = arr(j)
arr(j) = lTemp
End If
Next j
Next i
End Sub
I am trying to change the location of the graph that I generate with my vba. For now, it is just taking data from a column that may or may not change size. I understand I do not have 'Chart1' identified ion my code but I can not figure out where to declare it where it doesn't create another sheet for the chart as well.
reportsheet.Select
ActiveSheet.Range("a4", ActiveSheet.Range("a4").End(xlDown)).Select
ActiveSheet.Shapes.AddChart.Select
With ActiveSheet.Shapes("Chart1")
.Left = Range("A40").Left
.Top = Range("A40").Top
End With
You can change Name of active Chart and then assign the properties to it.
Try This...
reportsheet.Select
ActiveSheet.Range("A4", ActiveSheet.Range("A4").End(xlDown)).Select
ActiveSheet.Shapes.AddChart.Select
ActiveChart.Parent.Name = "Chart1"
With ActiveSheet.Shapes("Chart1")
.Left = Range("A40").Left
.Top = Range("A40").Top
End With
I use make chart like this. Please refer to bellows.
Sub test()
Dim Ws As Worksheet
Set Ws = ActiveSheet
InsertCharts 20, Ws
InsertCharts 30, Sheets("Sheet1")
End Sub
Sub InsertCharts(n As Integer, Ws As Worksheet)
Dim Cht As Shape
Dim t As Single, w As Integer, h As Integer, x As Integer
Dim i As Integer
With Ws
If .ChartObjects.Count > 0 Then
.ChartObjects.Delete
End If
x = 0
t = .Range("a26").Top
w = 217.1338582677
h = 203.5275590551
For i = 1 To n
Set Cht = .Shapes.AddChart(, x, t, w, h)
If i Mod 5 = 0 Then
t = .Range("a26").Top
x = x + w + 20
Else
t = t + h + 20
End If
Next i
End With
End Sub
I'm trying to put together some code that merges cells where there is duplicate content in the row above. The code works, but once I get to the third row, I get an error that says:
Cell (unknown number): Invalid Request. Cannot merge cells of different sizes.
When I go back to the UI, I can perform the merge manually, so I don'be believe that the cells are different sizes. So I am thinking it is a problem with my code or a limitation of the VBA .Merge method?
Code is below
Sub testMergeDuplicateCells()
Dim oSl As Slide
Dim oSh As Shape
Dim k As Long
slideCount = ActivePresentation.Slides.Count
For k = 3 To (slideCount)
Set oSl = ActivePresentation.Slides(k)
'Start looping through shapes
For Each oSh In oSl.Shapes
'Now deal with text that is in a table
If oSh.HasTable Then
Dim x As Long, z As Long, y As Long
Dim oText As TextRange
Dim counter As Long
counter = 0
For x = 17 To oSh.Table.Rows.Count 'will always start on 17th row
For z = 1 To oSh.Table.Columns.Count
Set oText = oSh.Table.Cell(x, z).Shape.TextFrame.TextRange
y = x - 1
Set pText = oSh.Table.Cell(y, z).Shape.TextFrame.TextRange
If pText = oText Then
With oSh.Table
.Cell(x + counter, z).Shape.TextFrame.TextRange.Delete
.Cell(y, z).Merge MergeTo:=.Cell(x, z)
End With
counter = counter + 1
End If
Next z
Next x
End If
Next oSh
Next k
End Sub
I found the issue and came up with a very in-elegant solution (for now).
First was realizing what the actual dimensions of the cell were. Apparently when PPT does a cell merge it retains the underlying coordinates before the merge. So after I merge Cell (1,1) to Cell (2,1) the cell visually appears as one cell but retains the coordinates of both (1,1) and (2,1).
This utility helped me understand what was the actual underlying construct of my table, by selecting a cell in the UI and having the utility give me the full dimensions.
Sub TableTest()
Dim x As Long
Dim y As Long
Dim oSh As Shape
Set oSh = ActiveWindow.Selection.ShapeRange(1)
With oSh.Table
For x = 1 To .Rows.Count
For y = 1 To .Columns.Count
If .Cell(x, y).Selected Then
Debug.Print "Row " + CStr(x) + " Col " + CStr(y)
End If
Next
Next
End With
End Sub
I then put in a rather in-elegant If statement to have my loop skip to the last column that was part of the set of merged cells, so the Delete and Merge only statement only happened once. The error was introduced when (as Steve pointed out above) the loop looked at the same cell again and interpreted it as having duplicate value across two cells, even though it was one value in a merged cell.
Sub MergeDuplicateCells()
Dim oSl As Slide
Dim oSh As Shape
Dim k As Long
slideCount = ActivePresentation.Slides.Count
For k = 3 To (slideCount)
Set oSl = ActivePresentation.Slides(k)
'Start looping through shapes
For Each oSh In oSl.Shapes
'Now deal with text that is in a table
If oSh.HasTable Then
Dim x As Long, z As Long, y As Long
Dim oText As TextRange
For z = 1 To oSh.Table.Columns.Count
'inelegant solution of skipping the loop to the last column
'to prevent looping over same merged cell
If z = 3 Or z = 6 Or z = 8 Or z = 16 Then
For x = 17 To oSh.Table.Rows.Count
Set oText = Nothing
Set pText = Nothing
Set oText = oSh.Table.Cell(x, z).Shape.TextFrame.TextRange
If x < oSh.Table.Rows.Count Then
y = x + 1
Set pText = oSh.Table.Cell(y, z).Shape.TextFrame.TextRange
If pText = oText And Not pText = "" Then
With oSh.Table
Debug.Print "Page " + CStr(k) + "Merge Row " + CStr(x) + " Col " + CStr(z) + " with " + "Row " + CStr(y) + " Col " + CStr(z)
.Cell(y, z).Shape.TextFrame.TextRange.Delete
.Cell(x, z).Merge MergeTo:=.Cell(y, z)
End With
End If
End If
Next x
End If
Next z
End If
Next oSh
Next k
End Sub
Develop a VBA to create the values into the three rectangles and determine the intersection of the numbers.
Example code for creating code.
Public Sub call_shareRectangles()
Call shareRectangles("inputRange", "C1")
End Sub
Private Sub shareRectangles(referenceRange As String, initCell As String)
Dim R As Range
Dim rangeIterator As Integer
Dim countRange As Integer
Dim move As Integer
Set R = Names(referenceRange).RefersToRange
rangeIterator = 1
countRange = Range(referenceRange).Count
move = 0
While (rangeIterator <= countRange)
For i = 1 To R(rangeIterator)
For j = 1 To R(rangeIterator)
Range(initCell).Offset(move + i - 1, j - 1) = R(rangeIterator)
Next j
Next i
move = move + R(rangeIterator)
rangeIterator = rangeIterator + 1
Wend
End Sub
Solution rectangles needed
This code:
Dim c As Range
With Range("B2:G7")
.BorderAround Weight:=xlMedium, Color:=rgbCoral
For Each c In .Cells: c.value = c.value + 1: Next
End With
With Range("C4:E12")
.BorderAround Weight:=xlMedium, Color:=rgbDarkViolet
For Each c In .Cells: c.value = c.value + 1: Next
End With
With Range("D3:I10")
.BorderAround Weight:=xlMedium, Color:=rgbGray
For Each c In .Cells: c.value = c.value + 1: Next
End With
will produce this result:
I was wondering if anyone had any experience imposing time limits on sections of code. I have programmed a search engine into an excel spreadsheet in VBA and there is a section of the code that removes duplicate results. Now this part can sometimes stretch on for quite a long time if given the most vague search criteria. So I would like to impose a time limit for this operation. I have looked everywhere for a solution and tried using OnTime, but it doesnt seem to work in the way I need. Ideally, I'd like an imposed time limit and then when that is reached a GoTo statement, to move it further on in the code. From what I have read the OnTime will not interrupt an operation, but will wait for it to finish instead, this is not what I want.
Thanks for your help guys.
Amy
I've added my code:
Sub RemoveDuplicates()
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Application.EnableEvents = False
'Code called upon through the other macros which will remove duplicates from all the types of search.
Application.StatusBar = "Removing Duplicates...."
Dim k As Integer
Dim SuperArray As String
Dim CheckingArray As String
Dim Duplicate As Boolean
Dim i As Integer
Dim j As Integer
Dim m As Integer
Dim Endrow As Integer
Dim Endcolumn As Integer
Dim w As Integer
Dim x As Integer
Dim n As Integer
w = 1
x = 9
Endcolumn = Module6.Endcolumn(x)
Endrow = Module6.Endrow(w)
If Worksheets("Search Engine").Cells(9, Endrow) = "Percentage Similarity" Then
Endrow = Endrow - 1
End If
For i = 9 To Endcolumn
j = 1
k = i + 1
Do While j <> Endrow + 1
SuperArray = Cells(i, j) & Superstring
Superstring = SuperArray
j = j + 1
Loop
For k = k To Endcolumn
m = 1
Do While m <> Endrow
CheckingArray = Cells(k, m) & Uberstring
Uberstring = CheckingArray
m = m + 1
Loop
If Uberstring = Superstring Then
n = 1
Do While n <> Endrow + 1
If Worksheets("Search Engine").Cells(k, n).Interior.ColorIndex = 37 Then
Worksheets("Search Engine").Cells(i, n).Interior.ColorIndex = 37
End If
n = n + 1
Loop
Rows(k).Clear
End If
Uberstring = -1
Next k
Superstring = -1
Next i
Do While i > 9
If Cells(i, 1) = Empty Then
Rows(i).Delete
End If
i = i - 1
Loop
End Sub
I assume your code must have some kind of loop, e.g. For Each, While ... Wend, Do ... Loop Until, etc.
In theses cases, extend the condition by a comparison to the Timer. This returns you a Double between 0 and 86400, indicating how many seconds have passed since midnight. Thus, you also need to account for the day break. Here is some example code showing you implementations for three different loop constructs:
Sub ExampleLoops()
Dim dblStart As Double
Dim tmp As Long
Const cDblMaxTimeInSeconds As Double = 2.5
dblStart = Timer
'Example with For loop
For tmp = 1 To 1000
tmp = 1 'to fake a very long loop, replace with your code
DoEvents 'your code here
If TimerDiff(dblStart, Timer) > cDblMaxTimeInSeconds Then GoTo Finalize 'Alternative: Exit For
Next
'Alternative example for Do loop
Do
DoEvents 'your code here
Loop Until TimerDiff(dblStart, Timer) > cDblMaxTimeInSeconds And False 'your condition here
'Alternative example for While loop
While TimerDiff(dblStart, Timer) <= cDblMaxTimeInSeconds And True 'your condtion here
DoEvents 'your code here
Wend
Finalize:
'FinalizeCode here
Exit Sub
End Sub
Function TimerDiff(dblTimerStart As Double, dblTimerEnd As Double)
Dim dblTemp As Double
dblTemp = dblTimerEnd - dblTimerStart
If dblTemp < -43200 Then 'half a day
dblTemp = dblTemp + 86400
End If
TimerDiff = dblTemp
End Function