Altium Scripting And Using The API

Overview

Altium supports the ability to wire and run scripts, pieces of code which can perform repetitive tasks or tasks which would otherwise take along time for a person to do and speed up the PCB design process. Altium supports DelphiScript (.pas), EnableBasic, VBScript, JavaScript, and TCL scripting language sets.

From my personal wonders it seems that Delphi and VBScript are the two most popular (Delphi being the one most of Altium’s own examples are written in, and VBScript being the one that many 3rd parties have used). My personal favourite is VBScript, due to it’s simplicity (while not lacking any power).

Child Pages

PCB-Related API
PCB-related scripting tips, advice and examples for Altium Designer.
Reading And Writing To Files
How to read from and write to files in your operating system from an Altium script.

Where To Download Scripts From

Lots of third-party scripts can be downloaded from the Google Code Project, found here.

Unfortunately, the original scripts that used to be included with Altium disappeared from Altium Designer 10. I’ve uploaded a zip of all these scripts that can be downloaded below.

[wpfilebase tag=file id=16 /]

Also, I have a project called AltiumScriptCentral which can be downloaded from GitHub. It has a collection of useful scripts for increasing productivity and validating PCB’s before being sent of for manufacturing. They are all written in VBScript, and can be used as code examples to write your own.

How To Write Your Own Scripts

Below are tips and code examples to write your own Altium scripts. I prefer VBScript (which to me personally was one of the easiest to grasp coming from a C/C++/C# background, and seems to be one of the widely supported languages), however most are written in Delphi Script.

If you want to learn more about these scripting languages, check out the VBScript page or the Delphi page.

A word of caution, when writing/testing scripts (and running downloaded scripts for the first time), always test them on a non-valuable PCB project (or similar) incase the script does not work as expected! Buggy/rouge scripts can cause Altium to lock-up/crash, stop the undo from working (until you restart Altium), stop you from being able to save from that point onwards (again until you restart Altium), among other things! You do not want to lose your precious work!

More than likely, you will need to use the internet to get familiar with the scripting language of choice. Remember, if using VBScript, make sure you search for VBScript tutorials, not just “Visual Basic”, as you will more than likely get the wrong language and you code won’t work (VBScript is different to Visual Basic).

The Basics

Exiting

The command

exit 

quits the current script and returns the user back to Altium.

Message Box

You can use the function ShowMessage() to display to the user a message box with simple text. An example in Delphi:

This will display a message box with the text “ Hello, world.”. Note that in Delphi, strings are delimited with single quotations ( ' ), not double quotations ( ").

This is the equivalent in VBScript:

The Hello World Example

In Altium, click File->New->Script Files->VB Script Unit. Altium should open a new, blank script file. Then type in the following code:

 Save the script file. Now run the script file by clicking DXP->Run Script and then selecting the HelloWorld function (which should be listed underneath the filename of the script, see the below image). When run in Altium, this code should display a message box with the text “Hello, world!”.

Running the Hello, world script in Altium.
Running the Hello, world script in Altium.

The Delphi equivalent is shown below:

Notice the differences between the two languages, VBScript requires only an end identifier but Delphi requires both a begin and end identifier. VBScript delimits strings with the " character, while Delphi uses the ' character.

Looping

Looping indefinitely (e.g. while true then...) WILL lock up Altium, forcing you to forcibly close it, and loosing all un-saved work. All make sure to save your work before running scripts!

Conversion From A String

Altium provides a set of functions for converting strings to other data types. These include functions like StrToFloat()

However, I recommend that you do not use these, but use the built-in ones of the scripting language of your choice. Why? Because the built-in ones have better support when used with other functions.

For example, if using VBScript, the CDbl() function converts all numbers that pass the IsNumeric() test, while the Altium-provided StrToFloat() will fail on things like “2-” (which passes IsNumeric()), and throws an exception, potentially crashing your application.

Best Practices

Before we get too serious, here are some best practices (stipulated by no-one except myself). These apply if you are VBScript:

  1. Always add the line  Option Explicit to every script file. This forces you to define all variables with Dim before using them, which results in far fewer bugs!
  2. Add a header to the top of every script file telling you and anyone else what it does. You consider yourself to be a good programmer, right? Here is an example (this is what I use):

     

  3. Be consistent with your naming conventions. I prefer camelCase for variable names.

Project-Related API

This sections covers code non-specific to either the schematics or PCB.

Obtaining The Current Project

Projects are represented as IProject objects. To get the currently active project, you can use the  DM_FocusedProject function on an IWorkspace object, as shown in the following Delphi example:

Informing Altium That You’ve Changed Things

Everytime you modify something with an Altium script, you should inform Altium that you have done so, so that things like undo/redo work correctly and Altium knows that the file needs saving. This is done through the RobotManager.SendMessage() procedure, which is a member of both the SchServer and PCBServer objects.

The following code examples show the RobotManager being used for schematic operations.

I have experienced issues with calling RobotManager.SendMessage() before, resulting it taking a long time (about 2s per call), displaying the infamous “Please wait a moment” dialogue box, and not seeming to have any effect. I am not sure what caused this, as this procedure is used in many scripts available on the web.

Mathematics

It won’t be long before you’ll end up doing something like trigonometry in Altium scripts to work out the placement of objects onto a PCB. This kind of thing is not really dependent on Altium but more on the provided mathematics libraries that come with the scripting language you are using.

Check out the VBScript page to find out how to use the sin() and cos() family of functions, how to get a value for Pi, and more! All these are applicable to Altium scripts.

Catching Exceptions

Usually, if you do something that throws an exception, the script will halt at the line which threw the exception, and you can probe the in-scope variables to find their values and other useful debugging information.

However, in my experience, you run into problems if you close the first (main) script form, and then an exception is thrown. You get an “unhandled exception” error, and you can’t debug the code at the current stop point. To prevent this, I make sure I never close the main form, but instead resize it to the smallest area possible before loading up a child form.

What happens when an exception is thrown in an Altium script.
What happens when an exception is thrown in an Altium script.

You get into some nasty situations where Altium will “lock-up”, if an unhandled exception is thrown and there are forms hidden but still active.

Schematic-Related API

The schematic server ( SchServer) is the root object for all script usage within schematics.

Getting The Current Schematic

Two of the most common ways to get an object to the current schematic is to use either SCHServer.GetCurrentSchDocument() or SCHServer.GetSchDocumentByPath(), as shown in the examples below.

Looping Through All Schematics In A Project

This example shows how you can safely loop through all schematics in a project. Put code to run on each schematic where shown. Note that the project must be compiled for this to work! Otherwise the iterator may only get some or none of the schematic documents. This is because SchServer.GetSchDocumentByPath() only retrieves open documents. Compiling a project loads the documents into memory, which counts as being ‘open’, even though you may not be able to see the schematic tab.

The GetSchDocumentByPath() function requires the full path to the schematic to be passed into it. This can be obtained by using an IDocument object which in turn is obtained with an IProject  object.

Compiling The Project

Compiling the project can be done programmatically and is necessary precursor for a number of other scripts to work (e.g. iterating through all the schematics in the project, they cannot be found until the project is compiled). This code example assumes you already have retrieved the IProject interface object from the workspace interface

Refreshing The Schematic

Use the following command to refresh the screen:

Auto-complete Tip

Press Ctrl-Space to bring up the auto-complete help at any time. This is very useful when writing scripts for Altium, and results in you having to do a lot less of searching through code reference guides!

Special Strings

Special strings are text strings which get converted when presenting certain output. The following special strings are available:

Special strings are the reason why most objects are provided with two string properties, UnderlyingString and ConvertedString. UnderlyingString contains the raw text of a string, while ConvertedString contains the text once all special strings have been converted into the appropriate output.

Regex

If using VBScript, you can easily use the built-in regex engine to use regex with Altium scripts.

GUI Design

The Altium scripting language lets you design graphical user interfaces (GUIs) for your scripts. Each script file has the option of having an associated form which can be displayed from a function call.

Warning/Be Aware: The script engine will automatically remove empty event handler functions (e.g. a function that is called when the user clicks the button) on file save. It also will prompt you to delete event handler function calls that point to functions that no longer exist.

TForm

The TForm object is the main window, the parent UI object which holds all of the other UI objects for the window inside it.

You can resize a TForm object programmatically using the Height and Width parameters. The script file Main.vbs in the AltiumScriptCentral project uses these parameters often to “hide” the main window when a child window is opened (without actually hiding it, which causes other issues). Here is an example which resizes the main form before calling a child script:

 Note that there is a minimum window size to Altium script forms which keeps the minimise, maximise and close buttons just visible. Setting the Height and Width parameters to values below this minimum threshold has no effect.

TRadioGroup

TRadioGroup controls are useful for when you have multiple “sets” of radio buttons on one UI. They allow multiple radio buttons to be selected (from different groups of course), without all other on the UI being deselected.

Adding a TRadioGroup control to an Altium script UI.
Adding a TRadioGroup control to an Altium script UI.R

Individual radio buttons are added to a radio group through the Items property, in where you add a new radio button on a new line.

The selected radio button in a group is controlled via the TRadioGroups ItemIndex property. This property can the read or written. This property corresponds to the line number of the corresponding radio button entry in the  Items property (0-based count).

The DeleteSchematicParameters,vbs script in the AltiumScriptCentral project makes use of the TRadioGroup control.

Bugs

A Command Is Currently Active, Cannot Save

You can get the following error when trying to save if you haven’t called PCBServer.PostProcess an equal number of times as PCBServer.PostProcess. So far, if this does occur, I haven’t worked out how to save the document (all changes since the last save are lost!).

pcb-script-bug-a-command-is-currently-active-and-save-cannot-be-completed-at-this-time-png You might get this error in Altium when trying to save because you have run a script which hasn't called PSBServer.PostProcess an equal number of times as PCBServer.PostProcess.

See the Undo section for information.

Access Violation When Using Some Of The Arc’s Properties

When trying to define a arc using any of StartX , StartY , EndX , or EndY  causes an access violation within AltiumComponents.bpl. A workaround to this is to define the arc using the centre, radius, and start/stop angle parameters.

Variables Not Being Syntax Highlighted

The syntax highlighting in the code editor doesn’t highlight variables in some cases. It seems to happen after you copy and paste bits of code around and the syntax highlighter algorithm gets confused about what’s a variable and what’s not. If you define more than one variable in a single statement, e.g.

track1  will be coloured as a variable, while track2  will look like normal text. Normally closing and re-opening the file you are editing in Altium will fix this.

Some PCB OBjects Properties Cannot Be Set Directly, Even Though They Are Visable Through The AutoComplete Menu

For example. you cannot set a pads soldermask and pastemask expansion values (useful if you want to remove them alltogether) using the properties pad.SolderMaskExpansion and pad.PasteMaskExpansion. The only way I have found to do this is to create a pad cache, set the expansion values for the cache, and then assign the cache to the pad as per the following example.

Undo Is Not Working Correctly

Make sure that you call PCBServer.PreProcess and PCBServer.PostProcess correctly! (see the Undo section for more details).

 External Resources

Checkout CA Idiots blog

Posted: February 16th, 2012 at 8:26 am
Last Updated on: July 12th, 2015 at 4:28 pm

One Pingback/Trackback

  • Christian

    Is it also possible tu read out the thickness of the pcb board (Design -> Layer Stack Manager -> Total Height)?
    I also want to read out the project name out of the project parameters (Project -> Project Options -> Parameters).
    Do you got an idea?

    • Not of the top of my head I don’t, and I had a quick look elsewhere on the internet (which I’m sure you did too), and could not find anything.

      Might be something to ask Altium!

  • Christian

    Thanks for your effort 😉

  • Renju Mathew

    I have opened a PCB project and added PCB, Sch, PCB Lib, Sch Lib and a script file(Delphi).
    Script file contains the below.

    procedure Test_only
    var
    board : IPCB_Board;
    begin
    // Obtains the PCB server and the PCB document
    board := PCBServer.GetCurrentPCBBoard;
    if (board = nil) then exit;
    ShowMessage(‘Hello world!’);
    end;

    Even though the pcb file is opened, the script never shows the Hello world message. It always exit.
    I hope its suppose to show the message if the PCB file is opened.

    I cant find the solution for this problem. I tried reinstalling altium and yet the script did not work.
    Could you please help me to identify if i have done anything wrong? Kindly reply.

    • Hi,

      The code itself looks like it should work fine. Are you running the script? It won’t run automatically when you load up a PCB file, you have to manually start the script by clicking DRC->Run Script (up near the top-left of the screen) when the PCB file is loaded.

      The script has to be added to the current project to be seen in the dialogue box that opens.

      Geoff

      • Renju Mathew

        I have done as you suggested… and it worked!! 🙂 Thanks for your reply…. 🙂

        Renju

  • Michelle

    Hi,
    I have q query. I want to copy all the components of a specific layer to another PCBDoc. How can I achieve this. Really looking forward to your quick turnaround.

    • gbmhunter

      Most components in Altium have parts on many different layers (e.g. their copper pads on either the top or bottom layer, their silkscreen text on the silkscreen layer e.t.c), so what exactly do you mean? Do you wish to copy the entire component of any component that has a part of it on a certain layer, or only copy the sub-component parts that exists on a certain layer (which will make them free objects when copied)?

      • Michelle

        Hi Gbmhunter,
        Thanks for the reply. Really appreciate this. Basically, I have a requirement to scan a layer and copy content of that layer to another PCB doc’s same layer. Assume that component is present only on that layer (i.e. the component does not have parts on different layer but only on that specific layer). Really look forward to your help.

        • Michelle

          I have found a solution to this problem. Can you tell me how to add a text string any layer in Altium.

          • gbmhunter

            Good to hear you found a solution! Care to share the code? I can add it too this page 🙂 Regarding text string layers, I will have a look this weekend (busy atm, hope that it isn’t too late…).

          • Michelle Tekk

            HI GBMHunter,

            I followed these steps :
            Call SetDocumentPreferences_for_mech11()
            Function SetDocumentPreferences_for_mech11

            ResetParameters

            End Function

  • Michelle Tekk

    Hi,

    How to remove a document from a project through script?

  • Baudart François

    OK, I found how to draw polygon using your first method. Replace the line

    “polygon.Layer := eTopLayer;” and all the following by :

    polygon := PCBServer.PCBObjectFactory(ePolyObject, eNoDimension, eCreate_Default);
    Polygon.SetState_PointCount(4); // this one is really important
    polygon.SetState_Segments(0,polygonSegment1);
    polygon.SetState_Segments(1,polygonSegment2);
    polygon.SetState_Segments(2,polygonSegment3);
    polygon.SetState_Segments(3,polygonSegment4);
    polygon.SetState_Segments(4,polygonSegment1);
    polygon.SetState_PolygonType(eSignalLayerPolygon);
    polygon.SetState_PourOver(1);
    polygon.SetState_PolyHatchStyle(ePolySolid);
    polygon.SetState_BorderWidth(0);

    Board.AddPCBObject(polygon);
    polygon.Rebuild; // must be done after adding polzygon to PCB

    Client.SendMessage(‘PCB:Zoom’, ‘Action=Redraw’ , 255, Client.CurrentView);

    end;

    • Baudart François

      and
      “procedure CreatePolygon1;”
      instead of
      “procedure CreatePolygon1”

      Tell me if it works for u.

      And thks for the help. U built the first steps I needed. 🙂

      • gbmhunter

        Awesome work! I’ll give it a go as soon I have a bit of free time. If it works, permission to add the code to this website?

        • Baudart François

          Yeah of course !

          Here is a fully functioning script, working in Altium Build 9.4. :

          procedure CreatePolygon1();
          var
          polygon : IPCB_Polygon;
          polygonSegment1 : TPolySegment;
          polygonSegment2 : TPolySegment;
          polygonSegment3 : TPolySegment;
          polygonSegment4 : TPolySegment;
          begin

          // define the polygon segments
          polygonSegment1 := TPolySegment;
          polygonSegment1.Kind := ePolySegmentLine;
          polygonSegment1.vx := MMsToCoord(500);
          polygonSegment1.vy := MMsToCoord(500);

          polygonSegment2 := TPolySegment;
          polygonSegment2.Kind := ePolySegmentLine;
          polygonSegment2.vx := MMsToCoord(500);
          polygonSegment2.vy := MMsToCoord(1000);

          polygonSegment3 := TPolySegment;
          polygonSegment3.Kind := ePolySegmentLine;
          polygonSegment3.vx := MMsToCoord(1000);
          polygonSegment3.vy := MMsToCoord(1000);

          polygonSegment4 := TPolySegment;
          polygonSegment4.Kind := ePolySegmentLine;
          polygonSegment4.vx := MMsToCoord(1000);
          polygonSegment4.vy := MMsToCoord(500);

          // build the polygon
          polygon := PCBServer.PCBObjectFactory(ePolyObject, eNoDimension, eCreate_Default);
          polygon.SetState_PointCount(4); // this one is really important
          polygon.SetState_Segments(0,polygonSegment1);
          polygon.SetState_Segments(1,polygonSegment2);
          polygon.SetState_Segments(2,polygonSegment3);
          polygon.SetState_Segments(3,polygonSegment4);
          polygon.SetState_Segments(4,polygonSegment1);
          polygon.SetState_PolygonType(eSignalLayerPolygon);
          polygon.SetState_PourOver(true);
          polygon.SetState_PolyHatchStyle(ePolySolid);
          polygon.SetState_BorderWidth(false);
          polygon.SetState_Layer(eTopLayer);
          polygon.SetState_RemoveNarrowNecks(false);

          // add polygon to PCB
          Board := PCBServer.GetCurrentPCBBoard;
          Board.AddPCBObject(polygon);
          polygon.Rebuild; // must be done after adding polzygon to PCB

          // refresh
          Client.SendMessage(‘PCB:Zoom’, ‘Action=Redraw’ , 255, Client.CurrentView);

          end;

          • gbmhunter

            Awesome, it pretty much works, except you forgot to define the Board variable (just add the line “Board;” between “var” and “begin”). It would probably be good to make sure the user ran the script from a PCB document (check that Board is not nill after calling “GetCurrentPCBBoard”.

          • Baudart François

            I totally agree with you.
            Great job !

          • gbmhunter

            Just beginning to use VBScript rather than Delphi, and liking it! Not having to remember all the begin/end keywords, and the simpler looking code are good advantages. Which do you prefer?

          • Baudart François

            I totally agree with you ! But I couldn’t find the way to use TPolySegment in VBScript. So I used Delphi instead.
            (sorry for the delay of my answer, I missed the alert email.)

          • gbmhunter

            I added your code to this page! I also added PreProcess() and PostProcess() calls to make the example undo compatible.

  • Zman

    Undo
    With specifically calling the functions PCBServer.PreProcess and PCBServer.PostProcess, you will lose undo/redo functionality when/after running altium scripts.

    must be “without”

    • gbmhunter

      Hmmm…I have noticed this too Zman, calling some combination of PreProcess and/or PostProcess seems to stop undo/redo functionality until you restart Altium. I was under the assumption you had to call these to allow proper undo/redo functionality, so obviously somethings wrong here! Will look into this in more depth…

      • Zman

        by the way, the object created in the above example with contours is not a polygon, it is a region! and these are two different kinds of objects

        • gbmhunter

          Thanks Zman! I have updated that section above to reflect the fact they make regions not polygons.

          • FreakRob

            Just FYI: You can create a script to call PCBServer.PostProcess manually. At least that way, you will be able to save again after some manual labor.

            Procedure PostProcessBug;
            Begin

            Pcbserver.PostProcess;

            End;

  • DG

    Hi has anyone tried to script creation of a polygon on the schematics? I would love to know what the mechanism is for defining the vertices, I assume its a point array and you can just keep adding to it using the ChooseLocationInteractively function but I cannot find what that ‘array’ is called to even use it… or maybe its an Add Point thing?

    • gbmhunter

      No I haven’t tried that sorry! Given the huge differences between the schematic and PCB editor (Altium brought the PCB editor of another company at some point in the past), I’m guessing the code would not be that similar.

  • Pingback: December 2014 Updates | mbedded.ninja mbedded.ninja()

  • István Bakró Nagy

    Hi,

    I found your site very useful for writing Altium scripts with its API.

    Now I am in trouble finding a way to select a component by its designator.

    Is it possible to do it by its API or is it possible at least by one of the
    Processes?

    (select and than zoom to selected like fit-to-screen maybe?)

    And is there a global/generic ID of anything to address components, pins, nets, etc?

    • Hi there Istvan,

      Are you talking about the schematic or PCB editor?

      • István Bakró Nagy

        Let’s say PCB, but I am also interested in the SCH also 🙂

        • I’m pretty sure you can do this with a PCB Iterator. I will have a look tomorrow and get back to you.

  • Sean Whittaker
  • Sean Whittaker

    Hi,

    Thanks for highlighting the issue with the number of PostProcess calls not matching the PreProcess calls due to a crashed script.

    I created a script to kill any processes still outstanding, allowing you to then save any work you may have lost.

    Hopefully this helps anyone who encounters this issue in future.

    Cheers,

    Sean.

    procedure Main;

    // This script loops through and calls PostProcess a number of times

    // to allow exiting a locked up script in Altium.

    Var

    count : Integer;

    Go : Boolean;

    Begin

    count := 0;

    Go := True;

    while Go Do

    Begin

    Pcbserver.PostProcess;

    count := count + 1;

    // The number below can be incremented to guarantee a script

    // kill.

    if count > 10 then

    Begin

    // Exit loop when count greater than 10

    Go := False;

    End;

    End; // Exit while

    // Finish script

    End;