Autodesk® Inventor® API Lab for Power Users Wayne Brill – Autodesk Brian Ekins - Autodesk - Lab Assistant Philippe Leefsma - Autodesk - Lab Assistant Augusto Goncalves - Autodesk - Lab Assistant CP3505-L Are you an Autodesk Inventor power user with an interest in becoming even more productive? Would you like to automate or extend the capabilities of Inventor, but are new to computer programming? If so, this hands-on lab is designed for you. During the lab, you will create an Inventor plug-in that will introduce you to the programming world. This lab will provide a learning path for Inventor power users who know Autodesk Inventor but are absolutely new to programming and are thinking about taking the plunge. In this lab, we will be working with Autodesk Inventor API in Microsoft® Visual Basic® Express 2010. Learning Objectives At the end of this class, you will be able to: Describe the basics of using SelectSet in the Inventor API Describe the basics of using the Inventor API Create code that runs when a button is clicked so that it accesses selected components in an assembly Use Visual Basic Express 2010 About the Speaker Wayne has been a member of Autodesk Developer Technical Services supporting Autodesk Developer Network members for 10 years. Wayne provides API support for AutoCAD®, Autodesk® Inventor®, AutoCAD Mechanical, AutoCAD OEM, and RealDWG®. wayne.brill@autodesk.com Autodesk® Inventor® API Lab for Power Users Note: This handout is the same for two 90 minute LAB sessions. (This session and session CP2542-L) Six lessons in this LAB: LAB 1: The Basic Plug-in LAB 2: Create a new Visual Basic Express Project LAB 3: Connect to an Inventor Session LAB 4: Add a User Interface and logic flow using “If Then” and “For Next” LAB 5: Add attributes to selected components LAB 6: Add more UI and call Subs with an argument LAB 1: The Basic Plug-in In this lesson, to help you get a quick start, you will begin by writing part of a VB.NET application that uses the Autodesk Inventor API to hide selected components. 1. Open Visual Basic 2010 Express using the Windows Start menu, selecting All Programs, and then Microsoft Visual Studio 2010 Express, and then the Microsoft Visual Basic 2010 Express submenu-item. This launches the Visual Studio 2010 Express development environment. 2. Inside Visual Basic Express, on the File menu, click Open Project. Navigate to the subfolder of the supporting material you downloaded with this guide called ‘lesson1_VisualExpress2010’ and open the VB.NET project contained within it by selecting the project file ‘MyFirstInventorPlugin_Lesson1.vbproj’. 3. After you open the project you will see a form with one button (if you don’t see the Form, click on Form1.vb in the Solution Explorer frame in the upper right hand side). Right click on Form1 in the Solution Explorer and select View Code or just double click on the Form. 2 Autodesk® Inventor® API Lab for Power Users 4. In the code window, type the code below into the Sub Button1_Click. (This is what runs when the button is clicked). You may need to scroll down towards the bottom of the code to find the place to add the below code. You can just copy and paste the code, but we highly recommend you take the time to type it in as you’ll start to learn how VB Express helps you find and enter code using “IntelliSense” (as you start typing, use Tab to select the code snippets that build up various lines of code). At this stage you probably won’t understand what most of the code is doing – but this will give you the feeling of what it’s like to build a VB .NET application. 3 Autodesk® Inventor® API Lab for Power Users Type this following code: If _invApp.Documents.Count = 0 Then MsgBox("Need to open an Assembly document") Return End If If _invApp.ActiveDocument.DocumentType <> _ DocumentTypeEnum.kAssemblyDocumentObject Then MsgBox("Need to have an Assembly document active") Return End If Dim asmDoc As AssemblyDocument asmDoc = _invApp.ActiveDocument If asmDoc.SelectSet.Count = 0 Then MsgBox("Need to select a Part or Sub Assembly") Return End If 4 Autodesk® Inventor® API Lab for Power Users Dim selSet As SelectSet selSet = asmDoc.SelectSet Try Dim compOcc As ComponentOccurrence Dim obj As Object For Each obj In selSet compOcc = obj Debug.Print(compOcc.Name) compOcc.Visible = False Next Catch ex As Exception MsgBox("Is the selected item a Component?") MsgBox(ex.ToString()) Return End Try 5. On the File menu, click Save All. Don’t worry about the specifics of what this code is actually doing, for now: we’ll cover that within the next few lessons. That’s it! You have just written your first plug-in for Inventor. Let’s run the plug-in to see what it does. Running the code 1. Start Autodesk Inventor. (Note: When the project is run it will start a new session of Inventor if one is not already running.) 2. Within Inventor make sure you have an assembly active. There are several types of document that can be created or worked with inside Inventor. The most commonly used document types are Part (.ipt), Assembly (.iam) and Drawing (.idw). Open a new assembly and place some parts using the standard Inventor user-interface. 5 Autodesk® Inventor® API Lab for Power Users 3. To make Visual Basic Express execute the code you have entered, select Start Debugging on the Debug menu (you can use the F5 key or click on the filled green arrow – which looks like a “play” button – on the Debugging toolbar). This will cause our form to be displayed. You may need to minimize VB Express to see both the form and Inventor. 6 Autodesk® Inventor® API Lab for Power Users 4. Select one or more (by using the Ctrl key) components in the assembly and then click “Button1” on the form to execute our code and hide the selected components. 7 Autodesk® Inventor® API Lab for Power Users 5. To re-display the invisible components use the Inventor Assembly browser (you can identify them via their component icons which should now be grayed out). In the browser, right-click on the invisible components and pick Visibility, making them visible once again. 8 Autodesk® Inventor® API Lab for Power Users End of LAB 1. We will be reviewing the code in detail in Lesson 3. Link to LAB 1 Discussion LAB 2 - Create a new Visual Basic Express Project In this section, we will create a blank project similar to the one we were provided with in Lesson 1. This project will be the starting point for Lesson 3. 1. Close the project from Lesson1, if it is still open in Visual Basic Express, and then on the File menu, click New Project. This will open up a new dialog called New Project in which we have the option to select the template that will be used to create the plugin application. 9 Autodesk® Inventor® API Lab for Power Users New Project dialog As we are working with Visual Basic Express, our installed templates are under the Visual Basic category. In the center of the dialog, we can see various application templates that are available to us, the choice of which depends on the type of application we wish to create. As we’re creating a simple, standalone EXE with a dialog-based user interface, select the “Windows Forms Application” template. We now need to select a name for our project. Enter “MyFirstInventorPlug-in” in the box near the bottom of the New Project dialog and select OK: The basic solution containing a form should now be created and loaded in the Visual Basic Express editor. The project contains a blank form named Form1.vb. Forms have Design and Code views: we can switch between the two views by right-clicking on Form1.vb in the Solution Explorer and selecting View Code or View Designer. By default, commonly used .NET components get added as “references” to our project. You can see these by 10 Autodesk® Inventor® API Lab for Power Users right-clicking the project in the Solution Explorer and selecting Properties. In the Properties window select References. References in the Properties Window 2. So far we have created a blank “Windows Forms Application” project using Visual Basic Express. In order to create a plug-in for Inventor, we will need to make use of its API. To add the Inventor API to the components that can be used from our project, we need to add a reference to the API DLL located in <Inventor Product Installation folder>/Bin/Public Assemblies>. This folder contains one DLL that we need to add to project: Autodesk.Inventor.Interop.dll. In the Solution Explorer, right-click the project and choose Add Reference. Click the Browse tab in the Add Reference dialog and navigate to a location such as this beneath the installed location of Inventor on your system: “C:\Program 11 Autodesk® Inventor® API Lab for Power Users Files\Autodesk\Inventor 2012\Bin\Public Assemblies”. Select Autodesk.Inventor.Interop.dll and then OK. Referencing the Inventor API by adding a reference to the Interop.dll The Inventor API has now been referenced into our project. Before proceeding further, let’s use the Object Browser in Visual Basic Express to take a quick look at the contents of this DLL: objects representing Inventor's application, documents, elements and parameters. To display the Object Browser, either use the Window menu and select Object Browser or use the function key F2. 12 Autodesk® Inventor® API Lab for Power Users Object Browser with the ActiveDocument property of the Inventor.Application selected 3. We’re now ready to use the Inventor API by entering code in Visual Basic Express. In other words, to provide instructions for Inventor to perform certain tasks. While developing code, it’s a good idea to build the solution, from time to time, to check whether errors have been introduced in the code. The code doesn’t necessarily have to be complete or functional for the build process to provide useful information. This approach can help avoid potentially lengthy debugging once the code is complete, and has the side benefit of automatically saving any edited source files before the build starts. To build a solution inside Visual Basic Express, select Build <ProjectName> from the Debug pull-down menu. 13 Autodesk® Inventor® API Lab for Power Users Build the Application If the build was successful, you will see Build Succeeded in the status bar at the bottom-left corner of the Visual Basic Express interface. 4. Save the Project. To do this, on the File Menu select Save All. End of LAB 2 Link to Lab 2 Discussion LAB 3: Connect to an Inventor Session In this lesson, we will take a closer look at the Inventor API. In Lesson 1, we typed code (or instructions) into a code window associated with a Form and saw how it changed the visibility of 14 Autodesk® Inventor® API Lab for Power Users components in an assembly. In Lesson 2 we created a Windows Form application and referenced the Inventor API from it. In this lesson we will continue working on the project we created in Lesson 2, extending it to use the Inventor API to connect to or start a session of Inventor while taking a close look at how the code actually works. Add code to the project we created in lesson 2 In Lesson 2 we created a project and it referenced the Inventor API via Autodesk.Inventor.Interop.dll. Adding this reference to the Inventor API allows us to start using it in our project. In the following steps we will access the top level object in the API, the Inventor.Application object. 1. Open the project you created in Lesson 2 and add the following lines of code to the Form1.vb code window (above “Public Class Form1”). You can display the code window by right-clicking on Form1.vb in the Solution Explorer and selecting View Code. To make room for this place the cursor to the left of “Public Class” and press the Enter key a few times to add some blank lines at the top of the code window. Add these Imports statements to the top of the top of the code widow. Imports System Imports System.Type Imports System.Activator Imports System.Runtime.InteropServices Imports Inventor Keywords, such as “Imports” in the above code, are core to the VB.NET language and are displayed by Visual Basic Express in blue. They are used to help formulate our instructions for the computer. The “Imports” keyword has a particular role: it brings in the types from a specified namespace into the current code file’s useable types, which stops us from having to type complete namespaces in every line of code. What is a namespace? When we are working with different components and APIs in our Visual Basic project, it’s common to find multiple classes with the same name. To avoid this conflict, we use the concept of namespaces. A namespace is a way to organize classes by grouping them together in a logical way and to help us identity the class we wish to use. To give you an example, let’s say there are two boys named John living in the same neighborhood. One possible way of uniquely identifying one of the Johns might be to use his last name. But if both Johns have the same last name, such as Smith, this is no longer enough to uniquely identify them. We now need to use an additional identifier, such as the school at which they study. If this was put into VB.NET syntax, we might use SchoolA.Smith.John for one and SchoolB.Smith.John for the other. 15 Autodesk® Inventor® API Lab for Power Users After adding the Import statements several namespaces are now available including the Inventor namespace. The Inventor namespace is available for import because we referenced the Autodesk.Inventor.Interop.dll. The following code in Form1 defines a class. Public Class Form1 This is a standard declaration of a class and was added when the project was created by the Visual Studio Wizard. The public keyword states that this class is to be considered public and therefore accessible from other classes in this assembly (and even in other projects using our application as a component). The name Form1 is the name of the class. 2. Let’s now add a couple of variables after the class declaration. A variable is a named location where we place some data – usually of a specific type. Variables help us keep track of what’s going on in our application, as well as providing access to commonly used objects. We’ll start by creating a variable named _invApp. We can choose whatever names we want for our variables, as long as they are not reserved words (such as the “Imports” keyword) : we might also have chosen a more verbose name such as “theInventorApplication” for this variable. The underscore prefixing the variable name is a convention that denotes a class variable: one that can be accessed from all the Sub routines and Functions in the class. To declare the variable, we use the Dim keyword, followed by the variable name (_invApp). We specify the type of this variable after the “As” keyword (Inventor.Application). After declaring our _invApp variable – which will be used to provide quick access to the Inventor Application object – we’ll declare another variable, named _started, as a Boolean (which means it can either be True or False). Declaring variables reserves an area of memory to be used to store our data: the amount of memory depends on the size of the class being used. Note: Even though we added the Imports directive we still need to use “Inventor.Application”: using just “Application” would be ambiguous, as the System.Windows.Forms namespace also contains an Application type. Public Class Form1 Dim _invApp As Inventor.Application Dim _started As Boolean = False 16 Autodesk® Inventor® API Lab for Power Users 3. We are now going to create a Sub(-routine). A Sub is a block of code that can be executed under circumstances we decide: we might want it to be called when a particular button is clicked in a dialog, for instance. We will use the keywords “Public” and “Sub” - to denote a publically callable sub-routine – and then the name of the Sub. At the end of the Sub we use the keywords “End Sub”. It’s also possible to add a Sub using pull-down menus in VB.NET code windows, instead of typing the code in. The Subs we can add using the VB.NET dropdowns are special default Subs associated with the class for which we’re entering code – in our case a Form class. One of these is the Sub New. We want to implement this Sub because it is called automatically when our Form is created (i.e. when our application is started). During this Sub, we will connect to an existing Inventor session or start a new one. A Sub must be contained within a Class: let’s go ahead and add the “New” Sub to our Form1 class. We’ll first make the code window associated with Form1 active – by rightclicking on Form1.vb in the Solution Explorer and select “View Code” – and then select Form1 in the dropdown on the left and then select “New” in the dropdown on the right. 17 Autodesk® Inventor® API Lab for Power Users Adding the Sub New The “Sub New” procedure should now have been added to the Form1 class: it’s this Sub that gets called when the Windows Form Application is started, as the application’s default behavior is to create an object of type Form1 and display it (this can be controlled via our project’s properties). Our application needs a session of Inventor to work: while we could choose to connect to one at different times (each time it’s needed, for instance), the approach we’re going to use in this application is to connect to an Inventor session when our application is started. Public Sub New() ' This call is required by the designer. InitializeComponent() ' Add any initialization after the InitializeComponent() call. End Sub 4. Connect to or start an Inventor session in Sub New. Type this code after the InitializeComponent() call in this Sub. The call to InitializeComponent() was added automatically in the default implementation of Sub New. InitializeComponent helps set up the behavior of a Form. If you’re interested in seeing the code it contains, right-click on the name of the Sub and select “Go To Definition”. Just be sure not to change the code in the editor window: InitializeComponent’s code will be modified automatically when we add controls to our form, but you should not have to edit it manually. ' Add any initialization after the InitializeComponent() call. Try _invApp = Marshal.GetActiveObject("Inventor.Application") Catch ex As Exception Try Dim invAppType As Type = _ GetTypeFromProgID("Inventor.Application") _invApp = CreateInstance(invAppType) _invApp.Visible = True 'Note: if you shut down the Inventor session that was started 'this(way) there is still an Inventor.exe running. We will use 'this Boolean to test whether or not the Inventor App will 'need to be shut down. _started = True 18 Autodesk® Inventor® API Lab for Power Users Catch ex2 As Exception MsgBox(ex2.ToString()) MsgBox("Unable to get or start Inventor") End Try End Try Notice how the equals sign is used to assign an object to the _invApp variable. _invApp = Marshal.GetActiveObject("Inventor.Application") After this line has run the variable _invApp contains an object of type Inventor.Application that we can use. Also notice that the Dim statements we added for the Inventor Application and the Boolean are outside of the Sub itself, making them global. This means that we can access these variables from any Sub in the class. We do this because we want to use the variables in the FormClosed Sub event that we will add in the next step. The GetActiveObject function helps us find a running session of Inventor. The string of characters (known as a ProgID) that we pass into the function, “Inventor.Application”, tells GetActiveObject the type of object we’re trying to find. We surround the call to GetActiveObject with a Try-Catch block, which means our code stays on the lookout for exceptions (a way or returning errors from .NET code). We do this as we know GetActiveObject will “throw” an exception if it doesn’t find a running Inventor session. If this happens our Catch statement gets executed, which gives us the chance to try to launch a new Inventor session. 5. Respond to the FormClosed event. An event is something we get notified about and can respond to in our application’s code. During our FormClosed event we’re told that Form1 is closing, and that we should clean up after it. If we launched Inventor in our Sub New, above, we ought to close the session we started. It’s for this reason we set our Boolean variable – _started – to True if we had to start a new Inventor session: we can check this variable as the form is closing, and close the Inventor session from there. To add the FormClosed event Sub select “(Form1 Events)” in the dropdown on the left and then select “FormClosed” in the dropdown on the right. 19 Autodesk® Inventor® API Lab for Power Users Adding the Event FormClosed The “Form1_FormClosed” event handler is added to the Form1 class. This Sub will be run when the Form is closed. Private Sub Form1_FormClosed(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed End Sub 20 Autodesk® Inventor® API Lab for Power Users 6. Type this code inside the Form1_FormClosed Sub. ' This will shut down the Inventor session started when the form was started If _started Then _invApp.Quit() End If _invApp = Nothing Here we Quit the Inventor session if _started is set to True (i.e. it was our code that started the session, as one was not already running). 7. Run the Windows Form Application by hitting F5. If you do not have a session of Inventor running you should see one start. End of Lesson 3 Here is the complete code from the Code Window for Form1.vb at the end of Lesson 3 (modified slightly using the underscore chatacter (‘_’) to split long lines for readability). Imports System Imports System.Type Imports System.Activator Imports System.Runtime.InteropServices Imports Inventor Public Class Form1 Dim _invApp As Inventor.Application Dim _started As Boolean = False Public Sub New() ' This call is required by the designer. InitializeComponent() ' Add any initialization after the InitializeComponent() call. Try _invApp = Marshal.GetActiveObject("Inventor.Application") Catch ex As Exception Try Dim invAppType As Type = _ GetTypeFromProgID("Inventor.Application") _invApp = CreateInstance(invAppType) _invApp.Visible = True 'Note: if you shut down the Inventor session that was started 'this(way) there is still an Inventor.exe running. We will use 21 Autodesk® Inventor® API Lab for Power Users 'this Boolean to test whether or not the Inventor App will 'need to be shut down. _started = True Catch ex2 As Exception MsgBox(ex2.ToString()) MsgBox("Unable to get or start Inventor") End Try End Try End Sub Private Sub Form1_FormClosed( _ ByVal sender As Object, ByVal e As FormClosedEventArgs) _ Handles Me.FormClosed If _started Then _invApp.Quit() End If _invApp = Nothing End Sub End Class End of LAB 3 Link to LAB 3 Discussion LAB 4: Add a Button and logic flow using “If Then” and “For Next” In previous lessons we learned how to create a basic Windows Forms application and connect it to a session of Inventor. In this lesson we will add a button to the form and have it hide selected components, using the code we entered in lesson 1. We will now spend some time to understand how the code functions. We talked about how a program is a sequence of instructions telling the computer how to do something. Many programs will also need some type of User Interface, for the end user to interact with your new functionality. The Visual Basic Express toolbox makes it very easy to add user interface elements, such as buttons, to enable this interaction. 1. In the Solution Explorer right click on Form1.vb and select View Designer (or use Shift+F7). 22 Autodesk® Inventor® API Lab for Power Users The Designer is where you can create the user Interface. In this example we are just going to add one button. 2. From the View menu select Other Windows and then select Toolbox (or use Ctrl+Alt+X). 23 Autodesk® Inventor® API Lab for Power Users 3. On the Toolbox, select Button and draw a button on the Form by clicking and dragging to specify the position and size of the button. 24 Autodesk® Inventor® API Lab for Power Users 4. We now need to assign behavior to our button: code that gets executed when someone clicks on it. To do this we need to get to the code window for the form and get to the code that will run when the button is clicked. To do this, right click on Form1.vb and select View Code (or use F7). In the code window select the drop down on the left and select Button1. 25 Autodesk® Inventor® API Lab for Power Users In the right dropdown, select the Click event (the icon used for events is a lightning bolt). 26 Autodesk® Inventor® API Lab for Power Users This results in the Sub Button1_Click event hander being added to the code window for the Form and assigned to the button. Private Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click End Sub 5. Add the following If statement to the Sub Button1_Click. If _invApp.Documents.Count = 0 Then MsgBox("Need to open an Assembly document") Return End If 6. Add the following to the Sub Button1_Click. (After the “If Then” statement in step 5). If _invApp.ActiveDocument.DocumentType <> DocumentTypeEnum.kAssemblyDocumentObject Then 27 Autodesk® Inventor® API Lab for Power Users MsgBox("Need to have an Assembly document active") Return End If These two steps make sure a) at least one document is open and b) the active document is an assembly (our code is going to hide assembly components, after all). If a different type of document is active then we ask the user to make an assembly active and then Return from our Sub without executing any more code. 7. Add the following to the Sub Button1_Click after the “If Then” statement in step 6. Dim asmDoc As AssemblyDocument asmDoc = _invApp.ActiveDocument If asmDoc.SelectSet.Count = 0 Then MsgBox("Need to select a Part or Sub Assembly") Return End If 8. Add the following to the Sub Button1_Click after the “If Then” statement in step 7. Dim selSet As SelectSet selSet = asmDoc.SelectSet Try Dim compOcc As ComponentOccurrence Dim obj As Object For Each obj In selSet compOcc = obj Debug.Print(compOcc.Name) compOcc.Visible = False Next Catch ex As Exception MsgBox("Is the selected item a Component?") MsgBox(ex.ToString()) Return End Try This completes the code for this lesson. The code now checks to see if an assembly is active and some entities are selected. It then prints out the name of the component (an occurrence) and makes it invisible. If the selected entity is not a component then an error will occur and the catch block will handle it. 28 Autodesk® Inventor® API Lab for Power Users Running the Code 1. You should now be able to run the project (by selecting Start Debugging from the Debug menu), which leads to the application starting and the form being displayed. Once you’ve done this, switch to the Inventor session and select some components in an assembly, which will take the focus away from our application. Bring the application back in front of the Inventor session and click the button to run our code. The selected components in Inventor should now be hidden and – in the browser - display for the components will be unchecked. End of LAB 4 Link to LAB 4 Discussion LAB 5: Add attributes to selected components In Lesson 4 we created a button on the form to hide selected components in the active Inventor assembly. Along the way we learned how to use Inventor objects along with how to control code execution using “If Then” and “For Next” statements. Instead of directly hiding the selected components, we’ll now modify the code to “tag” our components for later hiding or display. Attaching attributes provided by the Inventor API will allow us to identify and perform different operations on our tagged components. 1. Open the project from the previous lesson and open the Code Window for Form1. Or if the plug-in is already running stop it by selecting Stop Debugging on the Debug Menu in VB Express. You can also close the application window of the plug-in by clicking the x in the upper right corner. If the Designer is active, right click on Form1.vb in the Solution Explorer and select “View Code”. In the “For Next” loop within the Button1_Click event we are currently hiding each of the selected components by setting its Visible property to False. Comment out the line of code that sets the Visible property by inserting an apostrophe/single quote (') at the beginning of the line. For Each obj In selSet compOcc = obj Debug.Print(compOcc.Name) 'compOcc.Visible = False Next 2. There are three Inventor API classes that are used to add an Attribute to entities, one of which is the AttributeSets collection. Add a line of code which uses the Dim statement to 29 Autodesk® Inventor® API Lab for Power Users declare a variable named “attbSets” of type AttributSets. We’ll assign this variable directly to contain the value of our component occurrence’s AttributeSets property. Dim attbSets As AttributeSets = compOcc.AttributeSets 3. We only want to add our attribute once to each tagged component. We can use the NameIsUsed Method of the AttributeSets collection to check whether an AttributeSet of a particular name has already been created. Add the below code below just after the code we added in step 2. ' Add the attributes to the ComponentOccurrence If Not attbSets.NameIsUsed("myPartGroup") Then End If 4. We’ll now create and assign a variable named “attbSet” of type AttributeSet, once again using the combination of the Dim statement and the equals sign. The string “myPartGroup” specifies the name of the AttributeSet in the AttributeSets collection . Add this in the body of the “If Then” statement we created in step 3. Dim attbSet As AttributeSet = attbSets.Add("myPartGroup") 5. The third class we use to add the attribute is the Attribute itself. We use the Add method on our AttributeSet to create an Attribute object and add it to the set. We can assign the results of the Add method directly to a variable named “attb” of type Inventor.Attribute. You may remember we have to specify the Inventor namespace explicitly to prevent a name clash with another Attribute class. Dim attb As Inventor.Attribute = _ attbSet.Add( _ "PartGroup1", ValueTypeEnum.kStringType, "Group1") 6. Build the project using the Build item on the Debug menu. This will let you know if the project contains any errors: it’s good practice to build regularly both to fix issues as they get introduced and to save your work. Use the Watch window Let’s now take a look at how we can debug our code using Visual Basic Express. 1. Place a break point on the line that adds the attribute. We can do this by putting the cursor on the line and then using the F9 key. Another way is to click on the bar to the left of the code window: 30 Autodesk® Inventor® API Lab for Power Users 2. Run the project by selecting “Start Debugging” from the Debug menu. (Or use F5) 31 Autodesk® Inventor® API Lab for Power Users 3. In the Inventor session select a couple of components and then click the button on our form. Visual Basic Express – and the code window showing this code – should become active when the line that adds the attribute is about to be executed. The execution will stop here, waiting for you to perform some action to advance it. 32 Autodesk® Inventor® API Lab for Power Users 4. When execution has been paused via a breakpoint, we can examine the values of the variables in our code using the Watch window. We do this by right clicking on the variable name and selecting “Add Watch”. 33 Autodesk® Inventor® API Lab for Power Users We can see in the Watch window that the values for the AttributeSet’s properties include the name we assigned it during the previous line of code. 34 Autodesk® Inventor® API Lab for Power Users To make the code execution proceed, use the Debug pulldown menu and select Continue (or Hit F5). 35 Autodesk® Inventor® API Lab for Power Users 5. Stop debugging by closing the form or select “Stop Debugging” on the Debug menu. Add another button to the form and use the AttributeManager to get the components 1. Let’s now add another button to the form that will make use of the attributes we’ve used to tag our components. Display the Designer by right-clicking on Form1.vb and select “View Designer” (or use Shift+F7). Display the Toolbox using the View Menu -> Other Windows then ToolBox (or use Ctrl+Alt+X). Click and drag to place the button. 36 Autodesk® Inventor® API Lab for Power Users 2. By default the text displayed on the buttons are “Button1”, “Button2” and so on. In most cases we will want to have more descriptive names for them. We can do this by using the “Properties” Window. We can display the Properties window by right clicking on the button and selecting “Properties”. Change the Text property of the button to “Hide or Show Group”: this changes the text shown inside the button but does not change the internal name of the button. 37 Autodesk® Inventor® API Lab for Power Users 3. Using the same approach, change the text on Button1 to “Add to group”. 4. Make the code window active. Double-click on the button in the Designer to switch to the code window and create the click event handler. In Lesson 4 we used drop downs from the code window to add the Sub Button1_Click event handler. 5. When the user clicks on Button2 we want to get the AttributeManager from the active document after making sure the active document is an assembly. We already have some code that does this in the Button1_Click Sub. Copy this code into the Button2_Click Sub. If _invApp.Documents.Count = 0 Then MsgBox("Need to open an Assembly document") Return End If If _invApp.ActiveDocument.DocumentType <> _ DocumentTypeEnum.kAssemblyDocumentObject Then MsgBox("Need to have an Assembly document active") 38 Autodesk® Inventor® API Lab for Power Users Return End If Dim asmDoc As AssemblyDocument asmDoc = _invApp.ActiveDocument 6. We will now use the AttributeManager to find the various objects tagged with our attribute. To do so add these lines of code after the code we added in step 5. Try Dim attbMan As AttributeManager = asmDoc.AttributeManager Dim objsCol As ObjectCollection objsCol = _ attbMan.FindObjects("myPartGroup", "PartGroup1", "Group1") Catch ex As Exception MsgBox("Problem hiding component") End Try 7. The components that were tagged by the code behind Button1 will be contained in the ObjectCollection after the call to FindObjects. We’ll now go through each of the items in this collection and “toggle” their visibility (setting the visible ones to be invisible and the invisible ones to be visible). Do so by adding the below code between the call to FindObjects and the Catch statement added in step 6. Dim compOcc As ComponentOccurrence For Each obj As Object In objsCol compOcc = obj ' Toggle the visibility of the Component Occurrence compOcc.Visible = Not compOcc.Visible Next 8. Build the project and fix any errors that get reported. You can test (and, if necessary, debug) the project by using the Debug pulldown menu and selecting Start Debugging (or use F5). Select a couple of components and click Button1 to add the attribute. When you click Button2 the display of the components should toggle between visible and invisible. End of LAB 5 Link to LAB 5 Discussion 39 Autodesk® Inventor® API Lab for Power Users LAB 6: Add More UI and call Subs with an argument In lesson 5 we used attributes to tag Inventor components. We then used those attributes to select the components and hide or display them. In this lesson, instead of toggling the display of the components, we will add an additional button to provide the user with the direct capability to either show or hide them. We will also add two more buttons, one to remove our attribute tag from the selected components and one to remove the attribute from all components. We will make our code easier to write and to maintain by creating procedures that are called when the button is clicked. We will pass a Boolean argument to these procedures, allowing the same procedure to be used in slightly different contexts. 1. Open the project from lesson 5 and display the Designer for Form1. If the code window is active right-click on Form1.vb in the Solution Explorer and select View Designer. Add three buttons so that the form looks like the screenshot below. Change the text for the button (Button2) we added in lesson 4 to Hide group. We will use another button to show the components in the group. In previous lessons we covered how to add buttons to the form and change the text for the buttons. (Show the toolbox, drag a button to place it on the form.) Make the text on the three new buttons: Show group Remove from group Remove all from group 40 Autodesk® Inventor® API Lab for Power Users 2. Type the following in the code window for Form1, before the End Class keyword and after the last End Sub. Public Sub HideOrShowGroup(ByVal hide As Boolean) End Sub 3. Cut the code from the Button2_Click Sub and paste it into the Sub we created in step 2. Here is the code we need to move into the HideOrShowGroup Sub: If _invApp.Documents.Count = 0 Then MsgBox("Need to open an Assembly document") Return End If 41 Autodesk® Inventor® API Lab for Power Users If _invApp.ActiveDocument.DocumentType <> _ DocumentTypeEnum.kAssemblyDocumentObject Then MsgBox("Need to have an Assembly document active") Return End If Dim asmDoc As AssemblyDocument asmDoc = _invApp.ActiveDocument Try Dim attbMan As AttributeManager = asmDoc.AttributeManager Dim objsCol As ObjectCollection objsCol = _ attbMan.FindObjects("myPartGroup", "PartGroup1", "Group1") Dim compOcc As ComponentOccurrence For Each obj As Object In objsCol compOcc = obj ' Toggle the visibility of the Component Occurrence compOcc.Visible = Not compOcc.Visible Next Catch ex As Exception MsgBox("Problem hiding component") End Try 4. Change the contents of the For Each loop so that it only makes the Visible property of the component equal to the Boolean that is passed in. (hide). For Each obj As Object In objsCol compOcc = obj compOcc.Visible = hide Next 5. Add the following to the now-empty Button2_Click Sub. When this button is clicked it will run the HideOrShowGroup Sub passing in False. HideOrShowGroup(False) 6. Add this to the Button3_Click Sub. (The Button with the text “Show group.) When this button is clicked it will run the HideOrShowGroup Sub passing in True. HideOrShowGroup(True) 42 Autodesk® Inventor® API Lab for Power Users 7. Type the following into the code window for Form1, before the End Class keyword and after the last End Sub. This will create another Sub that we can call, passing in a Boolean variable. Public Sub AddOrRemoveFromGroup(ByVal add As Boolean) End Sub 8. Cut and paste this code from the Button1_Click Sub into the AddOrRemoveFromGroup Sub that we added in Step 7. If _invApp.Documents.Count = 0 Then MsgBox("Need to open an Assembly document") Return End If If _invApp.ActiveDocument.DocumentType <> _ DocumentTypeEnum.kAssemblyDocumentObject Then MsgBox("Need to have an Assembly document active") Return End If Dim asmDoc As AssemblyDocument asmDoc = _invApp.ActiveDocument If asmDoc.SelectSet.Count = 0 Then MsgBox("Need to select a Part or Sub Assembly") Return End If Dim selSet As SelectSet selSet = asmDoc.SelectSet Try Dim compOcc As ComponentOccurrence Dim obj As Object For Each obj In selSet compOcc = obj Debug.Print(compOcc.Name) 'compOcc.Visible = False Dim attbSets As AttributeSets = compOcc.AttributeSets ' Add the attributes to the ComponentOccurrence If Not attbSets.NameIsUsed("myPartGroup") Then 43 Autodesk® Inventor® API Lab for Power Users Dim attbSet As AttributeSet = attbSets.Add("myPartGroup") Dim attb As Inventor.Attribute = _ attbSet.Add( _ "PartGroup1", ValueTypeEnum.kStringType, "Group1") End If Next Catch ex As Exception MsgBox("Is the selected item a Component?") MsgBox(ex.ToString()) Return End Try 9. Add an If Then Else statement and test the add Boolean variable that is passed into the Sub. Add the If Then statement right after the code where the attbSets is created. If add Then Put the Else statement right after the End If that is used for the If statement that uses the NameIsUsed property. “If Not attbSets.NameIsUsed…” and right before the Next keyword. Add an End If after the Else keyword. ' Add the attributes to the ComponentOccurrence If Not attbSets.NameIsUsed("myPartGroup") Then Dim attbSet As AttributeSet = attbSets.Add("myPartGroup") Dim attb As Inventor.Attribute = _ attbSet.Add( _ "PartGroup1", ValueTypeEnum.kStringType, "Group1") End If Else End If Next 10. Add a nested If Then statement that uses the NameIsUsed property to check if the name of the attribute myPartGroup is used. (Put it between the Else statement and the 44 Autodesk® Inventor® API Lab for Power Users End If added in the previous step.). If it has been used, get the AttributeSet by passing its name (“myPartGroup”) to the Item method of the attbSets object, and then call Delete on it to delete the AttributeSet from the component. ' Delete the attributes from the ComponentOccurrence If attbSets.NameIsUsed("myPartGroup") Then attbSets.Item("myPartGroup").Delete() End If 11. Call the AddOrRemoveFromGroup Sub from the now-empty Button1_Click passing True as the argument. Private Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click AddOrRemoveFromGroup(True) End Sub 12. Call the AddOrRemoveFromGroup Sub from the Button4_Click Sub, passing False as the argument. (The button with the text “Remove selected components from group”.) We can double-click on the button in the Designer view to display the code window and add the Sub Button4_Click. Private Sub Button4_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button4.Click AddOrRemoveFromGroup(False) End Sub 13. We have one more button that we need to complete. This is the Button with the text “Remove all components from group”. Double click on this button (Button5) in the Designer to open the code window and create the Button5_Click Sub. Copy and paste code from the other functions to do what we need. Here is the completed Button5_Click Sub. Notice how the code we added for our other button is being reused. Private Sub Button5_Click( _ ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles Button5.Click If _invApp.Documents.Count = 0 Then MsgBox("Need to open an Assembly document") Return End If If _invApp.ActiveDocument.DocumentType <> _ DocumentTypeEnum.kAssemblyDocumentObject Then 45 Autodesk® Inventor® API Lab for Power Users MsgBox("Need to have an Assembly document active") Return End If Dim asmDoc As AssemblyDocument = _invApp.ActiveDocument ' Get the attribute manager for the document Dim attbMan As AttributeManager = asmDoc.AttributeManager ' Find the objects with the attributes Dim objCol As ObjectCollection objCol = _ attbMan.FindObjects("myPartGroup", "PartGroup1", "Group1") Dim attbSets As AttributeSets ' Delete the attribute from the ComponentOccurrence For Each obj As Object In objCol attbSets = obj.AttributeSets attbSets.Item("myPartGroup").Delete() ' Make the ComponentOccurrence visible obj.Visible = True Next End Sub End of LAB 6 Link to LAB 6 Discussion Congratulations! You have now completed this training More Discussion on the Labs: Return to LAB 1 LAB 1 Let’s take a step back and review some basics concepts about programming and the benefits it can bring to your day-to-day work. Introduction to Programming More than half of the code that you entered into the project which hides the selected components is doing error checking. The code that actually does the work can be narrowed down to these lines of code: Dim asmDoc As AssemblyDocument asmDoc = _invApp.ActiveDocument 46 Autodesk® Inventor® API Lab for Power Users Dim selSet As SelectSet selSet = asmDoc.SelectSet Dim compOcc As ComponentOccurrence Dim obj As Object For Each obj In selSet compOcc = obj Debug.Print(compOcc.Name) compOcc.Visible = False Next As we can see a small amount of code can go a long way to simplify working with Inventor. Software programming allows you to capture the logic of a particular task once and then reap the benefits over and over again, every time you (or others) want to perform that task. What is Programming? A simple answer to this question is: Computer programming is the process of creating a sequence of instructions to tell the computer to do something. We will look at our program as a sequence of instructions. During the course of the upcoming lessons, we will look at the various lines and blocks of code and look at them all in the context of being instructions for a computer. If we were to explain what computers are to a young child, we might say: a computer is essentially a tool which follows instructions we provide. Programming is one way of giving instructions to the computer. Internally, a computer sees these instructions encoded as a series of numbers (also called machine code). The human-readable instructions we saw at the beginning of the lesson are called source code and the computer converts these instructions to machine code which it can then execute. A sequence of such instructions (or code), written to perform a specific task, is called a program and a collection of such programs and related data is called a software. Autodesk Inventor is one such software product. Source code can be written in different languages, just as we humans use different languages to communicate between ourselves. The development language we will be using in this guide is called Visual Basic.NET (VB.NET). What is an API? API is the acronym for Application Programming Interface: the way a software programmer can communicate with a software product. For instance, the Inventor API is the way programmers can work with Inventor, and establishes what functionality a software programmer can use in Inventor. An API such as Inventor’s allows you to write instructions for Inventor to execute one after the other. 47 Autodesk® Inventor® API Lab for Power Users Putting this slightly differently: commercial software companies, such as Autodesk, often distribute a set of libraries that you can use in your own program to interact with a particular software product, such as Autodesk Inventor, and extend its functionality. This set of libraries is known as the software product’s API. In other words, what functionality has been made available for you to manipulate through programming. The type of program you write to interact with a software product and extend its functionality will depend upon how the API has been designed and what has been exposed (through APIs) for you to work with. What is a Plug-in? A software plug-in is a type of program module (or file) that adds functionality to a software product, usually in the form of a command automating a task or some customization of the product’s behavior. When we talk about a plug-in for Inventor – and you will also hear the term AddIn used for this product – we mean a module containing code that makes use of the Inventor API and connects to Inventor when your plug-in runs that then changes the behavior of Inventor under certain conditions, such as when a particular command is executed by you. An Inventor AddIn would also be considered a plug-in. An AddIn is a special kind of Plug-in that automatically loads when Inventor is started, has high performance and appears to the user to be part of Inventor. LAB 2 Discussion Return to LAB 2 48 Autodesk® Inventor® API Lab for Power Users A quick recap: in this lesson we took a brief look at what happens when we build a project, as well as some background information on VB.NET and Visual Basic Express. We then spent some time creating a brand new Windows Forms Application project in Visual Basic Express, adding the required project reference for it to be able to use the Inventor API. In the previous lesson, we saw how you can increase productivity in Inventor by implementing a plugin built from a small amount of VB.NET code. We then defined some core concepts/terms: programming, API and plugin. You will probably have heard the terms COM and .NET with reference to programming with the Inventor API. Both COM and .NET are technologies that enable communication between software: we won’t go into details about either, at this stage, but if you are interested in learning more, you will find information in the Additional Topics section here. We will now look more closely at what happened when we executed the code in the previous lesson. What does it mean to “build” code? The code that we typed in to Visual Basic Express in the previous lesson was a set of humanreadable instructions (source code) that needed to be converted into code that could be understood and executed by the computer. The “build” we performed did just that: it packaged up the resulting executable code inside a standard Windows EXE file. It’s also possible to create a DLL that can be loaded into Inventor, but that’s a more advanced topic not covered by this guide. The Additional Topics section also covers this process is a bit more detail. The following screenshot shows the output EXE along with the associated program debug database (which provides additional information when debugging the EXE), once we have built the solution in Lesson 1 using Visual Basic Express. The path to which the EXE is compiled is specified in the Visual Basic Express project settings and is, by default, set to the bin sub-folder of the Visual Basic Express project folder. 49 Autodesk® Inventor® API Lab for Power Users Lesson_2_1_Windows Explorer showing Exe.bmp Choosing a programming language and development tool Just as humans use different languages to communicate, we have various language options available to us when creating an Inventor plugin: for the purposes of this guide we have chosen Visual Basic .NET (VB.NET), a strong general-purpose programming language that is often favored by beginners. There are a number of tools available for developing VB.NET code. They range from open source tools such as SharpDevelop to Microsoft’s flagship, professional development environment, Visual Studio. In our case we will be using Visual Basic Express, a free version of Visual Studio focused on building VB.NET applications. Visual Basic Express is an Integrated Development Environment (IDE) because it is composed of various tools, menus and toolbars which ease the creation and management of your code. The following screenshot shows the initial screen of Visual Basic 2010 Express with the default settings. 50 Autodesk® Inventor® API Lab for Power Users Lesson_2_2_VB start page.bmp The project system in Visual Basic Express comprises Solution and Project files as well as Project Items, the individual files belonging to projects. A solution is a container for one or more projects. Each project can in turn be considered a container for project items – such as source files, icons, etc. – most of which get compiled into the resultant executable file (EXE or DLL). Visual Basic Express provides a Solution Explorer that organizes and displays the contents of the loaded solution in a tree-view format: 51 Autodesk® Inventor® API Lab for Power Users Lesson_2_3_Solution Explorer window.bmp The Visual Basic Express interface also contains a text editor and interface designers. These are displayed in the main window depending on the type of file being edited. The text editor is where you will enter the Visual Basic code for your Inventor plugin. This editor provides advanced features such as IntelliSense and collapsible code sections along with the more classic text-editing features such as bookmarks and the display of line numbers. IntelliSense is an extremely valuable feature of the Visual Studio family that greatly improves programmer productivity: it automatically provides suggestions for the code being written based on the objects available and the letters that are being typed. 52 Autodesk® Inventor® API Lab for Power Users Lesson_2_4_Intellisense.bmp IntelliSense showing the methods and properties for a ComponentOccurrence Clearly one of the key features of Visual Basic Express is its ability to build VB.NET code into an executable file. During the build process, the language compiler performs various checks and analyses on the code. One such check is to ensure the code conforms to the syntactical rules of the Visual Basic language. The compiler also performs various other checks, such as whether a variable has been appropriately defined or not. Detected errors are reported via the Error List Window, typically found at the bottom of the main window. The Error List can be displayed by selecting Error List from the View Menu > Other Windows. 53 Autodesk® Inventor® API Lab for Power Users The Visual in Visual Basic Express One of the main strengths of Visual Basic Express is the set of tools it has for creating a User Interface. When we create a new project we can select a “Windows Forms Application”. When this template is used the main window for the application is automatically created. This window is called a form and we can place user interface elements (called controls), such as a command buttons, on it. We add an element by simply selecting it in the Toolbox and then clicking and dragging it onto the Form. The Toolbox can be displayed using the View menu > Other Windows > Toolbox. The code that is needed, behind the scenes, to make these elements work correctly is added automatically to the project. Because of this, it greatly simplifies the code we need to add ourselves to get our application up and running. 54 Autodesk® Inventor® API Lab for Power Users Additional Topics Visual Basic Express vs. Visual Studio Professional In this guide, as you will have noticed, we are using Visual Basic Express. This is a free version of Visual Studio, a great tool to start writing and managing Visual Basic code for our Inventor plugin without investing in a more expensive tool. Microsoft has targeted the Express editions of Visual Studio at students, hobbyists and other part-time programmers. While it provides most of the features of Visual Studio Professional, such as IntelliSense, it does have certain limitations. For instance, it contains fewer project templates and has limited options for debugging. For users who are serious about plugin development beyond this introductory project, we recommend investing in one of the more fully-featured members of the Visual Studio product family. What is COM? “Microsoft COM (Component Object Model) technology in the Microsoft Windows-family of Operating Systems enables software components to communicate. COM is used by developers to create re-usable software components, link components together to build applications, and take advantage of Windows services. COM objects can be created with a variety of programming languages. Object-oriented languages, such as C++, provide programming mechanisms that simplify the implementation of COM objects.” Source: MSDN Library What is .NET? “The .NET Framework is an integral Windows component that supports building and running the next generation of applications and XML Web services.” Source: MSDN Library The .NET Framework is a software framework that sits on top of the Microsoft® Windows® operating system* and provides the underlying platform, libraries and services for all .NET applications. The services generally include memory management, garbage collection, common type system, class libraries, etc. * Subsets of .NET are also available on other operating systems, whether via the open source Mono project or via Microsoft® Silverlight®, but these are not topics for this guide: we will focus solely on the use of .NET in the context of Microsoft Windows. What does the .NET Framework contain? The framework contains two main components: 1. Common Language Runtime (CLR) – This is the agent (or execution engine) in the .NET Framework responsible for managing the execution of code. Which is why code written to target this runtime is also known as managed code. All managed code runs under the supervision of the CLR, but what does this mean? The CLR manages code by 55 Autodesk® Inventor® API Lab for Power Users providing core services such as memory management (which includes automatically releasing the computer’s memory for reuse on other tasks when it is no longer needed), error (or exception) handling, managing the use of multiple threads of execution and ensuring rules around the use of different types of object are adhered to. The CLR is really the foundation of the .NET Framework. 2. .NET Framework Class Library – As the name suggests, this is a library or collection of object types that can be used from your own code when developing .NET applications. These .NET applications can target Windows (whether command-prompt based or with a graphical user interface), the web or mobile devices. This library is available to all languages using the .NET Framework. As mentioned above, the CLR improves code robustness by making sure the executing code conforms to a common type system (CTS). The CTS ensures that all .NET (or managed) code – irrespective of the language – uses a similar set of object types and can work together in the same environment. It is this feature that makes it possible for you to write applications in the development language of your choice and yet make use of components/code written by programmers using other .NET languages. Accessing COM from .NET Inventor’s API is COM-based: internally the Inventor product is implemented using C++ and its API is exposed through COM, enabling the use of various development environments and programming languages. Thankfully, the interoperability features of .NET make it really easy to work with COM APIs: any complexity of passing data between .NET and Inventor is hidden away, so we can focus on using the API rather than dealing with communication issues. Building Executables When we built our code into an EXE, it was compiled into Common Intermediate Language (CIL –also known as MSIL) code using the language-specific compiler. CIL is CPU-independent set of instruction that can be executed by the CLR on Windows operating systems. CIL is typically portable across 32- and 64-bit systems and even – to some extent – to non-Windows operating systems. The CIL code generated from our VB source code was then packaged into a .NET assembly. Such an assembly is a library of CIL code stored in Portable Executable (PE) format (which contains both the CIL its associated metadata). Assemblies can either be process assemblies (EXEs) or library assemblies (DLLs). During the course of this guide, we focus on developing a particular type of Inventor plugin: a process assembly (EXE) which communicates with Inventor. Because of the overhead associated with developing them, we will not spend time looking at Inventor AddIns, which are usually library assemblies (DLLs) that get loaded into and executed within Inventor’s memory space. One reason that implementing an EXE to work with Inventor is simpler that developing an AddIn is related to its user interface: Executables do not need to integrate seamlessly with the Inventor user interface by adding ribbon buttons (for instance). 56 Autodesk® Inventor® API Lab for Power Users Running Executables During execution of a .NET assembly, CIL (residing in the assembly) is passed through the CLR’s just-in-time (JIT) compiler to generate native (or machine) code. JIT compilation of the CIL to native code occurs when the application is executed. As not all of the code is required during execution, the JIT compiler only converts the CIL when it is needed, thus saving time and memory. It also stores any generated code in memory, making it available for subsequent use without the need to recompile. In the last step of this process, the native code gets executed by the computer’s processor. If you would like more details on the process of building .NET applications, please refer to the MSDN Library. LAB 3 Discussion Link to return to LAB 3 In this lesson, we will take a closer look at the Inventor API. In Lesson 1, we typed code (or instructions) into a code window associated with a Form and saw how it changed the visibility of components in an assembly. In Lesson 2 we created a Windows Form application and referenced the Inventor API from it. In this lesson we will continue working on the project we created in Lesson 2, extending it to use the Inventor API to connect to or start a session of Inventor while taking a close look at how the code actually works. Object Oriented Programming In the previous lesson, we talked about how a program is a sequence of instructions which tells the computer how to perform one or more tasks. Simple programs tend to consist of a sequence or list of instructions operating on variables – data representing what’s happening in the program as it executes. But, with increasing complexity, such linear sequences of instructions (an approach also often referred to as procedural programming) can be difficult to manage and ends up not being well-suited for the task for which the code was written. 57 Autodesk® Inventor® API Lab for Power Users To help address the complexity problem, Object-Oriented Programming (OOP) was developed to allow source code to be structured differently, enabling componentization and reuse. OOP encourages us to develop discrete, reusable units of programming logic which are aimed at modeling objects from the problem domain and the relationships between them. A good example of this is a map. A map of a city can be considered as a model or a simplified representation of the city itself, providing a useful abstraction of the information we need to get around it. The key to good object-oriented design is to model the elements of the problem domain as individual objects displaying the relevant behavior of the originals and the relationships between them. A class can be thought of as a type which can be used to describe a representation of a thing. A class can be considered the template that describes the details of an object and is used to create individual objects. In this way classes can help define the common characteristics of objects of their type: the objects’ attributes (properties) and behaviors (methods). In the context of VB.NET, methods are either Functions or Subs (more on this later). An object is an instance of a class. These are the building blocks of Object-Oriented Programming and can be thought of as variables - often quite complex ones – which encapsulate both data and behavior. To provide an example, the relationship between a class and an object has parallels with Inventor iFeatures. An iFeature could be considered as a class. When we create an instance of this iFeature, that instance can be thought of as an object. The iFeature is the blueprint or template: each instance of the iFeature has the set of parameters defined in the iFeature but has different parameter values. They might have a different length or width, for instance. Use the Object Model Chart to discover the Inventor API classes Looking at the Inventor API object model, you will see that many of the classes in the Inventor API are organized to follow the main document types in Inventor – Parts, Assemblies and Drawings. Other parts of the API are used to create user interfaces and other objects not related to a specific document type. You can download a PDF containing a diagram of the Inventor API Object Model from the Inventor Developer Center - http://www.autodesk.com/developinventor (you will find other resources available for the Inventor API here, as well). The Object Model chart allows us to see graphically how the classes in the Inventor API relate to one another. The top level class is the Application. One of the classes accessible from the Application class is the Documents class. Under Documents we find PartDocument, AssemblyDocument and DrawingDocument. Here is a screenshot of part of the Object Model chart that shows the relationship between the Application, Documents and DrawingDocument classes. Notice it is a hierarchy showing how classes are referenced by – and accessible from – other classes. 58 Autodesk® Inventor® API Lab for Power Users Lesson_3_1_Object_Model_Chart.bmp ObjectModel screenshot of DrawingDocument object We could think of the Object Model chart as we would a business organization chart. At the top we have the president and under the president we may have several vice presidents. Then each vice president would have directors and they would have employees under them and so on. So if this business was like the Inventor API, you would need to go through the president (Application), then the vice president (Documents) and down the hierarchy to get to the employee (DrawingDocument) to get something done related to this part of the business (such as saving the DrawingDocument). Another way to look at the Object Model is to see it as a type of containment such as a building. The Application object would be the entire building and it contains floors. One floor would be Documents collection and that floor contains rooms such as various Document objects. You would need to go the building front door (Application) then go to the right floor (Documents) and then into the Document room to do something with the document. In the Object Model Chart we see that some of the boxes around the classes are rectangular and some have rounded corners. Boxes with rounded corners are used to denote collections of 59 Autodesk® Inventor® API Lab for Power Users objects – notice their names typically end in an “s”, as they are intended to contain multiple objects of a particular class. The Documents collection, for example, will contain one or more Document objects. A collection will have methods such as an Add method that will create a new object of that class. (The Documents.Add method will create a new document). The Object Model PDF is also provided with the Inventor SDK (Software Development Kit). The SDK will be in a directory on your local system such as this: C:\Program Files\Autodesk\Inventor 2011\SDK\DeveloperTools\Docs The SDK ships with Inventor however separate msi files need to be run to install it. These are DeveloperTools.msi and UserTools.msi. DeveloperTools.msi installs samples and tools intended to help developers write applications. User Tools.msi will install applications that automate tasks for Inventor users that are not available in the Inventor product. The location of the installer is different for Windows XP and Windows Vista or Windows 7. Windows XP: C:\Program Files\Autodesk\Inventor 2011\SDK\DeveloperTools.msi Windows Vista or 7: C:\Users\Public\Documents\Autodesk\Inventor 2011\SDK\DeveloperTools.msi For more information on the SDK see this file: C:\Program Files\Autodesk\Inventor 2011\SDK\SDK_Readme.htm LAB 4 Discussion Link to LAB 4 A Closer Look at the Code In previous lessons we learned that a class can have methods and properties. Classes also generate events when things happen for your code to respond to: we want to respond to our button’s click event, for instance, and it’s in our handler for this event (which is a special kind of method) that we’ve added the rest of the code in this lesson. Private Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click End Sub When the button is clicked we want to get the components in the assembly that the user has already selected. To do this we need to use the SelectSet property of the document. 60 Autodesk® Inventor® API Lab for Power Users To make sure the rest of our code can function, we first need to check Inventor has a document open. We do this by checking the number of items in the Documents collection from Inventor’s Application object via the collection’s Count property. Notice how we’re using the dot notation to “walk the hierarchy”, going from one object to another via properties. If _invApp.Documents.Count = 0 Then MsgBox("Need to open an Assembly document") Return End If An “If Then” statement lets you execute code conditionally: if the condition between the “If” and the “Then” evaluates to true, then the code between the “Then” and the “End If” gets executed. In this example, when the Count property is zero, we know that no documents are open in Inventor. At this point we tell the user to open an assembly and we then Return from the Sub – jumping to the end of the Sub without executing any further code. It’s much better to check for problematic situations in this way than to let the code fail and generate a confusing error message. The Return keyword does not cause our application to exit completely – it only Returns from the Sub – so the user can simply open an assembly and click the button again. The next piece of code is another “If Then” statement: this time to make sure the document that’s active in Inventor is an assembly, as our code only works with assembly components. In this case we use a slightly different comparison: we use the inequality operator (<>) rather than the equality operator (=). So if the active document is not an assembly we present a different message to the user and Return from the Sub. If _invApp.ActiveDocument.DocumentType <> DocumentTypeEnum.kAssemblyDocumentObject Then MsgBox("Need to have an Assembly document active") Return If our code gets past the two “If Then” statements then we know an assembly is active and we can proceed to get the SelectSet from it. Dim asmDoc As AssemblyDocument asmDoc = _invApp.ActiveDocument If asmDoc.SelectSet.Count = 0 Then MsgBox("Need to select a Part or Sub Assembly") Return End If We use the Dim statement to declare a variable named “asmDoc” of type “AssemblyDocument” and directly assign the contents of the Inventor Application’s ActiveDocument property to it 61 Autodesk® Inventor® API Lab for Power Users using the equals sign (which is also the assignment operator in VB.NET). If we make changes to asmDoc using one of its properties, the change will take effect on the active document. Here we are just using one of the properties, “SelectSet” in an “If Then” statement – we’re not modifying the document at all. Once again we’re checking the Count property, to see whether the SelectSet contains any items: if the user did not select anything then we ask them to do so before Returning from the Sub. At this point in our code, we know that something is selected in the active Inventor assembly. Now we will go ahead and do something to each of the selected components. Dim selSet As SelectSet selSet = asmDoc.SelectSet Try Dim compOcc As ComponentOccurrence Dim obj As Object For Each obj In selSet compOcc = obj Debug.Print(compOcc.Name) compOcc.Visible = False Next Catch ex As Exception MsgBox("Is the selected item a Component?") MsgBox(ex.ToString()) Return End Try Here we assign the SelectSet containing the selected entities from the active AssemblyDocument to the “selSet” variable. A Try Catch block is used for error checking: if a problem occurs while executing the code inside the Try block, the code in the Catch block gets executed, allowing us to provide further information to the user about the problem (in this case we ask them to make sure they have selected one or more components). Within the Try block we use a “For Next” loop to step through each of the entities in the SelectSet. If only one entity is selected then the code between the “For Each” and “Next” statements will only be processed once. If there are more entities selected, the code will be processed once for each entity. We know that the code will be executed at least once, as we would already have Returned from the Sub if there were no entities in the SelectSet. The SelectSet could contain any type of entity that is selectable in Inventor: it’s for this reason we access its contents the generic “Object” type (a type that can contain any kind of object). If the user has selected something other than a Component an error will be generated when we attempt to assign it to our “compOcc” variable of type ComponentOccurrence. Assuming this assignment doesn’t cause an error, the next line will execute, which uses Debug.Print to send 62 Autodesk® Inventor® API Lab for Power Users the name of the ComponentOccurrence to the Immediate window in Visual Basic Express. This window can be displayed in Visual Basic Express from the Debug Menu>Windows (or Ctrl+G). Using Debug.Print and the immediate window is a useful tool if a problem occurs and you need to work out what is wrong. Notice that the Immediate window is accessed from a different location then the Error List which is accessed from View>Other Windows. After the Debug.Print statement we reach the goal of this lesson’s code, to hide the selected components by setting their Visible property to False. LAB 5 Discussion Link to LAB 5 A Closer Look at the Code for Button1 As we now know, using the Inventor API we can use custom attribute values to tag entities such as components. We can then use this attribute to find the tagged entities and do something with them. In this example we use a custom attribute value to locate the components and then hide or display them. There are three Inventor API classes that are used to add an Attribute to entities: AttributeSets, AttributeSet and Attribute. Many types of Inventor entity – including ComponentOccurrence – have an AttributesSets property. Notice in this example that the name of the variable we use to store the contents of the AttributeSets property is attbSets, a shorthand version of AttributeSets. Any naming convention can be used; however it is helpful if the chosen names are descriptive and consistent with either the type or the purpose of the variable. Dim attbSets As AttributeSets = compOcc.AttributeSets As mentioned, the second class we use when adding attributes is AttributeSet. From the name of this class we can guess that the AttributeSets object is a collection of AttributeSet objects. Each AttributeSet associated with an object needs to have a unique name. If an AttributeSet with the name we want to use has already been added to the component, we should not try to add another with that name. We can use the NameIsUsed method of the AttributeSets class to check if an AttributeSet with a particular name has already been added to that object. The name of the AttributeSet we are going to create in this example, myPartGroup, is passed into the NameIsUsed property. The If Not Then statement will cause the code between the If and End If to be processed if the name myPartGroup has not already been given to an AttributeSet in the AttributesSets of the component. ' Add the attributes to the ComponentOccurrence If Not attbSets.NameIsUsed("myPartGroup") Then End If 63 Autodesk® Inventor® API Lab for Power Users We use the Add method of the AttributeSets object to create an AttributeSet. (Remember that we got the AttributeSets from the component). The AttributeSets.Add method takes a String that will be the name of the AttributeSet. Here we are using the name myPartGroup, which needs to be passed in as a string of characters. To create a String, we simply need to enclose the text – the contents of the string – in double quotes. It is a good idea to use a descriptive value in this string, which will essentially be the name of our AttributeSet. We will also use this text to test for the existence of our AttributeSet via the “NameIsUsed” property. Dim attbSet As AttributeSet = attbSets.Add("myPartGroup") The third object we need to add the attribute is the Attribute itself. We use the Add method of the AttributeSet to create the Attribute. Dim attb As Inventor.Attribute = _ attbSet.Add( _ "PartGroup1", ValueTypeEnum.kStringType, "Group1") The AttributeSet.Add method takes three arguments. Notice how IntelliSense is very helpful for knowing the types of the various arguments: 64 Autodesk® Inventor® API Lab for Power Users The first argument is a String and will be the name of the Attribute. The second argument is a ValueType and is one of the value types in the Inventor.ValueTypeEnum. The third argument is the Value which needs to be the type specified by the second argument. A Closer Look at the Code for Button2 The code in Button2 searches for entities tagged with the attributes added by the code behind Button1: it uses the FindObjects method of the AttributeManager, which we access via the assembly document (asmDoc) and assign to a variable (attbMan). We want to catch any errors so we enclose these operations in a Try Catch block. Try Dim attbMan As AttributeManager = asmDoc.AttributeManager Dim objsCol As ObjectCollection objsCol = _ attbMan.FindObjects("myPartGroup", "PartGroup1", "Group1") Catch ex As Exception MsgBox("Problem hiding component") 65 Autodesk® Inventor® API Lab for Power Users End Try The FindObjects method populates an ObjectCollection with each of the objects it finds with our custom attribute attached. FindObjects takes three arguments to locate objects (Inventor entities) with attributes. Although we use all three arguments in our project – to look for attributes with a specific AttributeSetName, AtributeName and AttributeValue – they are all not required: in other situations we might choose to look only for attributes belonging to a set with a particular name, for instance. In this example we passed in “myPartGroup” (as the AttributeSetName), “PartGroup1” (as the AttributeName) and “Group1” (as the AttributeValue). IntelliSense, as we see below, helps us understand the meaning of the arguments to the FindObjects method. Notice the square brackets, which indicate that each of the arguments is optional. We should now have the tagged components in the ObjectCollection returned by the call to FindObjects. To hide or show the components we need to loop through each object in the collection and change its Visible property. The natural way to loop through a collection is using a “For Each Next” loop: other types of loop would also work, but would require us to maintain a 66 Autodesk® Inventor® API Lab for Power Users counter which acts as an index into the collection and to select items manually from it. “For Each Next” loops provide us with the current object in the collection at each pass through the code. The next line is very simple: we are simply toggling the Visible property of each ComponentOccurrence object: if the value is True it gets set to False and if it’s False it gets set to True. The Not operator lets us do this very succinctly, without the need for an “If Then Else” conditional statement. Dim compOcc As ComponentOccurrence For Each obj As Object In objsCol compOcc = obj ' Toggle the visibility of the Component Occurrence compOcc.Visible = Not compOcc.Visible Next LAB 6 Discussion Link to LAB 6 A Closer Look at the Code In this lesson we created two different Sub-routines that we can call when a button is clicked instead of having the code directly in our Sub. The advantage of using a separate Sub is that similar code will not be duplicated in our project and our code will be easier to update in the future. In lesson 5 we created a button that, when clicked, hid or displayed components after determining which to modify by searching for an attribute tag.. We now have a Sub that can hide or show these components. Public Sub HideOrShowGroup(ByVal hide As Boolean) End Sub We also have a Sub that can add components to or remove them from the group. Public Sub AddOrRemoveFromGroup(ByVal add As Boolean) End Sub These Sub-routines can run from anywhere in our project. We can pass in True or False to control the behavior. We were therefore able to reuse code that we created previously by making a few simple changes. We changed the code we pasted into the HideOrShowComponentsInGroup Sub to either hide or show the components based on the 67 Autodesk® Inventor® API Lab for Power Users Boolean value passed in. We did this by changing the For Each loop simply to make the Visible property of the component equal to the argument passed into the Sub. (hide). For Each obj As Object In objsCol compOcc = obj compOcc.Visible = hide Next The changes we made to the code added to the AddOrRemoveFromGroup Sub were to change its behavior based on the value of the Boolean variable, add, that was passed into the Sub. For the case where add is True, we used the code we already had. For the case where add is False, we added code to delete the AttributeSet. We used If Then Else End If statements to do this. Similar to adding an AttributeSet, we do not want to try to delete an AttributeSet that does not exist, so we checked the NameIsUsed property to test this. In the case the AttributeSet exists, we then used the Item property of the AttributeSets with our chosen name (“myPartGroup”), calling the Delete method on the resultant AttributeSet to remove it from the component. If add Then ' Add the attributes to the ComponentOccurrence If Not attbSets.NameIsUsed("myPartGroup") Then Dim attbSet As AttributeSet = attbSets.Add("myPartGroup") Dim attb As Inventor.Attribute = _ attbSet.Add( _ "PartGroup1", ValueTypeEnum.kStringType, "Group1") End If Else ' Delete the attributes to the ComponentOccurrence If attbSets.NameIsUsed("myPartGroup") Then attbSets.Item("myPartGroup").Delete() End If End If Next The code for Button 5 (Remove all components from group) that we copied from another Sub gets all the components with our AttributeSet and deletes the AttributeSet from each one (rather than deleting it only from the selected components). In this Sub, after we have an ObjectCollection with all of the components in the Assembly with our AttributeSet, we can use a “For Next” loop to delete the AttributeSet from each component. We also need to make each 68 Autodesk® Inventor® API Lab for Power Users component visible: after the Sub has finished its work, these components will no longer have our AttributeSet attached, and so will no longer be made visible by the “Show Group” button. 69