Welcome, guest | Sign In | My Account | Store | Cart
NOTE: Recipes have moved! Please visit GitHub.com/activestate/code for the current versions.

This script asks for a base directory and then changes all xrefs in all drawings in all subdirectories so that they use relative paths. To use it just copy it somewhere in your target directory structure and run.

Python, 100 lines
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
# Relative-refs.pyw
"""A short python script for repathing xrefs in Autocad."""

import win32com.client,os, os.path, tkFileDialog
from Tkinter import *
from tkMessageBox import askokcancel
from time import sleep

# Get a COM object for Autocad
acad = win32com.client.Dispatch("AutoCAD.Application")

def repath(filename):
    print 'Repathing %s...' %filename
    doc = acad.Documents.Open(filename)
    
    blocks = doc.Database.Blocks # Internally xrefs are just blocks!
    xrefs = [item for item in blocks if item.IsXRef]
    
    if xrefs:
        for xref in xrefs:
            old_path = xref.Path
            new_path = os.path.join('..\\x-ref\\',os.path.basename(old_path))
            xref.Path = new_path
            print 'Old path name was %s, new path name is %s.\n' %(old_path, new_path)
    try:
        doc.Close(True) # Close and save
    except: # Something when wrong,
        doc.Close(False) # close then report it
        raise
    
class Logger:
    """A filelike object that prints its input on the screen."""
    
    def __init__(self, logfile=None):
        """Takes one argument, a file like object for logging."""
        print 'Starting logger...'
        if not logfile:
            self.logfile = open('relative-refs.log','w')
        else:
            self.logfile = logfile
        sys.stderr = self                 # Super cheap logging facility...
        sys.stdout = self                 # Just redirect output to a file.
        print 'Logger running...'
    
    def write(self, line):
        sys.__stdout__.write(line)
        self.logfile.write(line)
    
    def close(self):
        """The close method restores stdout and stderr to normal."""
        self.logfile.close()
        sys.stderr = sys.__stderr__
        sys.stdout = sys.__stdout__

class Tktextfile:
    """A file like interface to the Tk text widget."""
    
    def __init__(self, root):
        """Create a scrollable text widget to be written to."""
        self.root = root
        self.text = Text(root,width=40,height=20)
        self.text.pack(side=LEFT, expand=True, fill=BOTH)
        scrollbar = Scrollbar(root)
        scrollbar.pack(side=RIGHT,fill=Y)
        self.text.configure(yscrollcommand=scrollbar.set)
        scrollbar.config(command=self.text.yview)
        self.text.focus()
    
    def write(self, line):
        """Write method for file like widget."""
        self.text.insert(INSERT, line)
        self.text.see(END)
    
    def close(self):
        """Fake close method."""
        pass

if __name__ == '__main__':
    if acad.Visible:
        acad.Visible = False
    root = Tk()
    text = Tktextfile(root)
    logger = Logger(text)
    dir = tkFileDialog.askdirectory()

    answer = askokcancel('RePath','Re path all dwg files in ' + dir + '?')
    
    if answer:
        for dirpath, subdirs, files in os.walk(dir):
            for name in files:
                ext = name.split('.')[-1] or ''
                # We want dwg files which are not in the x-ref directory
                if ext.lower() == 'dwg' and 'x-ref' not in dirpath.lower():
                    drawing = os.path.join(dirpath, name)
                    try:
                        repath(drawing)
                    except:
                        print 'Unable to repath drawing %s!' %drawing
                root.update()
    acad.Visible = True

Where I work we had been using a poorly designed directory structure, with absolute paths to all our referance files. This caused troubles when files were moved, because they retain referances to things they shouldn't, and it prevented us from shipping our drawings in a meaningful directory structure. Being a Python programmer I decided to try and write a script to rectify the situation.

The relative path used is hard coded into the script and is specific to the directory structure at my company. It wouldn't be too difficult to also ask the user for the directory containing the referance files, but I had no reason to. My company's standards require the folder structure to be identical for every project.

Autocad does come with a separate 'referance manager' application for doing things like this. It is slower when working with many files though and is more complicated to use.

2 comments

Sebastien CELLES 8 years, 9 months ago  # | flag

Hello,

first I'd like to thanks you about this nice script. I would like to use it but in a different way. I have several Autocad files that I copy using a Python script (I save them as a zip file using the zipfile module). Most of this files have XRefs that refer to a network drive Y:\Autocad\Library. I'd like to be able to open this files even when this network drive is deconnected (when I'm for example at home). I think a good idea could be to copy Y:\Autocad\Library locally on C:\Documents and Settings\user\My documents\Autocad\Library and change all the XRefs starting with Y:\Autocad\Library to C:\Documents and Settings\user\My documents\Autocad\Library What is your opinion about it ? I didn't dive enough in your script... (I don't know win32com enought) but could you say me what I should change to fit to my needs.

Kind regards

Dave Buchhofer 8 years, 9 months ago  # | flag

This actually doesn't appear to work, after a bit of reading on the autodesk forums it appears to be a limitation built in where you cannot access the Path property from a AcadBlock object, you need to get an AcadExternalReference object in order to view/edit the path. due to being pretty new with python I'm not sure how to duplicate the functionality of the 'TypeOf' comparison operator yet.

I'm fairly unclear how to translate something like this from acad's VBA into python, though I'd love to do it ;)

Public Sub msgBoxPath()
Dim oEntity As AcadEntity
For Each oEntity In ThisDrawing.ModelSpace
If TypeOf oEntity Is AcadExternalReference Then
MsgBox oEntity.Path
End If
Next
End Sub