Civil 3D Dynamo Send Command Using Python Multiple DWGs

Hello,

I would like to run ATTSYNC on a directory containing multiple DWGs. I have a python script that should do the following:

  1. Get the DWG Files in the Directory
  2. Open up DWG and run the ATTSYNC command by sending a string to the command line
  3. Save and Close DWG
  4. Open up next DWG and follow same process

The script runs fine with no errors and I can tell that it is cycling through and opening each DWG. But the command is not successful. I can tell that the DWGs in the directory are not saved by the Python script so that could be the first issue. Any reason why my script below is not saving the DWGs when closing?

# Load the Python Standard and DesignScript Libraries
import clr
import os

# Add Assemblies for AutoCAD and Civil3D
clr.AddReference('AcMgd')
clr.AddReference('AcCoreMgd')
clr.AddReference('AcDbMgd')
clr.AddReference('AecBaseMgd')
clr.AddReference('AecPropDataMgd')
clr.AddReference('AeccDbMgd')

import Autodesk.AutoCAD.ApplicationServices.Application as acapp

# Import references from AutoCAD
from Autodesk.AutoCAD.Runtime import *
from Autodesk.AutoCAD.ApplicationServices import *
from Autodesk.AutoCAD.EditorInput import *
from Autodesk.AutoCAD.DatabaseServices import *
from Autodesk.AutoCAD.Geometry import *

import Autodesk.AutoCAD.ApplicationServices.DocumentCollectionExtension

# Import references from Civil3D
from Autodesk.Civil.ApplicationServices import *
from Autodesk.Civil.DatabaseServices import *

#bring in folder path where .dwg files are located
directory = IN[0]
#get list of files in folder path
fileList = os.listdir(directory)
#remove any files that do not have extension .dwg
fileList2 = [f for f in fileList if ".dwg" in f]
#create list of file paths
pathList = [directory+"\\"+f for f in fileList if ".dwg" in f ]

#Open Document For Write
for path in pathList:
    adoc = DocumentCollectionExtension.Open(acapp.DocumentManager, path, False)
    acapp.DocumentManager.CurrentDocument = adoc

    with adoc.LockDocument():
        with adoc.Database as db:
    	
            with db.TransactionManager.StartTransaction() as t:
            	# Code between here
                command = "ATTSYNC\nN\nTITLE PAC\n"
                adoc.SendStringToExecute(command, True, False, False)
            	# Code between here
                t.Commit()
                pass
                
#Save and Close                
    if adoc:
        DocumentExtension.CloseAndSave(adoc, path)

# Assign your output to the OUT variable.
OUT = pathList

image

As far as I know, commands can only work on the active document (source: when I try typing line it only works on the document in the UI). So you will need to change the document in the active user interface and then send the command. I think this can be done, but I am not sure Python will expose it. It would likely be a big lift if possible.

Running the above script opens up each DWG individually and runs the ATTSYNC command. Maybe Iā€™m not understanding what you mean by set ā€œactiveā€ but I can see the process happening live on my screen as the script cycles through DWGs. Is a document that is opened up not the active document?

1 Like

Ah - I think I misunderstood the issue. Youā€™re only failing to get results at the save command?

Sending commands to Autocad docs can be tricky - try via the interop assembly

clr.AddReference("Autodesk.AutoCAD.Interop")
from Autodesk.AutoCAD.Interop import *
acdoc = Application.AcadApplication.ActiveDocument
command = "ATTSYNC\nN\nTITLE PAC\n"
acdoc.SendCommand(command)

It is possible to programmatically mimic the ATTSYNC command - see here for versions in C# and VB .NET BLOCK Routines. To process a folder would require side loading each file database and processing

Other options would include using the python subprocess library to run a script with acadcoreconsole.exe or writing a dos/powershell script

1 Like

I might be having the issue that you earlier suggested but Iā€™m not sure. I do know for sure that the script is not saving each DWG before closing out so seems to be a problem there. Once I can get the script to save the DWGs then I will be able to diagnose if there is an issue sending the files to command if that makes sense

Could you send QSAVE via command?
Also prefixing commands with _.like this _.ATTSYNC will ensure Autocad uses internal ā€˜Englishā€™ version of command

I think Iā€™ve worked it out
You are locking the application document for the database transaction and then trying to run a command via the application api.
Remove the transaction wrapper as this is not required
[Edit] It was also if adoc: testing as False

Can you critique which portions of the code specifically I should remove? Iā€™m sorry Iā€™m not the most fluent in ironpython

Hereā€™s the script with some modifications. Iā€™ve cleaned up some of the imports and rewritten the pathlist comprehension. (Just becauseā€¦)

I did quite a few tests of other ways to save the document and the original works ok.

The biggest issue is the line if adoc: as this returns False - yes, hard to believe - I also assumed that any python object tests as True unless it is empty, equal to zero or None.

An alternative could be to use a parameter that returns a boolean such as adoc.IsActive: or just remove the line altogether.

# Load the Python Standard and DesignScript Libraries
import clr
import os

# import time # If required - see below

# Add Assemblies for AutoCAD
clr.AddReference("AcMgd")
clr.AddReference("AcCoreMgd")

# Import references from AutoCAD
from Autodesk.AutoCAD.Runtime import *
from Autodesk.AutoCAD.ApplicationServices import *
from Autodesk.AutoCAD.ApplicationServices import Application as acapp

# bring in folder path where .dwg files are located
directory = IN[0]

# get list of files in folder path
if os.path.isdir(directory):
    pathlist = [
        os.path.join(directory, f)
        for f in os.listdir(directory)
        if f.lower().endswith(".dwg")
    ]

# Open Document For Write
for path in pathlist:
    adoc = DocumentCollectionExtension.Open(acapp.DocumentManager, path, False)
    acapp.DocumentManager.CurrentDocument = adoc

    # time.sleep(3) # Pause as doc loads if required

    command = "_.ATTSYNC\nN\nTITLE PAC\n"
    adoc.SendStringToExecute(command, True, False, False)

    # Save and Close
    if adoc.IsActive:
        DocumentExtension.CloseAndSave(adoc, path)

# Assign your output to the OUT variable.
OUT = pathlist

Will it be the part of a larger script or the only purpose is the attsync?
Wouldnā€™t it be more convinient using a simple lisp and the batch save utility?