Write your first script for Altium Designer

Altium Designer code completion for scripts

Scripts are a great way to extend the features of Altium Designer: as soon as you have custom needs, writing a script could be a good option.
If you have already programmed some Altium Designer scripts, you can directly jump to my Code examples at the end of the article.

About the use of scripts

In the past I wrote a starting guide about scripts. If you are not used to the scripting system of Altium Designer, I would recommend to read this article first:

Available programming languages

Altium Designer is bundled with different script interpreters, supporting the following languages:

The most widely used is probably DelphiScript. At least the vast majority of examples are written with this language.
From what I have seen so far, VBScript holds the second position. The syntax is quite, but not strictly, similar to VBA (Visual Basic for Applications), widely used for MS Office macros.

IMPORTANT NOTICE: Since Altium Designer version 18, you’re not asked what language you want to work with while creating a script file. DelphiScript (*.pas) is assumed to be the one and only language for scripts.
Don’t worry, the other languages cited above are still supported. For the moment…

Documentation for scripts developers

The first version of the scripting documentation (“wiki.altium.com”) was a nightmare. It was very difficult to find where to start when you were a newcomer.
The today’s version is better in terms of organization and readability (“techdocs.altium.com”). But it has never been ported to the new Altium Designer documentation system (“altium.com/documentation”)! The Altium teams don’t seem very much involved in pushing the scripting system forward.

For that reason I would take the “techdocs” documentation as a reference (last update in late 2017).
Because the Altium Designer SDK probably shares the API with the scripting system, the Delphi SDK reference documentation could be considered as the “updated version” of the scripting system documentation.

Understanding how scripts work

Clients, Servers and Documents structure

If I were you, I would start by reading the following documentation articles:

  1. The scripting documentation entry point
  2. How is the platform architectured: models, objects, clients, servers, …

If you want to know more about how the system is built, you can read the following document (253 pages). It is described as “a concise reference of the Altium Designer low level system API as part of the Altium Designer Run Time Library”.

Working with the API

The API (Application Programming Interface) is the part of Altium Designer which allow developers to interact with internal functions from “outside”, ie. from user scripts / add-ons.

The Client-Server architecture

Based on what you’re trying to do, you will use different APIs:

  • Client API
  • Schematic API
  • PCB API
  • Integrated Library API
  • Vault/Nexus API
  • Workspace Manager API

“The client module is represented by its IClient interface object and you have the ability to take a peek into the data structures through this IClient interface. The client maintains a list of loaded servers, opened server documents, loaded server processes and resources.”

“Servers are represented by IServerModule interfaces. These interfaces are declared and partially implemented in the RT_ClientServerInterfaces unit.”

Each of the Schematic API, PCB API, Integrated Library API and Vault/Nexus API inherits from a “Server object model”:

Server API interface

The Workspace Manager doesn’t inherit the “IServerModule” interface. Instead it gives an access to the DM interfaces (Documents Model? Documents Manager? Design Manager?), but it’s kind of “read-only”. Unlike the SCH Server or PCB Server, the Workspace Manager is always available even if no document is open.

“The Workspace Manager is a system extensions server which is always running when Altium Designer is loaded in memory. […] This Workspace Manager provides information on how a project is structured, information on nets and its associated net aware objects of source and implementation documents.”

Here is the IWorkspace object structure:

  • IWorkspace
    • IProject
      • IDocument
        • ISheetSymbol
        • IComponent
        • IBus
API overview from the GUI

The whole API documentation is available in a single PDF document (1144 pages). This is the perfect document in which you could search for any function or parameter.

Let’s go coding!

Take care…

In the development process, something may go wrong. A buggy script could really mess up an entire project. Be sure to always test your scripts on dummy project files.
Furthermore, a badly written script can lock-up the Altium Designer user interface, forcing you to kill the program the dirty way. Be prepared for that!

Development tools

Altium Designer itself act as an IDE (Integrated Development Environment) for script programming. I don’t really like the embedded editor but I must admit it gives you some power in the development process.
I personally use the code completion feature as an “in-editor documentation”. At any time, hit CTRL + Space to grab the auto-completion box.

Some documentation pages can really help you being efficient in your programming tasks:

Keyboard shortcut keys available in the scripting context are listed in the “Script editor tools” page.

Create the script

Creating a script (without a graphical interface) is an easy task:

  • With Altium Designer version 18 and newer:
    • File > New > Script > Script Unit
      Noticed that you can’t choose the language? DelphiScript only (*.pas)! Fortunately you can “Save As” the file and force a new extension manually.
  • With AD17 and older:
    • File > New > Script Files > {{your preferred language}} Script Unit

The official documentation won’t encourage you to create a script outside of a script project, but if you don’t need to split your code across multiple files you may not need a project file.

“When multiple scripts are in a project, any script (using the same language set) within that project has access to global variables and procedures. All scripts in a project are ‘visible’ in effect, so a procedure in one script can call another procedure in different script within the same script project.”

Take inspiration from others

Again, the official script examples are here to help when trying to program a script from scratch.

If you want to broaden your mind, the “Altium Designer add-ons” project is probably the thing you should look at. Several users posted their codes and many use cases are addressed (schematics, PCB, automation, release process, …). Mostly written in DelphiScript, but other languages are also available.

Geoffrey HUNTER gave us its feedback from his programming experience with Altium Designer scripts: Altium scripting and using the API, PCB-related API.
He also made his scripts publicly available (VBScript). This is a solid source of inspiration: AltiumScriptCentral.

Code examples

All my examples are written in VBScript. If you want to program in an other language, you will need to adapt the following code snippets.
No graphical interface example for the moment, but don’t worry: many tasks can be achieved without having one. If you absolutely need a GUI, explore the documentation and look at the examples.

Pop-up dialogs

First things first: let’s test the execution of a simple script by popping-up dialogs to the user!

ShowMessage "This is a standard message"
ShowError "There is an error"
ShowInfo "Here is a piece of information"

Don’t know how to run the script? Didn’t you read the above instructions too fast?! I don’t blame you: here is the cheat sheet.

The auto-completion box (CTRL + Space) shows us every possible “ShowSomething” dialog box types.

Auto-completion on ShowSomething instruction

Write to the “Messages” panel

This is my preferred way to show the progress of a script execution. Plus it can tell the user on which objects the script did some modifications.

GetWorkspace.DM_MessagesManager.ClearMessages ' clear previous messages to see only those which are related to the current function
GetWorkspace.DM_ShowMessageView ' show up the 'Messages' panel
GetWorkspace.DM_MessagesManager.BeginUpdate
GetWorkspace.DM_MessagesManager.AddMessage "[Info]", "Part '" & foo & "' has been upgraded to '" & bar & "'.", "Script: MyScriptName", GetWorkspace.DM_FocusedDocument.DM_FileName, "", "", 2, False
GetWorkspace.DM_MessagesManager.EndUpdate

Each AddMessage() instruction could be associated with a small image to illustrate the message. Have a look at the image index table: “2” means “IndexPart”.

AddMessage to the Messages Panel

Verify things you are going to work on

As a good programming habit, check every objects you’re gonna work with:

Sub Check
  ' Check if Client is available
  If Client Is Nothing Then Exit Sub
  ' Check if GetWorkspace is available
  If GetWorkspace Is Nothing Then Exit Sub
  ' Check if the active document is a schematic sheet
  If GetWorkspace.DM_FocusedDocument.DM_DocumentKind <> "SCH" Then
    ShowError "This script must be loaded from an active schematic sheet."
    Exit Sub
  End If
  ' Verify that the file path really exists
  If Not FileExists(GetWorkspace.DM_FocusedDocument.DM_FullPath) Then
    ShowError "Please save your file before trying to operate on it."
    Exit Sub
  End If

  ' Do your stuff here

End Sub

Obtain the current document object

Workspace:

GetWorkspace.DM_FocusedDocument

Client:

Client.OpenDocument("SCH", GetWorkspace.DM_FocusedDocument.DM_FullPath)

Server:

SchServer.GetCurrentSchDocument
' OR
SchServer.GetSchDocumentByPath(GetWorkspace.DM_FocusedDocument.DM_FullPath)

Now you would be tempted to act on objects contained in a document: iterators + filters are the way to go.
Here is an example which loops on every parameters of every parts of the currently focused schematic document.

Set SchDoc = SchServer.GetCurrentSchDocument
Set SchDocIter = SchDoc.SchIterator_Create ' scan the document for all available objects
SchDocIter.AddFilter_ObjectSet(MkSet(eSchComponent)) ' get Sch components only, see https://techdocs.altium.com/display/SCRT1/Schematic+API+Types#TObjectId for more info
Set SchCurComponent = SchDocIter.FirstSchObject
While Not(SchCurComponent Is Nothing) ' loop on all components of the schematic sheet
  Set SchCompIter = SchCurComponent.SchIterator_Create ' scan the component for all available information
  SchCompIter.AddFilter_ObjectSet(MkSet(eParameter)) ' get component parameters only, see https://techdocs.altium.com/display/SCRT1/Schematic+API+Types#TObjectId for more info
  Set SchCurComponentParameter = SchCompIter.FirstSchObject
  While Not(SchCurComponentParameter Is Nothing)
    ' Do some stuff with the parameter
    ShowMessage SchCurComponentParameter.Name & " = " & SchCurComponentParameter.Text ' this is just an example
    Set SchCurComponentParameter = SchCompIter.NextSchObject ' go to next parameter
  WEnd
  SchDoc.SchIterator_Destroy(SchCompIter)
  Set SchCurComponent = SchDocIter.NextSchObject ' go to next component
WEnd
SchDoc.SchIterator_Destroy(SchDocIter)

Undo/Redo

It’s convenient for the end user to be able to revert the changes made by a script. This is your job, as a developer, to tell the system if the “Undo” feature would work and how.
You can decide what to put in the undo stack. If you’re doing a lot of modifications, I would recommend to do a single call of the below functions. This way, the “Undo” command will revert all the changes at once.

' Initialize the Robot Manager to activate the Undo stack
Call SchServer.ProcessControl.PreProcess(Client.CurrentView, "")

' Do your modifications here

' Clean-up the Robot Manager
Call SchServer.ProcessControl.PostProcess(Client.CurrentView, "")

Editing the content

Simple things to know before editing the content of a document.

SetCursorBusy ' the mouse pointer indicates that the function is running
Client.OpenDocument("SCH", Document.DM_FullPath).Modified = True ' set the document "dirty" to tell that it has been modified

' Do your modifications here

ResetCursor
SchServer.GetCurrentSchDocument.GraphicallyInvalidate ' force the graphical engine to refresh what is currently visible

More to come…

I’ve planned to write more articles about scripting. Stay tuned!