Customized CATIA V5 Macro to browse Excel coordinate file & plot points - vba

Please bear with my limited knowledge in CATIA VBA.
I am having some difficulties in customize a CATIA V5 macro to browse for Excel coordinate points and plot it in CATIA, all with a click on the customized CATIA icon.
I got an Excel file with many XYZ coordinates, let call it ExcelP1
(The excel file has no scripts/Macro in it), I would like to develop
a macro in CATIA to read & plot points from ExcelP1.
Currently i have another "Excel file with macro" to browse the
ExcelP1, and plot the points in CATIA. But i need to open and run
the "Excel file with macro" first to initiate CATIA. The scripts are
as below (i didn't develop this)
Public Filename As String
Private Sub Browse_Click()
'Open File
Mainform.Hide
Filename = Application.GetOpenFilename("Excel Files (*.xls), *.xls")
If Filename <> "False" Then
Application.Visible = False
filenamebox.Value = Filename
Else
Application.Visible = False
Filename = filenamebox.Value
End If
Mainform.Show
End Sub
Private Sub ClearButton_Click()
Mainform.Hide
ActiveWorkbook.Close (False)
Application.Visible = False
End Sub
Private Sub OKButton_Click()
'Set Up Message Labels
Title = "Information Message"
'Check for Entered Values
If filenamebox.Value <> "" Then
Workbooks.Open Filename:=Filename
Application.Visible = False
'Start CATIA and add an Open body to the document
Start_CATIA
Mainform.Hide
'Read Point Data from file and create point in CATIA
i = 2
Do Until Worksheets("Sheet1").Range("a" & i).Value = ""
x = Worksheets("Sheet1").Range("a" & i).Value
y = Worksheets("Sheet1").Range("b" & i).Value
z = Worksheets("Sheet1").Range("c" & i).Value
Create_Point
i = i + 1
Loop
i = i - 2
MsgBox i & " Points Created in New Part", , Title
Else
MsgBox "Enter a Filename", , Title
End If
ActiveWorkbook.Close (False)
Mainform.Show
End Sub
Private Sub UserForm_Initialize()
If Worksheets("Filepath_Location").Range("a1").Value <> "" Then
Filename = Worksheets("Filepath_Location").Range("a1").Value
filenamebox.Value = Filename
End If
End Sub
What do I need to add/modify in order for the scripts to run in CATIA?

The first thing you need to do after you start Catia and get the application is to create a new Part in which you will be adding the points.
Dim MyPartDocument As PartDocument
Dim MyPart As Part
Dim PointGeoSet As HybridBody
Set MyPartDocument = CATIA.Documents.Add("Part")
Set MyPart = MyPartDocument.Part
Set PointGeoSet = MyPart.HybridBodies.Add()
PointGeoSet.Name = "MyPoints"
The next thing is to create the point from the excel data by using a function like this. I like to create a wrapper, but you can rewrite this anyway you want:
Sub CreateXYZPoint(TargetPart As Part, TargetGeometricalSet As HybridBody, _
Xmm As Double, Ymm As Double, Zmm As Double, _
PointCount As String)
Dim HSFactory As HybridShapeFactory
Dim NewPoint As Point
'get the factory
Set HSFactory = TargetPart.HybridShapeFactory
'create the point with the factory
Set NewPoint = HSFactory.AddNewPointCoord(Xmm, Ymm, Zmm)
'Append the point to the geometrical set
TargetGeometricalSet.AppendHybridShape NewPoint
'rename the point
NewPoint.Name = "Point." & PointCount
End Sub
You Would call
CreateZYXPoint MyPart, PointGeoSet,x,y,z,cstr(i) in your loop
Finally, at the end of your loop, you will want to update the part so call:
MyPart.Update
It is much faster to do a single update at the end of your program than to update after each point is created.
This should get you started. Remember, Catia uses Millimeters as it's base internal units. Therefore, your spreadsheet match units or you must do a unit conversion before calling CreateXYZPoint...or However you want to accomplish that.
Let me know if this works for you.
Edit: Here's a link to the code put together with your code above. You need to make sure you excel code is working, but where I inserted the Catia code is correct:
http://pastebin.com/vxFcPw52

Related

Catia VBA Automation Error Run-Time 80010005 - Selection ERROR

I have a Problem with my Userform. It should automatically Switch to another TextBox when an selection in the catpart made. I get the Automation Error: It is illegal to call out while inside message filter.
Run-time error '-2147418107 (80010005)
Sub Auswahl_Click()
Dim sel As Object, Objekt As Object, ObjektTyp(0)
Dim b, Auswahl, i As Integer
ObjektTyp(0) = "Body"
Set sel = CATIA.ActiveDocument.Selection
For i = 1 To 6
sel.Clear
UserFormNow.Controls("Textbox" & i).SetFocus
Auswahl = sel.SelectElement2(ObjektTyp, "Wähle ein Body aus...", False)
Set b = CATIA.ActiveDocument.Selection.Item(i)
If Auswahl = "Normal" Then
Set Objekt = sel.Item(i)
UserFormNow.ActiveControl = Objekt.Value.Name
sel.Clear
End If
i = i + 1
Next
sel.Clear
End Sub
' EXCEL DATEI ÖFFNEN____________________________________
Sub Durchsuchen1_Click()
Dim FPath As String
FPath = CATIA.FileSelectionBox("Select the Excel file you wish to put the value in", "*.xlsx", CatFileSelectionModeOpen)
If FPath = "" Then
Else
DurchsuchenFeld.AddItem FPath
ListBox1.Clear
ListBox1.AddItem "Bitte wählen Sie das Panel"
TextBox1.SetFocus
End If
End Sub
' FORMULAR SCHLIEßEN____________________________________
Sub ButtonEnd_Click()
ButtonEnd = True
Unload UserFormNow
End Sub
First you have to know that when you use an UI and still want to interact with CATIA, you have to choices:
Launch the UI in NoModal: mode UserFormNow.Show 0
Hide the UI each time you want to interact with CATIA: Me.Hide or UserFormNow.Hide
Then, I strongly recommend you to avoid looking for items with names:
UserFormNow.Controls("Textbox" & i).SetFocus
If you want to group controls and loop through them, use a Frame and then use a For Each loop.
For Each currentTextBox In MyFrame.Controls
MsgBox currentTextBox.Text
Next
Regarding your code, many simplifications can be done:
Private Sub Auswahl_Click()
Dim sel As Object
Dim currentTextBox As TextBox
Dim Filter As Variant
ReDim Filter(0)
Filter(0) = "Body"
Set sel = CATIA.ActiveDocument.Selection
'Loop through each textbox
For Each currentTextBox In MyFrame.Controls
sel.Clear
'Ask for the selection and test the result at the same time
If sel.SelectElement2(Filter, "Wahle ein Body aus...", False) = "Normal" Then
'Get the name without saving the object
currentTextBox.Text = sel.Item2(1).Value.Name
Else
'allow the user to exit all the process if press Escape
Exit Sub
End If
Next
sel.Clear
End Sub

Remove all macros from a visio 2013 file

I have a Viso 2013 .vstm file that launches a VBA macro on document creation (template instanciation when a user opens the template manually). This macro populates the created drawing from a datasource. When finished, I would like to save programatically (from VBA) the drawing that has been generated as a .vsdx file, i.e. with all VBA macros that were used to populate the drawing being removed.
My questions are:
Is it possible to remove all macros programatically from a VBA macro (Visio 2013) which is in the .vstm file itself without causing the VBA Macro to fail and if yes, how can I do it ?
If 1. is not possible, how can I force programatically Visio to save to .vsdx a drawing that has macros (i.e. save ignoring all macros)
If 2. is not possible, how can I copy current drawing (everything except macros) to a new Drawing which should then be savable to .vsdx?
I have tried the following:
Deleting all lines with VBProject.VBComponents.Item(index).CodeModule.DeleteLines causes the macro to fail with "End Function is missing" (I have checked and there is no missing End Function anywhere, my guess is that the macro probably deletes the code that hasn't been executed yet, which in turn causes this error)
Save and SaveEX do not work either, I get a "VBProjects cannot be saved in macro-free files" error/message, even if I add a Application.AlertResponse = IDOK prior to the call to Save / SaveEx.
Here follows a sample code.
Private Sub RemoveVBACode()
' If document is a drawing remove all VBA code
' Works fine however execution fails as all code has been deleted (issue 1)
If ActiveDocument.Type = visTypeDrawing Then
Dim i As Integer
With ActiveDocument.VBProject
For i = .VBComponents.Count To 1 Step -1
.VBComponents.Item(i).CodeModule.DeleteLines 1, .VBComponents.Item(i).CodeModule.CountOfLines
Next i
End With
On Error GoTo 0
End If
End Sub
Private Sub SaveAsVSDX(strDataFilePath As String)
RemoveVBACode
Application.AlertResponse = IDOK
' Next line fails at runtime (issue 2), the same occurs when using Save
ThisDocument.SaveAsEx strDataFilePath, visSaveAsWS + visSaveAsListInMRU
Application.AlertResponse = 0
End Sub
The code that starts the execution of the Macro is the following event:
' This procedure runs when a Visio document is
' created. I.e., when the template (.vstm) is opened.
Private Sub Document_DocumentCreated(ByVal Doc As IVDocument)
' ...
SaveAsVSDX (strDataFilePath)
' ...
End Sub
I finally found a way to achieve what I wanted : generate a macro-less visio drawing, from a macro-enabled drawing.
What IS NOT possible from my understanding :
Have vba code that removes modules / class modules that is launched through an event such as Document_DocumentCreated. The best I could achieve is to remove the content of ThisDocument vba visio object, but all code in modules / class modules were not removable (note that if the macro is called manually, everything works like a charm, but this was not what I wanted to achieve).
Saving a a drawing instanciated from a vstm template as a macro-less vsdx file.
What IS possible (and is my solution to the third part of the question) :
Instead of loading datasource into the drawing instanciated from the vstm file, have the macro do the following:
select all shapes that appear on the page of the drawing that has been instanciated
group them
copy them
create a new Document
setup the page of the new document (orientation, size, disable snapping and gluing)
paste the group into the first page of the newly created document
center the drawing on the new document
Then load the datasource into the newly created document and link data to existing Shapes
Finaly you can save the new document as vsdx
With lots of shapes (more than 400) this takes some time (around 10 seconds), but it works.
Here is the code of the class module that generates the document.
Option Explicit
'Declare private variables accessible only from within this class
Private m_document As Document
Private m_dataSource As DataSourceFile
Private m_longDataRecordsetID As Long
Public Function Document() As Document
Set Document = m_document
End Function
Private Sub CreateDocument()
' I consider here that the active window is displaying the diagram to
' be copied
ActiveWindow.ViewFit = visFitPage
ActiveWindow.SelectAll
Dim activeGroup As Shape
Set activeGroup = ActiveWindow.Selection.Group
activeGroup.Copy
ActiveWindow.DeselectAll
Set m_document = Application.Documents.Add("")
' I need an A4 document
m_document.Pages(1).PageSheet.CellsSRC(visSectionObject, visRowPage, visPageWidth).FormulaU = "297 mm"
m_document.Pages(1).PageSheet.CellsSRC(visSectionObject, visRowPage, visPageHeight).FormulaU = "210 mm"
m_document.Pages(1).PageSheet.CellsSRC(visSectionObject, visRowPrintProperties, visPrintPropertiesPageOrientation).FormulaForceU = "2"
m_document.Pages(1).PageSheet.CellsSRC(visSectionObject, visRowPrintProperties, visPrintPropertiesPaperKind).FormulaForceU = "9"
m_document.SnapEnabled = False
m_document.GlueEnabled = False
m_document.Pages(1).Paste
m_document.Pages(1).CenterDrawing
End Sub
Private Sub LoadDataSource()
Dim strConnection As String
Dim strCommand As String
Dim vsoDataRecordset As Visio.DataRecordset
strConnection = "Provider=Microsoft.ACE.OLEDB.12.0;" _
& "User ID=Admin;" _
& "Data Source=" + m_dataSource.DataSourcePath + ";" _
& "Mode=Read;" _
& "Extended Properties=""HDR=YES;IMEX=1;MaxScanRows=0;Excel 12.0;"";" _
& "Jet OLEDB:Engine Type=34;"
strCommand = "SELECT * FROM [Data$]"
Set vsoDataRecordset = m_document.DataRecordsets.Add(strConnection, strCommand, 0, "Data")
m_longDataRecordsetID = vsoDataRecordset.ID
End Sub
Private Function CheckDataSourceCompatibility() As Boolean
Dim visRecordsets As Visio.DataRecordsets
Dim varRowData As Variant
Set visRecordsets = m_document.DataRecordsets
varRowData = visRecordsets(1).GetRowData(1)
If varRowData(3) = "0.6" Then
CheckDataSourceCompatibility = True
Else
MsgBox "Using invalid DataSource version, aborting. You shoud use data format version 0.6."
CheckDataSourceCompatibility = False
End If
End Function
Private Sub LinkDataToShapes()
Application.ActiveWindow.SelectAll
Dim ColumnNames(1) As String
Dim FieldTypes(1) As Long
Dim FieldNames(1) As String
Dim IDsofLinkedShapes() As Long
ColumnNames(0) = "ID"
FieldTypes(0) = Visio.VisAutoLinkFieldTypes.visAutoLinkCustPropsLabel
FieldNames(0) = "ID"
Application.ActiveWindow.Selection.AutomaticLink m_longDataRecordsetID, ColumnNames, FieldTypes, FieldNames, 10, IDsofLinkedShapes
Application.ActiveWindow.DeselectAll
End Sub
Public Function GenerateFrom(dataSource As DataSourceFile) As Boolean
Set m_dataSource = dataSource
'Store diagram services
Dim DiagramServices As Integer
DiagramServices = ActiveDocument.DiagramServicesEnabled
ActiveDocument.DiagramServicesEnabled = visServiceVersion140
' Create a new document that contains only shapes
CreateDocument
' Load datasource
LoadDataSource
' Check datasource conformity
If CheckDataSourceCompatibility Then
' Link data recordset to Visio shapes
LinkDataToShapes
GenerateFrom = True
Else
GenerateFrom = False
End If
'Restore diagram services
ActiveDocument.DiagramServicesEnabled = DiagramServices
End Function
Hope this helps.

How to embed large (max 10Mb) text files into an Excel file

What is the best way to store a large text file (max 10Mb) in an Excel file?
I have a couple of requirements:
It has to be embedded so that the excel file can be moved and sent to a different computer and all the text files will follow.
It needs to be done from a macro.
And a macro needs to be able to read the file contents after it has been embedded.
I already tried to store it by breaking the text into several chunks enough small to fit into a cell (~32 000 chars), but it didn't work. After my macro had inserted the first 150 000 characters it gave me an "Out of Memory" error.
I remember seeing one web page with a couple of options for this I but cannot find it anymore. Any suggestions are most welcome. I will try them out if you are not sure if it works or not.
It would likely be best to simply save the .txt file alongside the Excel file, and have the macro pull the text as needed from that folder. To read more on importing files see this:
http://answers.microsoft.com/en-us/office/forum/office_2010-customize/vba-code-to-import-multiple-text-files-from/525bd388-0f7d-4b4a-89f9-310c67227458
Keeping the .txt within the Excel file itself is not necessary and will likely make it harder to transfer files in the long run. For example, if you cannot e-mail a file larger than 10MB, then you can simply break your .txt file in half and e-mail separately - using a macro which loads the text into Excel locally.
Very simple CustomXMLPart example:
Sub CustomTextTester()
Dim cxp1 As CustomXMLPart, cxp2 As CustomXMLPart
Dim txt As String
'read file content
txt = CreateObject("scripting.filesystemobject").opentextfile( _
"C:\_Stuff\test.txt").readall()
'Add a custom XML part with that content
Set cxp1 = ThisWorkbook.CustomXMLParts.Add("<myXMLPart><content><![CDATA[" & txt _
& "]]></content></myXMLPart>")
Debug.Print cxp1.SelectSingleNode("myXMLPart/content").FirstChild.NodeValue
End Sub
Consider the method shown below. It uses Caption property of Label object located on a worksheet for data storage. So you can create a number of such containers with different names.
Sub Test()
Dim sText
' create special hidden sheet for data storage
If Not IsSheetExists("storage") Then
With ThisWorkbook.Worksheets.Add()
.Name = "storage"
.Visible = xlVeryHidden
End With
End If
' create new OLE object TypeForms.Label type as container
AddContainer "test_container_"
' read text from file
sText = ReadTextFile("C:\Users\DELL\Desktop\tmp\tmp.txt", 0)
' put text into container
PutContent "test_container_", sText
' retrieve text from container
sText = GetContent("test_container_")
' show length
MsgBox Len(sText)
' remove container
RemoveContainer "test_container_"
End Sub
Function IsSheetExists(sSheetName)
Dim oSheet
For Each oSheet In ThisWorkbook.Sheets
If oSheet.Name = sSheetName Then
IsSheetExists = True
Exit Function
End If
Next
IsSheetExists = False
End Function
Sub AddContainer(sName)
With ThisWorkbook.Sheets("storage").OLEObjects.Add(ClassType:="Forms.Label.1")
.Visible = False
.Name = sName
End With
End Sub
Sub RemoveContainer(sName)
ThisWorkbook.Sheets("storage").OLEObjects.Item(sName).Delete
End Sub
Sub PutContent(sName, sContent)
ThisWorkbook.Sheets("storage").OLEObjects.Item(sName).Object.Caption = sContent
End Sub
Function GetContent(sName)
GetContent = ThisWorkbook.Sheets("storage").OLEObjects.Item(sName).Object.Caption
End Function
Function ReadTextFile(sPath, iFormat)
With CreateObject("Scripting.FileSystemObject").OpenTextFile(sPath, 1, False, iFormat)
ReadTextFile = ""
If Not .AtEndOfStream Then ReadTextFile = .ReadAll
.Close
End With
End Function

How do you populate imagelist with shape from worksheet?

I wish to create a Treeview with images and data from a worksheet (each line has the name of a part, its next up assembly and an icon all populated by the user). I've finally managed to get the treeview to populate correctly and to include images from an external source (based on code from various sources and just a little of my own).
Setting up the Imagelist and assigning it to the Treeview is still a mystery to me but it works.
What is missing is getting the pictures from inside the worksheet and into the Imagelist instead of from an outside source ((using iml.ListImages.Add 1, "img1", LoadPicture("C:\Temp\red.jpg") ).
I read dozens of posts about it to no avail, and there is one that is mentioned in several places but is a deadend. The one other alternative I've read about involves copying the picture to the clipboard and pasting elsewhere but it involves a lot of code and seemed beyond my capability, so I haven't tried it yet.
I can "read" the pictures properties from the worksheet using Sheet1.Shapes(1) or similar with .Type (which results in "13") or .Name (which returns "Picture 1" for example) or .TopLeftCell.Address (which returns "$C$1" for example) etc. So I know I have access to them and am referencing the correct objects.
When I try to use iml.ListImages.Add 1, "img1", Sheet1.Shapes(1) I get a "Invalid Picture" error.
When I try to use iml.ListImages.Add 1, "img1", Sheet1.Shapes(1).Picture I get a "Object doesn't support this property or method" error.
When I try to use iml.ListImages.Add 1, "img1", Sheet1.Shapes(1).CopyPicture I get a "Type Mismatch" error.
I don't know what else to try and where else to look. Please help.
EDIT:
All this happens within a userform.
Are you using a UserForm? If so here is a suggestion or more of a workaround to your issue.
Why have the images in your worksheet to then try and load them in the form? Maybe try having them in the UserForm in the first place, here is how.
Create a frame on your userform:
Frame http://im88.gulfup.com/Moy8I6.png
Set the visible property of the frame to "False":
Visible http://im88.gulfup.com/sAIQqh.png
Insert your images by adding a picture control and loading the images, you can add as many images as you need:
Images http://im88.gulfup.com/oas0EQ.png
Name the images:
Name http://im88.gulfup.com/cIO317.png
Drag all the images one over the other into the frame, (you can then move the frame into a corner so it doesn't bother you:
Drag http://im88.gulfup.com/1fOSut.png
Move Away http://im88.gulfup.com/Q1fzKd.png
Next create a picture control, this is what you will use to display the picture based on a selection:
Form View http://im88.gulfup.com/X1UVRB.png
In this example, I am going to use a combobox for the selection. Now insert the below code in to the form which is pretty straight forward:
Private Sub ComboBox1_Change()
' Image1 is the name of the created picture control
UserForm3.Controls.Item("Image1").Picture = UserForm3.Controls.Item(UserForm3.ComboBox1.Value).Picture
End Sub
Private Sub UserForm_Initialize()
UserForm3.ComboBox1.AddItem "Argentina"
UserForm3.ComboBox1.AddItem "Brazil"
UserForm3.ComboBox1.AddItem "Chile"
End Sub
As you will see, the frame with the pictures is Hidden, and the image is changing inside the picture control based on a selection:
Result http://im88.gulfup.com/MSqyHF.png
I think it's the better way to go as opposed to exporting the images from the worksheet to a Temp folder and then loading them back into the picture controls.
#SiddhartRout provided the alternative that worked in a comment above: "Stephen Bullen's PastePicture code" as shown HERE. It's the only alternative I found that would not require going outside the file and it worked fine (on a sample file; still pending testing on a bigger example).
Thank you all for the help.
I would like to upload the file with the code etc. but I don't know how to do it, so I'm pasting the part of the "heart" of the code. There are two more modules: one to call the userform and Stephen Bullen's module. The code below is added to the userform itself, and it contains the treeview, the "OK" button and two images called "RED" and "GREEN" which are just small square jpgs of the respective color. I hope this helps.
' based on macros written 19991217 by Ole P. Erlandsen, ope#erlandsendata.no
Option Explicit
Private Sub CommandButton1_Click()
Dim i As Integer, strNodes As String, lngSelCount As Long
Me.Hide
lngSelCount = 0
strNodes = "Checked Items" & Chr(13) & "Index, Key, Text:" & Chr(13)
For i = 1 To TreeView1.Nodes.Count
With TreeView1.Nodes(i)
If .Checked Then
strNodes = strNodes & .Index & "; " & .Key & "; " & .Text & "; " & .Image & Chr(13)
lngSelCount = lngSelCount + 1
End If
End With
Next i
strNodes = strNodes & Chr(13) & "Count of Checked Items: " & lngSelCount
strNodes = strNodes & Chr(13) & Chr(13) & _
"Selected Item" & Chr(13) & "Index, Key, Text:" & Chr(13)
With TreeView1.SelectedItem
strNodes = strNodes & .Index & "; " & .Key & "; " & .Text & "; " & .Image & Chr(13)
End With
MsgBox strNodes, , "TreeView1 Output"
Unload Me
End Sub
Private Sub UserForm_Initialize()
'Author: Paulo Mendonça 02/September/2014 ppmendonca#hotmail.com
Dim oNode, oParent As Node
Dim oCell As Range
Dim oShape As Shape
Dim iml As ImageList
Dim oImage, oSheet, oDataColumn As String
Dim oParentColumnOffset, oImageColumnOffset, oInitialDataRow As Integer
Dim oFound As Boolean
oSheet = "Sheet2"
oDataColumn = "A"
oInitialDataRow = 2
oImageColumnOffset = 2
oParentColumnOffset = 1
'create new ImagList and populate it
Set iml = New ImageList
'iml.ImageHeight = 256
'iml.ImageWidth = 256
iml.ListImages.Add 1, "red", RED.Picture 'defined in UserForm1 and set to invisible
iml.ListImages.Add 2, "green", GREEN.Picture 'defined in UserForm1 and set to invisible
For Each oShape In Sheets(oSheet).Shapes 'look up every shape in the sheet (including non-pictures and add a picture of it in iml
If oShape.Type = 13 Then 'if is picture
If Not PictureKeyExists(oShape.TopLeftCell.Address, iml) Then 'find if picture key exists, if not add it
oShape.CopyPicture xlScreen, xlBitmap 'copy shape to clipboard
iml.ListImages.Add 3, oShape.TopLeftCell.Address, PastePicture(xlBitmap) 'add a picture of the clipboard contents to iml with key = to shapes top left corner cell address
'NOTE: eventhough the index is set to 3 the actual index of the pictures gets incremented automatically
Else 'if yes report to user and don't add it
MsgBox "More than one image in cell " & oShape.TopLeftCell.Address & "." & Chr(13) & _
"Only one will be used."
End If
End If
Next
'set TreeView1 formats etc.
With TreeView1
Set .ImageList = iml
.Indentation = 14
.LabelEdit = tvwManual
.HideSelection = False
.CheckBoxes = True
.Style = tvwTreelinesPlusMinusPictureText
.BorderStyle = ccFixedSingle
End With
'populate TreeView1
With TreeView1.Nodes
.Clear
Set oNode = .Add(, , "Root", "Root Node") 'add root node; key = "Root"
oNode.Expanded = True
oNode.EnsureVisible
'look up all cells from A2 to last cell with content in it and add it to TreeView1
For Each oCell In Sheets(oSheet).Range(oDataColumn & oInitialDataRow, Sheets(oSheet).Range(oDataColumn & "65536").End(xlUp)).SpecialCells(xlCellTypeVisible)
'find if parent exists
Set oParent = Nothing
For Each oNode In TreeView1.Nodes
If oNode.Text = oCell.Offset(0, oParentColumnOffset).Value Then
Set oParent = oNode
Exit For
End If
Next
'find if picture exists, if yes use it, if not use "RED"
If PictureKeyExists(oCell.Offset(0, oImageColumnOffset).Address, iml) Then
oImage = oCell.Offset(0, oImageColumnOffset).Address
Else
oImage = "red"
End If
'add node
If oParent Is Nothing Then 'if parent not found add as child to root; key = name
Set oNode = .Add("Root", tvwChild, oCell.Value, oCell.Value, oImage)
oNode.Expanded = False
Else 'add as child to parent found previously; key = name concatenated to parent node key
Set oNode = .Add(oParent.Key, tvwChild, oParent.Key & "|" & oCell.Value, oCell.Value, oImage)
oNode.Expanded = False
End If
Next
End With
End Sub
Function PictureKeyExists(oKey As String, oImageList As ImageList) As Boolean
'Author: Paulo Mendonça 29/August/2014 ppmendonca#hotmail.com
Dim oPicture As ListImage
PictureKeyExists = False
For Each oPicture In oImageList.ListImages
If oPicture.Key = oKey Then
PictureKeyExists = True
Exit For
End If
Next
End Function

Programmatically combine slides from multiple presentations into a single presentation

I need to automate the creation of a presentation (either OpenOffice or Powerpoint). The presentation should take the first two slides of each of the presentations in a given directory, and then combine them into a single presentation. I'm confused as to what approach I should take to solve this. Any pointers will be appreciated.
Talking about PowerPoint, you would use a VBA Macro to do the job, something like
Sub Pull()
Dim SrcDir As String, SrcFile As String
SrcDir = PickDir()
If SrcDir = "" Then Exit Sub
SrcFile = Dir(SrcDir & "\*.ppt")
Do While SrcFile <> ""
ImportFromPPT SrcDir + "\" + SrcFile, 1, 2
SrcFile = Dir()
Loop
End Sub
Selecting your source directory you can use this function
Private Function PickDir() As String
Dim FD As FileDialog
PickDir = ""
Set FD = Application.FileDialog(msoFileDialogFolderPicker)
With FD
.Title = "Pick a directory to work on"
.AllowMultiSelect = False
.Show
If .SelectedItems.Count <> 0 Then
PickDir = .SelectedItems(1)
End If
End With
End Function
Now - the main point is inserting slides from another PPT while preserving the source format. This is a tricky thing, as the PPT VBA InsertFromFile method is of no good use. Microsoft gave us good time to figure it out the hard way in countless 20hrs debuging sessions :-) and you need to type a lot of code to get it done correctly - far more complicated than using the dialogue manually, in particular if your source slide deviates from your source master slide.
If your PPT's are sticking to their masters, you can safely omit all code between the ">>>>"
Private Sub ImportFromPPT(FileName As String, SlideFrom As Long, SlideTo As Long)
Dim SrcPPT As Presentation, SrcSld As Slide, Idx As Long, SldCnt As Long
Set SrcPPT = Presentations.Open(FileName, , , msoFalse)
SldCnt = SrcPPT.Slides.Count
If SlideFrom > SldCnt Then Exit Sub
If SlideTo > SldCnt Then SlideTo = SldCnt
For Idx = SlideFrom To SlideTo Step 1
Set SrcSld = SrcPPT.Slides(Idx)
SrcSld.Copy
With ActivePresentation.Slides.Paste
.Design = SrcSld.Design
.ColorScheme = SrcSld.ColorScheme
' if slide is not following its master (design, color scheme)
' we must collect all bits & pieces from the slide itself
' >>>>>>>>>>>>>>>>>>>>
If SrcSld.FollowMasterBackground = False Then
.FollowMasterBackground = False
.Background.Fill.Visible = SrcSld.Background.Fill.Visible
.Background.Fill.ForeColor = SrcSld.Background.Fill.ForeColor
.Background.Fill.BackColor = SrcSld.Background.Fill.BackColor
' inspect the FillType object
Select Case SrcSld.Background.Fill.Type
Case Is = msoFillTextured
Select Case SrcSld.Background.Fill.TextureType
Case Is = msoTexturePreset
.Background.Fill.PresetTextured (SrcSld.Background.Fill.PresetTexture)
Case Is = msoTextureUserDefined
' TextureName gives a filename w/o path
' not implemented, see picture handling
End Select
Case Is = msoFillSolid
.Background.Fill.Transparency = 0#
.Background.Fill.Solid
Case Is = msoFillPicture
' picture cannot be copied directly, need to export and re-import slide image
If SrcSld.Shapes.Count > 0 Then SrcSld.Shapes.Range.Visible = False
bMasterShapes = SrcSld.DisplayMasterShapes
SrcSld.DisplayMasterShapes = False
SrcSld.Export SrcPPT.Path & SrcSld.SlideID & ".png", "PNG"
.Background.Fill.UserPicture SrcPPT.Path & SrcSld.SlideID & ".png"
Kill (SrcPPT.Path & SrcSld.SlideID & ".png")
SrcSld.DisplayMasterShapes = bMasterShapes
If SrcSld.Shapes.Count > 0 Then SrcSld.Shapes.Range.Visible = True
Case Is = msoFillPatterned
.Background.Fill.Patterned (SrcSld.Background.Fill.Pattern)
Case Is = msoFillGradient
' inspect gradient type
Select Case SrcSld.Background.Fill.GradientColorType
Case Is = msoGradientTwoColors
.Background.Fill.TwoColorGradient
SrcSld.Background.Fill.GradientStyle , _
SrcSld.Background.Fill.GradientVariant
Case Is = msoGradientPresetColors
.Background.Fill.PresetGradient _
SrcSld.Background.Fill.GradientStyle, _
SrcSld.Background.Fill.GradientVariant, _
SrcSld.Background.Fill.PresetGradientType
Case Is = msoGradientOneColor
.Background.Fill.OneColorGradient _
SrcSld.Background.Fill.GradientStyle, _
SrcSld.Background.Fill.GradientVariant, _
SrcSld.Background.Fill.GradientDegree
End Select
Case Is = msoFillBackground
' Only shapes - we shouldn't come here
End Select
End If
' >>>>>>>>>>>>>>>>>>>>
End With
Next Idx
End Sub
The code doesn't check for read-only or password protected fies and will crash on them. Also be careful not to run over the collector file itself. Otherwise it should work. I must admit I haven't reviewed the code for a long time ;-)
You can google "powerpoint join" to find a useful tool to join many ppts.
I'm glad #miked was able to get you what you needed.
Another method to consider, if using .NET, is discussed in this post
A simple and fast solution:
I := Presentation.Slides.InsertFromFile(FileName,X,StartSlideNo,EndSlideNo);
Presentation.Slides.Item(I).ApplyTheme(FileName);
Presentation.Slides.Item(I).ApplyTemplate(FileName);
Note:
X is place to insert slide in presentation
I is actual place where slide was inserted
Code is written in Delphi/Pascal but you can convert it easelly ...
You can do this with Aspose.Slides for .NET. It even allows joining OpenOffice and PowerPoint presentations together. View this article.
var presentation1 = new Presentation("presentation1.pptx");
var presentation2 = new Presentation("presentation2.odp");
var mergedPresentation = new Presentation();
while (mergedPresentation.Slides.Count > 0) mergedPresentation.Slides.RemoveAt(0);
// Adding two slides from the first PPTX presentation
mergedPresentation.Slides.AddClone(presentation1.Slides[0]);
mergedPresentation.Slides.AddClone(presentation1.Slides[1]);
// Adding two slides from the second OPD presentation
mergedPresentation.Slides.AddClone(presentation2.Slides[0]);
mergedPresentation.Slides.AddClone(presentation2.Slides[1]);
mergedPresentation.Save("mergedPresentation.pptx", SaveFormat.Pptx);