简体   繁体   English

通过 VBA 将大量图表从 Excel 复制并粘贴到 PowerPoint

[英]Copy and paste large number of charts from Excel to PowerPoint via VBA

The task is to loop through an Excel workbook with multiple sheets and copy all the charts contained in the workbook into a PowerPoint presentation, one chart per slide and always the same layout.任务是遍历具有多张工作表的 Excel 工作簿,并将工作簿中包含的所有图表复制到 PowerPoint 演示文稿中,每张幻灯片一个图表,并且始终使用相同的布局。

Sub PPT_Example()
    Dim pptApp As PowerPoint.Application
    Dim pptPres As PowerPoint.Presentation
    Dim sh As Worksheet
    Dim ch As ChartObject

    Set pptApp = New PowerPoint.Application
    pptApp.Visible = True
    Set pptPres = pptApp.Presentations.Add
    pptPres.PageSetup.SlideSize = PpSlideSizeType.ppSlideSizeOnScreen16x9

    For Each sh In ActiveWorkbook.Sheets
        For Each ch In sh.ChartObjects
            Dim pptSlide As Slide
            Dim Title As Object
            Dim Box As Object
            Dim Txt As Object
            Set pptSlide = pptPres.Slides.Add(pptPres.Slides.Count + 1, ppLayoutBlank)
            ch.Copy
            With pptSlide.Shapes.Paste
                .Top = Application.CentimetersToPoints(3.3)
                .Left = Application.CentimetersToPoints(0.76)
                .Width = Application.CentimetersToPoints(16)
                .Height = Application.CentimetersToPoints(10.16)
            End With
            'Insert Box
            Set Box = pptSlide.Shapes.AddShape(Type:=msoShapeRectangle, _
              Left:=Application.CentimetersToPoints(17.1), _
              Top:=Application.CentimetersToPoints(3.3), _
              Width:=Application.CentimetersToPoints(7.22), _
              Height:=Application.CentimetersToPoints(9.29))
            Prop_Box.Name = "Box"
            pptSlide.Shapes("Box").Fill.ForeColor.RGB = RGB(219, 233, 255)
            pptSlide.Shapes("Box").Line.ForeColor.RGB = RGB(0, 102, 255)
            
            'Insert the text box
            Set Txt = pptSlide.Shapes.AddTextbox(Orientation:=msoTextOrientationHorizontal, _
              Left:=Application.CentimetersToPoints(17.1), _
              Top:=Application.CentimetersToPoints(3.3), _
              Width:=Application.CentimetersToPoints(7.22), _
              Height:=Application.CentimetersToPoints(9.29))
            Txt.Name = "Txt"
            pptSlide.Shapes("Txt").TextFrame.TextRange.Font.Size = 14
            pptSlide.Shapes("Txt").TextFrame.TextRange.Font.Bold = msoCTrue
            pptSlide.Shapes("Txt").TextFrame.TextRange.Font.Name = "Arial"
            pptSlide.Shapes("Txt").TextFrame.TextRange.Text = "Sample Text"
            
            'Clear the Clipboard
            Dim oData   As New DataObject 'object to use the clipboard
            oData.SetText Text:=Empty 'Clear
            oData.PutInClipboard
        Next
    Next
End Sub

The code works on my example (2 sheets, 3 charts total) but not if I apply it to the real thing, which is a workbook with 10-15 sheets and 8 charts per sheet.该代码适用于我的示例(2 张纸,总共 3 张图表),但如果我将其应用于真实的东西,它是一个工作簿,每张纸有 10-15 张纸和 8 张图表。 At some (random?) point, the code stops and gives me this error.在某个(随机?)点,代码停止并给我这个错误。

Run-time error:运行时错误:
Shapes (unknown member): Invalid request.形状(未知成员):请求无效。 Clipboard is empty or contains data which may not be pasted here.剪贴板为空或包含可能无法粘贴到此处的数据。

I noted that the code crashed earlier, the more objects I put on the slides (which is why I left the text and the box in my example, although not strictly neccessary).我注意到代码更早崩溃了,我在幻灯片上放置的对象越多(这就是为什么我在示例中留下文本和框的原因,尽管并非绝对必要)。 Given that and the error message, I assumed the clipboard might not be cleared properly after each loop, so I put in a section to clear the clipboard but it didn't solve the issue.鉴于此和错误消息,我认为剪贴板在每次循环后可能无法正确清除,因此我放入了一个部分来清除剪贴板,但它并没有解决问题。

After the chart is copied, try adding DoEvents and pausing the macro for a few seconds before pasting it into your slide.复制图表后,尝试添加 DoEvents 并暂停宏几秒钟,然后将其粘贴到幻灯片中。 And the same thing after it's pasted into your slide.粘贴到幻灯片后也是如此。

So, for example, first add the following function to pause your code .因此,例如,首先添加以下函数来暂停您的代码。 . . . .

Sub PauseMacro(ByVal secs As Long)

    Dim endTime As Single
    endTime = Timer + secs
    
    Do
        DoEvents
    Loop Until Timer > endTime
    
End Sub

Then try something like this .然后尝试这样的事情。 . . . .

            ch.Copy
            
            DoEvents
            
            PauseMacro 5 'pause for 5 seconds
            
            With pptSlide.Shapes.Paste
                DoEvents
                PauseMacro 5 'pause for 5 seconds
                .Top = Application.CentimetersToPoints(3.3)
                .Left = Application.CentimetersToPoints(0.76)
                .Width = Application.CentimetersToPoints(16)
                .Height = Application.CentimetersToPoints(10.16)
            End With

You may find through testing that you can pause for less than 5 seconds, maybe 3 seconds.通过测试你可能会发现你可以暂停不到 5 秒,也许是 3 秒。

My approach is to split out potentially time-consuming operations into separate functions (see "'' Call as a Function" below).我的方法是将潜在的耗时操作拆分为单独的函数(请参阅下面的“'' Call as a Function”)。 When a function is called, and then has to return, it seems that Excel/VBA/the-little-green-men-running-everything make sure that whatever operation it is waits until the operation is finished (the chart is totally added to the clipboard, the clipboard contents are totally pasted, the shape is totally instantiated, etc.) before continuing.当一个函数被调用,然后必须返回时,似乎 Excel/VBA/the-little-green-men-running-everything 确保它的任何操作都等到操作完成(图表完全添加到剪贴板,剪贴板内容完全粘贴,形状完全实例化等),然后继续。

This also means not necessarily forcing a delay during execution that might not be needed (the Do Until or Loop Until or Wait that is often suggested).这也意味着不必在执行期间强制执行可能不需要的延迟(通常建议的Do UntilLoop UntilWait )。

So your code might look like this (caveat: untested)所以你的代码可能看起来像这样(警告:未经测试)

Sub PPT_Example()
    Dim pptApp As PowerPoint.Application
    Dim pptPres As PowerPoint.Presentation
    Dim sh As Worksheet
    Dim ch As ChartObject

    Set pptApp = New PowerPoint.Application
    pptApp.Visible = True
    Set pptPres = pptApp.Presentations.Add
    pptPres.PageSetup.SlideSize = PpSlideSizeType.ppSlideSizeOnScreen16x9

    For Each sh In ActiveWorkbook.Sheets
        For Each ch In sh.ChartObjects
            Dim pptSlide As Slide
            Dim Title As Object
            Dim Box As Object
            Dim Txt As Object
            Set pptSlide = NewSlide(pptPres) '' Call as a Function
            ch.Copy
            Dim shp As PowerPoint.Shape
            Set shp = NewShape(pptSlide) '' Call as a Function
            With shp
                .Top = Application.CentimetersToPoints(3.3)
                .Left = Application.CentimetersToPoints(0.76)
                .Width = Application.CentimetersToPoints(16)
                .Height = Application.CentimetersToPoints(10.16)
            End With
        'Insert Box
        Set Box = NewBox(pptSlide) '' Call as a Function
        Prop_Box.Name = "Box"
        pptSlide.Shapes("Box").Fill.ForeColor.RGB = RGB(219, 233, 255)
        pptSlide.Shapes("Box").Line.ForeColor.RGB = RGB(0, 102, 255)
        
        'Insert the text box
        Set Txt = NewTextBox(pptSlide) '' Call as a Function
        Txt.Name = "Txt"
        pptSlide.Shapes("Txt").TextFrame.TextRange.Font.Size = 14
        pptSlide.Shapes("Txt").TextFrame.TextRange.Font.Bold = msoCTrue
        pptSlide.Shapes("Txt").TextFrame.TextRange.Font.Name = "Arial"
        pptSlide.Shapes("Txt").TextFrame.TextRange.Text = "Sample Text"
        
        'Clear the Clipboard
        Dim oData   As New DataObject 'object to use the clipboard
        oData.SetText Text:=Empty 'Clear
        oData.PutInClipboard
        Next
    Next
End Sub

Function NewSlide(pptPres As PowerPoint.Presentation) As PowerPoint.Slide
    Set NewSlide = pptPres.Slides.Add(pptPres.Slides.Count + 1, ppLayoutBlank)
End Function

Function NewShape(pptSlide As PowerPoint.Slide) As PowerPoint.Shape
    Set NewShape = pptSlide.Shapes.Paste
End Function

Function NewBox(pptSlide As PowerPoint.Slide) As Object
    Set Box = pptSlide.Shapes.AddShape(Type:=msoShapeRectangle, _
        Left:=Application.CentimetersToPoints(17.1), _
        Top:=Application.CentimetersToPoints(3.3), _
        Width:=Application.CentimetersToPoints(7.22), _
        Height:=Application.CentimetersToPoints(9.29))
End Function

Function NewTextBox(pptSlide As PowerPoint.Slide) As Object
    Set NewTextBox = pptSlide.Shapes.AddTextbox(Orientation:=msoTextOrientationHorizontal, _
        Left:=Application.CentimetersToPoints(17.1), _
        Top:=Application.CentimetersToPoints(3.3), _
        Width:=Application.CentimetersToPoints(7.22), _
        Height:=Application.CentimetersToPoints(9.29))
End Function

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM