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.
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:
- The user performs an action that requires an AutoCAD analysis.
- Your app opens a session, and each ActiveX call it makes is sent to the worker as a task.
- The worker executes that one call against the live AutoCAD instance and returns its result.
- Your app uses the returned values to build its views, or draws the next piece of geometry.
- (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.
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:
import viktor as vkt
from viktor.external.autocad import AcRegenType
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(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
withblock ends. - Navigate down from
acad. The back-referencesApplication,DocumentandParentare refused, so keep your own reference toacad.ActiveDocumentinstead 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,PlotToFileandInsertBlocka 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.GetPointand the other promptingGet*,Prompt,SelectOnScreen), those that change settings outliving the session (Preferences,SetVariable), andSave,CloseandQuit. Save withSaveAsand a relative filename instead. - The enumerations are importable from the SDK as
viktor.external.autocad.Ac*, and their member names mirror ActiveX exactly.
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.
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 useattach().- There is no automatic fallback in either direction. If you want one, write it yourself by catching
vkt.WorkerSessionAttachErrorand callingconnect().
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:
Saveis refused, andSaveAswrites into the session's working directory.
vkt.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. That set is open-ended,
so handle an unrecognised reason as well. 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:
-
Download and install VIKTOR Desktop and log in with your VIKTOR account, if you haven't done so already
-
In VIKTOR Desktop, click "Add" and select AutoCAD

-
Start the worker. You can view its logs inside VIKTOR Desktop, and the editor shows when your worker is online
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.
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.
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.