Skip to main content

AutoCAD

This guide explains how to integrate AutoCAD with a VIKTOR app. Your app calls the AutoCAD ActiveX Automation API directly, so you can draw parametric geometry into a drawing, read an existing drawing, and annotate it, all from your app code.

note

vkt.autocad.connect() and vkt.autocad.attach() are a BETA feature and require viktor >= 14.35.0.

vkt.autocad.connect() starts an AutoCAD instance on the worker for the session. vkt.autocad.attach() binds the session to the instance the user already has open instead of starting a new one; see Attaching to a running instance.

The worker​

A worker is a program that connects the VIKTOR platform to third-party software running outside the platform. For AutoCAD it belongs on the machine where you use AutoCAD, as a personal worker: AutoCAD must be installed where the worker is, and a session drives the real desktop application. Your VIKTOR app handles the communication between the web app and the worker.

Here is what happens during the integration:

  1. The user performs an action that requires an AutoCAD analysis.
  2. Your app opens a session, and each ActiveX call it makes is sent to the worker as a task.
  3. The worker executes that one call against the live AutoCAD instance and returns its result.
  4. Your app uses the returned values to build its views, or draws the next piece of geometry.
  5. (Optional) VIKTOR UI displays the result.

Unlike the integrations that send a file or a script to the worker, there is no drawing script to write and ship: all of your code stays in your app, and the worker machine needs no Python environment.

AutoCAD cannot run headless

The worker drives the real desktop application, so the machine it runs on needs a logged-in, interactive Windows session (a service running in Session 0 cannot start AutoCAD). The licence must not prompt on startup either: nobody is there to answer an Autodesk sign-in or activation dialog, so the app would only see a timeout. Activate the licence once by launching AutoCAD by hand on that machine, as the same user the worker runs as.

Calling the API directly from your app​

Open a session with vkt.autocad.connect() and navigate the AutoCAD object model from the acad object it gives you:

app.py
import viktor as vkt


class Controller(vkt.Controller):
parametrization = Parametrization

def draw_in_autocad(self, params, **kwargs):
with vkt.autocad.connect(timeout=60) as acad:
document = acad.ActiveDocument
model_space = document.ModelSpace

line = model_space.AddLine([0, 0, 0], [params.span, 0, 0])
line.Layer = "STRUCTURE" # property write: plain assignment
length = line.Length() # property read: call syntax (note the parentheses)

document.Regen(vkt.autocad.AcRegenType.acAllViewports)

Please consider the following:

  • A property is read with parentheses and written without them. line.Length() gives you the value; line.Layer = "STRUCTURE" sets it. This is the convention that catches people out most often, so it is worth checking first when a call behaves unexpectedly.
  • Objects that calls return are session-scoped handles. Use them as the start of further calls and as arguments, exactly like the COM objects they stand for. They are released when the session closes, so read the values you need before the with block ends.
  • Navigate down from acad. The back-references Application, Document and Parent are refused, so keep your own reference to acad.ActiveDocument instead of walking back up.
  • Every call is a round trip to the worker; navigating attributes to reach it is free. acad.ActiveDocument.ModelSpace.AddLine(...) is one round trip, not three. Bound loops that call or read properties per entity, and prefer the ActiveX calls that work in bulk over a loop that touches every entity of a large drawing.
  • Filenames are confined to the session's working directory. Give SaveAs, Import, Export, PlotToFile and InsertBlock a relative filename rather than an absolute path. In this release a drawing cannot be returned from the session to your app, so read the values you need through ActiveX while the session is open.
  • Some members are refused by the worker and raise vkt.ExecutionError: those that execute arbitrary code (SendCommand, RunMacro, LoadArx), those that wait for input at the command line (Utility.GetPoint and the other prompting Get*, Prompt, SelectOnScreen), those that change settings outliving the session (Preferences, SetVariable), and Save, Close and Quit. Save with SaveAs and a relative filename instead.
  • The enumerations live on vkt.autocad, and their member names mirror ActiveX exactly: vkt.autocad.AcRegenType.acAllViewports. from viktor.external.autocad import AcRegenType gives you the same object under a shorter name. Note that they are not on the top-level vkt namespace, so vkt.AcRegenType raises AttributeError.

The ActiveX members you call on acad mirror the AutoCAD Object Model and are not listed in the SDK reference. Look them up in the AutoCAD ActiveX Reference Guide, or in the acadauto.chm help file in your AutoCAD installation.

Attaching to a running instance​

vkt.autocad.attach() opens the same kind of session against the AutoCAD that is already running on the worker machine, rather than starting a fresh instance. The app then reads and writes the drawing the user has on screen, and what it draws appears in their window. This is what you want when the point is to act on the drawing in front of the user, which in practice means a personal worker on their own machine.

app.py
def draw_in_autocad(self, params, **kwargs):
try:
with vkt.autocad.attach(timeout=120) as acad:
self.draw(acad, params)
except vkt.WorkerSessionAttachError as err:
if err.reason == "error_attach_no_instance":
raise vkt.UserError(
"No AutoCAD instance was found on your worker. Open your drawing in AutoCAD on the "
"machine running your personal worker, then try again."
) from err
raise vkt.UserError(f"Could not attach to AutoCAD ({err.reason or 'unknown reason'}).") from err

The two are strictly separate:

  • attach() fails when nothing is running on the worker machine; it never starts AutoCAD for you.
  • connect() always starts a fresh instance. If AutoCAD is already open on the worker machine the session is refused — close it there, or use attach().
  • There is no automatic fallback in either direction. If you want one, write it yourself by catching vkt.WorkerSessionAttachError and calling connect().

What changes once the session is attached:

  • The drawing is not empty. Do not assume the model space is blank, that layer names are free, or that the coordinate system is at its defaults.
  • Closing the session detaches without exiting AutoCAD. The instance and the drawing stay as they were, including everything your app drew.
  • Nothing rolls back. Your app's changes are real edits in the user's drawing, and undoing them is their call. Draw onto layers with a recognisable prefix so the result is easy to isolate.
  • The user's file on disk is still never overwritten: Save is refused, and SaveAs writes into the session's working directory.
Trigger it from an action button, not a download button

An attached session produces edits in the drawing already open on the user's screen — they watch the geometry appear, and saving the file is theirs to do. A DownloadButton promises a file that is never coming, and a save dialog is not what anyone expects after editing a model in front of them. Use a vkt.ActionButton when your app only draws, and a vkt.SetParamsButton when it also writes values back into the parametrization. Keep DownloadButton for output your app builds itself, such as a report or a CSV of values read through ActiveX.

vkt.errors.WorkerSessionAttachError carries a reason: "error_attach_no_instance" when nothing is running on the worker machine, and "error_attach_refused" when an instance was found but would not accept the connection, typically because it is busy or waiting on a dialog. Others come from the worker connection itself rather than AutoCAD — for example "no_worker_online" (no worker is connected) and "worker_kind_not_allowed" (this worker kind is disabled for the organization) are usually the first ones a user hits; see the reference page for the full set of eight. Handle an unrecognised reason as a generic attach failure. Note that WorkerSessionAttachError is a subclass of vkt.WorkerSessionError, so catch it first if you handle both.

Add integration to app config​

To make the worker integration available through the interface, add the following to your viktor.config.toml:

worker_integrations = [
"autocad",
]

Install the worker​

Install an AutoCAD worker as a personal worker, on the machine where you use AutoCAD. It calls the AutoCAD API directly, so it needs no Python environment.

A personal worker connects the software that is installed on your own machine, for your own use. Personal workers are installed and managed with VIKTOR Desktop:

  1. Download and install VIKTOR Desktop and log in with your VIKTOR account, if you haven't done so already

  2. In VIKTOR Desktop, click "Add" and select AutoCAD

  3. Start the worker. You can view its logs inside VIKTOR Desktop, and the editor shows when your worker is online

note

Personal workers that were installed with the previous per-worker installer keep running, but new personal workers can only be added through VIKTOR Desktop: the per-worker installer download and connection-key flow are no longer available for personal use.

Why there is no organization worker here

An organization worker shares one machine between the users of a workspace, which does not suit AutoCAD. The session drives the real desktop application, so every concurrent user needs their own interactive Windows session and their own licence, and attaching to "the instance already running" on a shared machine would reach whatever drawing the previous job left open.

To offer AutoCAD capability across your organization instead of per user, use Autodesk Platform Services and its Design Automation API, which runs AutoCAD workloads in Autodesk's cloud with no worker and no local install.

Testing​

vkt.autocad.connect needs to be mocked within the context of (automated) testing. The vkt.testing module provides the mock_AutoCADConnection decorator, which maps each ActiveX path to the result it should return. The same decorator also mocks vkt.autocad.attach:

import unittest

import viktor as vkt

from app.my_entity_type.controller import MyEntityTypeController


class TestMyEntityTypeController(unittest.TestCase):
@vkt.testing.mock_AutoCADConnection(results={
# a result shaped {"__handle__": n} is decoded into an object handle, as in production
'ActiveDocument.ModelSpace.AddLine': [{"__handle__": 1}],
'Length': [6.0],
})
def test_draw_in_autocad(self):
MyEntityTypeController().draw_in_autocad()

Paths that are not in the dictionary return None. For a result that depends on the call, pass a function of the ActiveX path and its arguments instead of a dictionary.

Troubleshooting the worker installation​

How the worker finds the application​

The autocad and civil3d worker kinds both use AutoCAD's automation registration, AutoCAD.Application. Civil 3D has no separate registration: it is AutoCAD started with Civil 3D's product switches, so both kinds start and connect to the same acad.exe. That shared registration is why an installation problem on either product can surface in the other.

At worker start, the worker checks that this registration points to an installed executable. If the version-independent entry is stale, the worker falls back to the newest versioned entry, such as AutoCAD.Application.26, and logs a warning. If no entry points to an installed executable, the kind refuses to start and asks you to install or repair the product.

Installation conflicts​

Every AutoCAD-family product registers two entries: a versioned one per release, such as AutoCAD.Application.26 for the 2027 release, and the shared version-independent AutoCAD.Application. The shared entry is owned by whichever installer wrote it last, and uninstallers do not always clean it up. This is what can leave it stale or pointing at the wrong product:

  • An older release was removed after a newer one was installed. The shared entry keeps pointing at the removed release. The newer install registered its own versioned entry but did not rewrite the shared one. Without the fallback this shows as "cannot find the file specified" on connect() and "no running instance" on attach() while the product is visibly open. With the fallback the worker binds through the newest versioned entry and logs a warning. No action is needed, but repairing the installation cleans the entry up.
  • Several releases installed side by side. The worker starts the newest registered release. Install one AutoCAD-family release per worker machine, or make sure the newest one is the release you want to automate.
  • Per-user registry overrides. Entries under the current user's classes take precedence over the machine-wide ones for a worker that runs without elevation. A leftover per-user entry can shadow a healthy machine-wide registration. Remove it, or repair the installation.
  • No registration at all. A failed or trial installation may not register the automation server, and the worker refuses to start the kind.

In all cases the fix is on the machine, not in the worker. Repair the installation from Apps & Features, restart the worker, and check the worker log for the stale-registration warning.

Create mode​

connect() starts a private instance for the session. Only one AutoCAD-family application can be automated on a machine at a time, so create mode refuses to start while any AutoCAD or Civil 3D window is already open on the worker machine. Close the open application, or use attach mode to work with it. This also means create sessions on one worker run one at a time.

Attach mode​

attach() connects to the instance the engineer already has open. Keep exactly one instance open. With several open, the worker reaches only the first one started and cannot pick another.

  • Nothing open. The session fails with reason "error_attach_no_instance". Open the drawing on the worker machine and retry.
  • Civil 3D open, AutoCAD requested. Accepted. Civil 3D exposes the full AutoCAD API, so the autocad kind binds it happily — but remember the drawing is a Civil 3D drawing, and the app is reaching only its AutoCAD surface. To reach surfaces, alignments or COGO points, use the civil3d kind instead.
  • Product started as administrator. An elevated instance is invisible to a worker that runs without elevation, and the session reports no running instance. Run the product and the worker as the same Windows user at the same elevation.

The worker never closes, hides, or saves the engineer's instance in attach mode.

The autocad kind has no release gate: any installed AutoCAD release that registers the automation server can be automated. The civil3d kind does gate on the release, and also on a units profile.

FAQs​

Why does reading a property need parentheses?

Every attribute you touch on acad is forwarded to AutoCAD, and the connection cannot tell "give me this value" from "give me this object to keep navigating" without the call. So a property is read with call syntax, line.Length(), and written by plain assignment, line.Layer = "STRUCTURE". A method is called the same way, which makes the rule simple in practice: anything you want a value back from gets parentheses.

My app fails with "No AutoCAD instance was found on your worker"

That is vkt.WorkerSessionAttachError with reason "error_attach_no_instance": vkt.autocad.attach() binds only to an AutoCAD that is already running, and never starts one. Open the drawing in AutoCAD on the machine running the worker and try again, or use vkt.autocad.connect() if the app does not need the user's own drawing.

Why are Save and SendCommand refused?

The worker refuses members that let a session reach outside itself. Save would overwrite the user's file in place, so SaveAs with a relative filename is offered instead, which writes into the session's working directory. SendCommand, RunMacro, LoadArx and Eval execute arbitrary code on the worker machine. The object model covers essentially everything in the AutoCAD UI, so there is a supported member for what you need.

Can I download the DWG my app drew?

Not in this release: no file can be returned from the session to the app, and the session's working directory goes away with the session. Where the point is to produce drawn output, use vkt.autocad.attach() so the geometry lands in the drawing the user has open and they save it themselves. Where the point is a calculation, read the values you need through ActiveX while the session is open and build your views from those.

How do I roll this out to everyone in my organization?

Not with an organization worker: it shares one machine between the users of a workspace, and each concurrent AutoCAD session needs its own interactive Windows session and licence. Give each engineer a personal worker if they each work in their own drawings.

If what you need is AutoCAD capability without a per-user install, that is a different integration: Autodesk Platform Services offers a Design Automation API that runs AutoCAD workloads in Autodesk's cloud, reached from a VIKTOR app through an OAuth 2.0 integration rather than through vkt.autocad.

Every session times out and AutoCAD never appears

Check the two requirements in The worker before looking at your app code. The worker machine needs a logged-in, interactive Windows session, and AutoCAD must open straight to a drawing without an Autodesk sign-in or activation dialog. There is no way to check a licence up front, so a prompting licence surfaces only as a start-up failure.

My app is slow when it draws many entities

Every call, property read, or property write is one round trip to the worker; navigating attributes to reach it is free. Two things usually account for slowness: per-entity property reads in a loop that could be bounded or replaced with a SelectionSet filter, and per-entity work where a bulk ActiveX call would do (AddLightWeightPolyline with a flat coordinate array, or Copy on a block reference). Note also that about 65,000 object handles are available per session, so a job larger than that has to be split across several sessions.