Plans2DWG (Iron > CPython3)

the year is 2024
and some folk are still still asking for .dwg files!

…and I am picking up a very old script (that we had, working, in Revit 2019) that used the bulk of @Konrad_K_Sobon’s (IronPython) code from this thread:

# Copyright(c) 2016, Konrad K Sobon
# @arch_laboratory, http://archi-lab.net

# Import Element wrapper extension methods
import clr
clr.AddReference("RevitNodes")
import Revit
clr.ImportExtensions(Revit.Elements)

# Import DocumentManager and TransactionManager
clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
doc = DocumentManager.Instance.CurrentDBDocument

# Import RevitAPI
clr.AddReference("RevitAPI")
import Autodesk
from Autodesk.Revit.DB import *

import System
from System.Collections.Generic import *

import sys
pyt_path = r'C:\Program Files (x86)\IronPython 2.7\Lib'
sys.path.append(pyt_path)

#The inputs to this node will be stored as a list in the IN variable.
dataEnteringNode = IN

def ProcessList(_func, _list):
    return map( lambda x: ProcessList(_func, x) if type(x)==list else _func(x), _list )

def ProcessParallelLists(_func, *lists):
	return map( lambda *xs: ProcessParallelLists(_func, *xs) if all(type(x) is list for x in xs) else _func(*xs), *lists )

def Unwrap(item):
	return UnwrapElement(item)

folderPath = IN[0]

if isinstance(IN[1], list):
	views = ProcessList(Unwrap, IN[1])
else:
	views = list(Unwrap(IN[1]))

if isinstance(IN[2], list):
	names = IN[2]
else:
	names = list(IN[2])

RunIt = IN[3]

def ExportDwg(name, view, folder = folderPath):
	options = DWGExportOptions()
	options.SharedCoords = True #sharedCoords
	options.LayerMapping = "L:\SOFTWARE\REVIT\Library\Shared\Export_Definitions\A_PT_RVT-to-DWG.txt"
	options.Colors = options.Colors.TrueColorPerView
	views = List[ElementId]()
	views.Add(view.Id)
	result = doc.Export(folder, name, views, options)
	return result

if RunIt:
	try:
		errorReport = None
		# run export
		ProcessParallelLists(ExportDwg, names, views)
		
	except:
		# if error accurs anywhere in the process catch it
		import traceback
		errorReport = traceback.format_exc()
else:
	errorReport = "Please set the RunIt to True!"

#Assign your output to the OUT variable
if errorReport == None:
	OUT = 0
else:
	OUT = errorReport

w/ a few modifications of my own, namely added:

  • shared coord export (ln56)
  • layer-mapping (ln57)*
  • color options (ln58)
    (that I sourced from scavenging code elsewhere)

*which I’ve removed from both .dyn files uploaded here as that points to a local resource wouldbe testers won’t have


thing is, once in Revit 2023, having switched the script-node to CPython3
(in the lazy, fingers-crossed hope that that would be all that’s needed)
the Revit 2023 version does not output any .dwg files.

Peering through the code late last Friday afternoon I spotted ln25 calling specifiically on a library:
pyt_path = r'C:\Program Files (x86)\IronPython 2.7\Lib'
… and thought, “aha! that’s the block!” had a bit of a cry on Twitargh (hey @solamour !) then sought to swap that out

but alas even after “learning” the CPython3 libraries live elsewhere/behave a bit differently - care of this:

swapping in either

C:\Users\Kieren.Porter\AppData\Local\python-3.9.12-embed-amd64\
C:\Users\Kieren.Porter\AppData\Local\python-3.9.12-embed-amd64\python39.zip

didn’t do anything!?
nor did removing that whole chunk (that @Mark.Ackerley appeared to allude to)
(which I’ve left in in the uploaded copies)


presumably, there’s something more (not) going-on here - and this is one of those perfect-storm moments folk like me deserve to run into when blindly using found-code - but if anyone could offer up any insight what could get this over the line, and spitting out .dwg files “again”, I’d be most grateful!!!

PS:
the quick-test .rvt models for 2019 & 2023 uploaded here should spit out 3No. .dwg files (using their respective .dyn)
success2019
Project_2019.rvt (460 KB)
Plans to DWG_2019_no-mapping.dyn (38.8 KB)
Project_2023.rvt (480 KB)
Plans to DWG_2023_no-mapping.dyn (41.2 KB)

Hey,

I hit the image button in the python script editor, and commented out the line 58 and it worked for me?

options.Colors = options.Colors.TrueColorPerView

I’m not quite sure why, but options.Colors returns 0 instead of the enumeration?

I’ll dig around and see if I can work out why :slight_smile:

Hope that helps,

Mark

1 Like

Enums are not fully supported, so you have to do a round-about way to get them. There are several examples on the forum which can be found via search. :slight_smile:

1 Like

ooo

I did (after posting) pull out all my additions - but didn’t get any different result (interestingly, I get a 0 return even on succesful runs of the 2019 version?)

tbh I’ll happily abandon the additional switches (well, all except the shared.coords one, as that’s really the whole point we need/want the automation) if it gets the thing working

1 Like

That’s how it’s setup :slight_smile:

You could change to OUT = ‘woop_woop’ or perhaps append ‘result’ to an output list to confirm the files created? Something similar anyway :slight_smile:

Mark

# Copyright(c) 2016, Konrad K Sobon
# @arch_laboratory, http://archi-lab.net

# Import Element wrapper extension methods
import clr
clr.AddReference("RevitNodes")
import Revit
clr.ImportExtensions(Revit.Elements)

# Import DocumentManager and TransactionManager
clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
doc = DocumentManager.Instance.CurrentDBDocument

# Import RevitAPI
clr.AddReference("RevitAPI")
import Autodesk
from Autodesk.Revit.DB import *

import System
from System.Collections.Generic import *

import sys
pyt_path = r'C:\Program Files (x86)\IronPython 2.7\Lib'
sys.path.append(pyt_path)

#The inputs to this node will be stored as a list in the IN variable.
dataEnteringNode = IN

def ProcessList(_func, _list):
    return [ProcessList(_func, x) if type(x)==list else _func(x) for x in _list]

def ProcessParallelLists(_func, *lists):
        return list(map( lambda *xs: ProcessParallelLists(_func, *xs) if all(type(x) is list for x in xs) else _func(*xs), *lists ))

def Unwrap(item):
        return UnwrapElement(item)

folderPath = IN[0]

if isinstance(IN[1], list):
        views = ProcessList(Unwrap, IN[1])
else:
        views = list(Unwrap(IN[1]))

if isinstance(IN[2], list):
        names = IN[2]
else:
        names = list(IN[2])

RunIt = IN[3]

output = []


def ExportDwg(name, view, folder = folderPath):
        options = DWGExportOptions()
        options.SharedCoords = True #sharedCoords
        options.LayerMapping = "L:\SOFTWARE\REVIT\Library\Shared\Export_Definitions\A_PT_RVT-to-DWG.txt"
        options.Colors = ExportColorMode.TrueColorPerView
        views = List[ElementId]()
        views.Add(view.Id)
        result = doc.Export(folder, name, views, options)
        return name

if RunIt:
        try:
                errorReport = None
                # run export
                output.append(ProcessParallelLists(ExportDwg, names, views))
                

        except:
                # if error accurs anywhere in the process catch it
                import traceback
                errorReport = traceback.format_exc()
else:
        errorReport = "Please set the RunIt to True!"

#Assign your output to the OUT variable
if errorReport == None:
        OUT = output
else:
        OUT = errorReport
1 Like

This seems to work…

options.Colors = ExportColorMode.TrueColorPerView
1 Like


confirmed! bless you Mark!!!

PS:
i wasn’t even aware of the 2-to-3 auto-fix-thingy - so that’s a real plus to know!!!

3 Likes

Glad it’s working now :smiley: Big-ups to @Mark.Ackerley!

Also, we’re actively working on improving the CPython3 implementation this quarter, so expect some of the “Workarounds” to disappear in liue of… better things :slight_smile: Will never be full parity with IronPython as they are different implementations, but it will definitely be better.

4 Likes