Tuesday, May 21, 2013

Deploying RPS scripts with DeployRpsAddin

I know nobody is checking the code I check into the RevitPythonShell code repository, because nobody bothered to ask me about the new killer feature i sneaked in. For a project at work that I used the RevitPythonShell for, the need arose to deploy a bunch of scripts on a customers computer. Now, RPS itself has a simple installer that you can execute and go next, next, next, finish and voilĂ , you have RPS installed on your computer. But... any scripts you write need to be copied to the customers computer, then RPS has to be installed and configured to pick up the script. Oh, and don't forget to install cpython and any modules you make use of.
Have you tried writing instructions for such a manual installation procedure? I have. It is not fun at all! I have also tried to get people to execute these instructions. It just doesn't fly.
This is where the new feature of RPS comes in: In the Ribbon, there is now an additional button called "Deploy RpsAddin" that is located under the button for starting the shell, just above the one for customizing RPS.
Deploying an RPS script as a standalone addin involves choosing an xml file that acts as a manifest or package description of the addin you are about to create. Basically, you describe which python scripts to assign to which buttons and it creates a new dll for you that includes all these scripts and can be used as an addin in Revit. You just need to write an addin manifest and place it in the right position.
The source comes with an example addin called HelloWorld. That really is all it does: a Button that prints "Hello, World!" to the screen. But I include the xml deployment file and also an InnoSetup script to get you started on deploying your own addins.
When you want to include a cpython library, you will need to make sure that this is also in the search path of the addin's bundled interpreter. So, the addin includes a bunch of files, dlls, a version of IronPython (2.7) and also the new RpsRuntime dll that handles the parts of RPS that get used by both the standard RPS version and deployed addins.
You can include cpython modules in your setup program, copying them for instance into your installation directory and then go from there. There is an equivalent of the RevitPythonShell2013.xml file that gets deployed with your addin to the %AppData% folder that you can use for setting up stuff.
The structure of the deployment xml file looks like this: (I will call it RpsAddin xml file from now on)
<?xml version=" 1.0" encoding=" utf-8" ?>
  <RibbonPanel text=" Hello World">
    <!-- the script is always searched relative to the location of the RpsAddin xml file -->
    < PushButton text="Hello World! " src="helloworld.py "/>
You can add as many RibbonPanel tags as you would like. Each PushButton is then placed on that panel and assigned to the script. The path to the script is relative to the RpsAddin xml file. The DLL that gets created is placed in a folder "Output_YOUR_RPSADDIN_NAME" relative to the RpsAddin xml. The name of your addin is taken from the name you call the RpsAddin xml file. In this case, the file is called "HelloWorld.xml", so the Addin will be called "HelloWorld", a folder "Output_HelloWorld" is created with the dll "HelloWorld.dll", "RpsRuntime.dll" and a bunch of IronPython dlls.
You can then use an InnoSetup file to create an installer for this. The HelloWorld example comes with this file:
Source: Output_HelloWorld\RpsRuntime.dll; DestDir: {app};
Source: Output_HelloWorld\IronPython.dll; DestDir: {app};
Source: Output_HelloWorld\IronPython.Modules.dll; DestDir: {app};
Source: Output_HelloWorld\Microsoft.Scripting.Metadata.dll; DestDir: {app};
Source: Output_HelloWorld\Microsoft.Dynamic.dll; DestDir: {app};
Source: Output_HelloWorld\Microsoft.Scripting.dll; DestDir: {app};

; this is the main dll with the script embedded
Source: Output_HelloWorld\HelloWorld.dll; DestDir: {app};

; add a similar line, if your addin requires a configuration file (search paths or predefined variables)
;Source: HelloWorld.xml; DestDir: {userappdata}\HelloWorld; Flags: onlyifdoesntexist;

{ install revit manifest file }
procedure CurStepChanged(CurStep: TSetupStep);
  AddInFilePath: String;
  AddInFileContents: String;

  if CurStep = ssPostInstall then

  { GET LOCATION OF USER AppData (Roaming) }
  AddInFilePath := ExpandConstant('{userappdata}\Autodesk\Revit\Addins\2013\HelloWorld.addin');

  AddInFileContents := '<?xml version="1.0" encoding="utf-8" standalone="no"?>' + #13#10;
  AddInFileContents := AddInFileContents + '<RevitAddIns>' + #13#10;
  AddInFileContents := AddInFileContents + '  <AddIn Type="Application">' + #13#10;
    AddInFileContents := AddInFileContents + '    <Name>HelloWorld</Name>' + #13#10;
  AddInFileContents := AddInFileContents + '    <Assembly>'  + ExpandConstant('{app}') + '\HelloWorld.dll</Assembly>' + #13#10;

  { NOTE: create your own GUID here!!! }
  AddInFileContents := AddInFileContents + '    <AddInId>276D41F2-CCC4-4B55-AF2A-47D30227F289</AddInId>' + #13#10;

  AddInFileContents := AddInFileContents + '    <FullClassName>HelloWorld</FullClassName>' + #13#10;

  { NOTE: you should register your own VendorId with Autodesk }
  AddInFileContents := AddInFileContents + '  <VendorId>RIPS</VendorId>' + #13#10;
  AddInFileContents := AddInFileContents + '  </AddIn>' + #13#10;
  AddInFileContents := AddInFileContents + '</RevitAddIns>' + #13#10;
  SaveStringToFile(AddInFilePath, AddInFileContents, False);


{ uninstall revit addin manifest }
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
  AddInFilePath: String;
  if CurUninstallStep = usPostUninstall then
    AddInFilePath := ExpandConstant('{userappdata}\Autodesk\Revit\Addins\2013\HelloWorld.addin');

    if FileExists(AddInFilePath) then

I included some Pascal code for installing an addin manifest to the %APPDATA% folder. This is generally something like C:\Users\username\AppData\Roaming\ADDIN_NAME. Running this setup will produce a file called Setup_Helloworld.exe that can then be given to your friends to try out your new cool HelloWorld Revit Addin, coded in the sweet python language we all love so much!


  1. Hi Daren,

    Absolutely love the RevitPythonShell; it's a real blessing for those cases where developing a full blown addin would be overkill and for exploring .NET/Revit API behaviours in a live environment.

    At work I've developed a standalone package (one folder structure) housing everything RevitPythonShell needs to run, with a custom configuration file so it's been easy-peasy getting it out to the 100+ revit users we have, along with any custom Revit python scripts. A simple batch script can be used to deploy it.

    Have you thought about packaging RevitPythonShell in a manner that it doesn't require the separate IronPython/Lib install, or installer at all?

    Really CPython isn't needed for the bulk of stuff one might do in the RevitPythonShell.

    1. Hi Dan R,

      Sorry, was on vacation and am only just now going through my mail... yes, you can deploy RPS like this really easy - the installers I wrote are for just getting there as fast as possible, so as many people as possible can try it out and have it just work.

      The IronPython libraries are necessary for it to work. CPython not so much. If you already have IronPython, then you don't really need to include those dlls, but this is not the general case.

      The DeployRpsAddin stuff makes it even easier to deploy just a few scripts - and what you bundle with it in the installer is really up to you, all that really is provided is a DLL that contains all the code and configuration stuff for the Ribbon...

    2. Ok yeah I guess the installer makes life simple for most users. The ironpython lib files we just bundle in with RPS to save relying on a [non]existing ironpython install (can't remember if the RPS installer requires ironpython separately?).

      Thanks for all your hard work on this addin. The only question is why didn't you put a price tag on it!? It seriously improves our development productivity in Revit.

    3. @DanR, regarding the price tag: RevitPythonShell was only possible, because I had access to a bunch of open source stuff to stitch together. Its great to hear from people actually using it - my work has since shifted and I'm not really using it much myself anymore, but will be opensourcing a real example sometime soon that includes energy analysis with EnergyPlus. Stay tuned ;-)

      But this tool is definitely going on my CV when I next look for a job!

  2. Hi Daren,

    I'm new to RPS and my own inexperience is giving me some trouble using this command. I've written a python script I would like to deploy and distribute to other Revit users. I'm simply working through your example with the helloworld.py script and XML files, but I cannot get the Deply RPSAddin to create the necessary .dll's. I have both files in the same folder, could you offer any advice or point out any rookie mistakes I am probably making?


    1. Hi Kevin,

      Did you receive any error messages? You should end up with a sub-directory called "Output_HelloWorld". What version of Revit are you using?

  3. Thanks for the quick reply, Daren. I did, its a generic Revit issue saying Command Failure for External Command.

    It states: Revit could not complete the external command. Contact the provider for assitance. Information they provided to Revit about their identity:.

    I am working in 2014 Revit. I have no resultant sub-directory in the folder structure.

  4. Kevin, I don't see this error - but just because it works on my machine doesn't mean anything. Where did you place the files? Maybe it is a permissions thing. Also, can you click on the "Details" button / link in the error message? On my computer that shows additional information I could use to help debug your problem.

    1. The files are in a folder on my desktop, the .py and the .xml file are the only two files in there. I should have full permissions, but perhaps something is off somewhere. I have the error message, but even with OCR in Adobe I cannot get a decent text version without retyping it. Perhaps I could email it to you somehow?

      The first line, however, is as follows:

      "Revit encountered a System.Xml.XmlException: Version number '1.0' is invalid.
      Line 1, position 16.

      Line one is just the XML version:

      Thanks for all of your help Daren, I'm very excited to get this up and running!

    2. This really is weird. Could you send me the XML file, the Screenshot and the .py file? I just added you on Google+. Maybe it is good for something?

    3. This comment has been removed by the author.

    4. Thanks for all of your help Daren, I just got it all to successfully compile and run. I'm unsure why the XML gave me an issue, but starting from a clean slate I managed to get it to run fine. The .addin file's had to be changed from to despite the fact that they are supposedly interchangeable. I could not get it to run with AddinId, and continually ran into an error.

      Up and running now - thanks for writing this great tool!

    5. Kevin, could you please post the addin file you used? I will then incorporate that into the trunk for future users. Just post it to the group: https://groups.google.com/forum/#!forum/RevitPythonShell

    6. Sure. I'll be away from the office for a bit but I'll get it up this week.

  5. Hi again Darren, loving using RevitPythonShell

    Quick question on creating addins, you mention that "You can include cpython modules in your setup program, copying them for instance into your installation directory".

    Which installation directory is this? Does something need to be defined in the .xml manifest? I have some user made modules that my script relies on, but when I install the addin the ribbon button throws an exception that it cant find the module.

    Another quickie - how easy is it to alter the icon of the addins ribbon button?

    Thanks for your time!

  6. Hi Callum, the xml manifest only really specifies the scripts run when a ribbon button is clicked. When installing your addin on another computer, you need to make sure that any modules (your own or built in python modules) are accessible to the IronPython interpreter running in RPS. This means, you need to add the search path to the RPS configuration file - AND DEPLOY IT ON THE USER MACHINE: %appdata%/ADDIN_NAME/ADDIN_NAME.xml. You can use %appdata%/RevitPythonShell2014/RevitPythonShell.xml as a starting point for this. Also, the modules need to be deployed to the user too...

    1. Thanks for the quick reply Darren!

      Where abouts should the module file be created on the users machine? My {program files}\RevitPythonShell2014 folder doesnt have a RevitPythonShell2014,xml file to sample....

    2. Ive worked through this a little more and made some progress, Im thinking the .xml file is the one selected when you 'deploy RPS addin'.

      Ive added the identical search paths to this .xml file that Ive added to the RPS configuration, but its still failing to import a module. hmmmmm

    3. Hi Callum, not %programfiles% (as those aren't editable), but instead %appdata%. There are two separate xml files needed: one describes the addin and is only used by the 'deploy RPS addin' functionality to create the dlls. The other is an RPS configuration file, containing search paths. RPS looks for this in the %appdata% folder (e.g.: C:\Users\daren\AppData\Roaming\MY_COOL_ADDIN\MY_COOL_ADDIN.xml). As to where to save the module files - I opted to place them in the same folder as the DLLs for my Addin... I'll post an example addin sometime to my blog soon, but I hope in the meantime you can figure it out?

    4. Hi Darren, managed to work it out. Thanks again for your help

  7. Regarding images for the ribbon: Add a "largeImage" and a "smallImage" attribute to the PushButton tag. The value of the attribute is the path to the image to use. Read up the RevitAPI docs on what sizes and colour depths to use (i think 32x32 and 16x16, 16bit deep) and if you have blurry images, I recall the DPI setting in the image needs to be set correctly - check the RPS icons in the source for an example of correct icons.

    1. Legend Darren, I was struggling to find anything on google for this

      Thanks again!

    2. Hmmmm in trying this Im getting an error when I start Revit:

      "Cannot run the external command"
      "Could not find part of the path C:\Program Files\MyApp\MyApp.dll\icon.png"

      My app deployment .xml file has this line:

      Does it look like Im using it right?
      I havent been able to find the RPS icons either for reference

    3. (note to self: dot try adding sample xml text in forum posts)
      Ive tries the following lines in MyApp deployment xml file:

      PushButton text="MyApp" script="MyApp.py" largeImage="C:\sample addin\icon.png" smallImage="C:\sample addin\iconSmall.png"

      PushButton text="MyApp" script="MyApp.py" largeImage="icon.png" smallImage="iconSmall.png"

      Either way Im getting the error

    4. Hi again Darren, Ive made a couple of APIs now and really enjoying it - thanks for all your great work on the RevitPythonShell.

      Getting these icons in the ribbon is the only aspect still not working right though, any help would be appreciated!

    5. Hello Callum, I'm sorry for not reacting to your question earlier - I was on vacation and didn't want to tell the whole internet I'm not home... I have fixed some bugs with the icon functionality and tested it. Google code won't let me create new downloads anymore (policy change) so use this link instead: https://www.dropbox.com/s/hwg1t6sd5wamg5w/Setup_RevitPythonShell_2014_r193.exe.

      In the PushButton tags, use the attributes "largeImage" and "smallImage". The value should be a path relative to the addin xml file. The icons will then be copied to the output folder and should be deployed in the same folder as the addin dll on the client machine (the relative path is stripped during the process of bundling). Can you give this a try and tell me if it worked? Thanks a lot!

    6. Hi Daren, no problems at all - I appreciate all you hard work on such a cool tool.

      Tried the new downloader from your link, re-deployed and still getting the error.

      Everything you mentioned happens (icons get copied etc), but the addin seems to be looking for the icon file on the end of the .dll file (as mentioned above) - not sure if this is expected behavior!

  8. Hi,

    Thank you for this post, I surely will need that in a near future !
    But right now, I'm stuck a little bit upstream of that. (I'm a beginner...)

    I wrote a python script with the help of the RPS, and it works fine when I run it from the console.
    But how can I call it from the Addin tab in Revit, just like the "Hello World" example ?
    Where do I need to locate my Python script - and are there other steps that need to be done in order to get it work ?
    That'd be a lot already for me !

    Thanks a lot,

  9. Hi Arnaud,

    check here: https://code.google.com/p/revitpythonshell/wiki/FrequentlyAskedQuestions

    you want to add a "canned command". Use the "Configure" dialog to add the path to the script to run. You'll see the "Hello World" example there too.

  10. Daren,

    Thanks for the quick reply, I have to say that was easy ;)
    Worst is that I had seen that video previously !

  11. Hi Daren,

    I'm a little bit confused now that I try to add ribbons the same way as the "Hello World", which, if I'm not mistaken, is a different approach as using the "Configure" dialog (?)

    I think I may also mix information between the "RPS" approach, and information that is related to the "classic" API approach...

    So, from what I see, "Hello World" is different because 1) it appears as a "sub-ribbon" in the "Revit Lookup" tab and 2) when I click it, it directly prompts a window saying "hello world", which is not the case if I call it from a button that I'd have added using "Configure" (in that case, a kind of shell is first opening, and then the "hello world" window pops up).

    So, I'll just throw some questions kind of randomly, and I think/hope it will help me to see clearer in all of that.

    Here it is ;)

    1) do I need also to write an "addin" file with scripts written with RPS ? I know I don't HAVE to, by using "Configure", but if I want to adopt the same approach as for the "Hello World" example, do I need to do that ? Or is it only for code written through the "non-Python API" ?

    2) same question for the xml files, do I have to write it ? I found the HelloWorld.xml file (http://revitpythonshell.googlecode.com/svn/trunk/RevitPythonShell/Examples/HelloWorld.xml), so obviously the answer is yes, but then again : what is the difference between this approach and using the "Configure" button ?

    3) and what about "xml configuration files" ? I found such a file while installing the AddinManager, but not necessarily with other add-ins. What is it ? Do I have to write it ? Is it only for the "non-Python API" ?

    4) with another add-in I found another type of xml file which seems to be called an "xml document". It looks "kind of" the other xml files, but hasn't the same key words. How do I know which files I need for wich addin ?

    5) ...and ".addin" files are also xml files, right ? :)

    6) And finally what about dll files ? Is that only for the "non-Python API" ? Or maybe you'll say that this is what this article is above :)

    In the end, I observe also that the HelloWorld.xml has particuliar tags such as an . Does that mean it "kind of" replace the usual .addin" files ? Is there documentation about that ?

    For now I don't even want to distribute my scripts, just use it for myself, but understand how all these files interact (or not).
    Sorry for these random questions, but maybe it'll help me to see a little bit better what I'm doing.

    Any help would be greatly appreciated !


    1. sorry, some text didn't pass, i meant :
      ... has particuliar tags such as "RibbonPanel" and "RpsAddin".

  12. Hello Arnaud,

    You are right: There are two basic approaches for RPS - the first one is to use RPS and add scripts with the "Configure" dialog. This is the way you should do it for scripts that you are currently developing.

    The whole deployment thing mentioned in this post solves the problem of where to go once you have a script you like and want to share. It allows you to carve out just those bits of RPS that you need to run your script on a computer that might not have RPS installed.

    RPS has it's own .addin file - it needs one so Revit knows to load RPS! In addition, RPS has an XML configuration file that stores the changes you make in the "Configure" dialog.

    When you distribute your script without RPS (as an RpsAddin), then of course you will still need a .addin file - to tell Revit to load your RpsAddin. You will also need the RpsAddin xml file. This is used by the "Deploy RpsAddin" command to create the DLL that contains your code. This file does not need to be deployed - it is just a "build script" for creating your own stand-alone addin. Your addin might also have a configuration xml file like RPS does, if you need to add search paths etc. I might need to do a better job documenting this...

    Answering your questions:

    1) yes, you need to write an ".addin" file if you want to deploy your scripts without RPS. Revit would otherwise not know how to load your addin. Check the InnoSetup file for the HelloWorld example in the RPS source code - it shows how you could automatically create such a file during installation: https://code.google.com/p/revitpythonshell/source/browse/trunk/RevitPythonShell/Examples/HelloWorld.iss

    2) the HelloWorld.xml file is the "build script" to produce the HelloWorld.dll file for standalone deployment of the helloworld.py script. This is the file you select from the "Deploy RpsAddin" command.

    3) uh. xml configuration files... hm. RPS also has a notion of this, but it will not have anything to do with the ones you found with the AddinManager. RPS stores the data from the "Configure" dialog in a file in %APPDATA%/RevitPythonShell201X/RevitPythonShell.xml. An standalone RpsAddin can have a similar file for search paths and variable definitions.

    4) this is vague. you are right: there are waaaay too many xml files in this world!

    5) yep. xml is quite handy for storing data. there are also other formats that could be used - some would argue, xml is the worst of the lot. It's not - there *are* worse - but yeah, not the best format for a lot of tasks...

    6) Revit can only load .NET assemblies as addins. These are either .dll or .exe files. No way around this. That is why the "Deploy RpsAddin" command creates a DLL: It is just a simple bootstrap to load the RpsRuntime.dll with the RpsAddin xml file (stored in the RpsAddin dll file) and get on with loading up everything. Check the source for how I did it - I'm quite proud of this solution :)

    HelloWorld.xml is *not* a replacement for the usual .addin file. It is a "build script" for producing the dll file your HelloWorld.addin file will point to. Also, it contains information used by the RpsRuntime.dll to create the ribbon panel, what scripts to put in it (the scripts are stored as resources inside the HelloWorld.dll).

    I hope that helps you?

  13. Hi,

    Thanks for the reply !
    It's getting clearer everyday but it's still a long way to getting comfortable. The simple fact that I now know that I can focus for now on the "Configure" approach is already a god thing ;)
    But yeah, I see clearer in all these files, I'll probably have to re-read this some times, but that's ok ;)

    My questioning actually came from the fact that I'd like to deal with functions that prompt the user to do something (pick a wall for instance), and then continue in the script. I thought that in order to do that I HAD to implement the Execute() method of IExternalCommand, and that this method could only be called using deployed scripts. But if I can do such things (I mean interacting with the user) with the "Configure" approach, then I'm good !

    Thanks again!

  14. Hi @Daren Thomas, if i want to use xml to make add-in for revit, but i want to make more type of button like More control over the RibbonPanel topic, it's possible ??

    1. Sure. You basically have two paths you can take: Either, you beef up the RPS and send me a pull request including new tags (instead of just "PushButton") OR you just create your own xml format and parse that in the startup script using the techniques described in the "More control over the RibbonPanel" topic. The first solution is my preferred solution but will require you to do some c# coding - I don't know how comfortable you are with that. For the second solution, look into the `etree` module - either the `ElementTree.etree` (builtin, shipped with RPS) or the `lxml.etree` versions will work.

    2. Thanks for you answering, but i just want to make Add-in for others can use it, not including PythonShell add-in. So i just need how to make file xml to build more type of PushButton (ex: radio, stackbutton,list button..). Can you send me some example *xml file how to make it ??

    3. I'm truly sorry. This doesn't exist yet. Making an Add-In without deploying the Pythonshell add-in as described in this article just reuses the RpsRuntime.dll library - so improving that would add the functionality for other users too. I can't give you an xml file to show, since it currently can't be done...

    4. I mean i'll make Add-in by Deploying file *xml, but i need make a few type of button (ex: stack button) instead of one add-in one push button in the current. It's possible ?
      Because i see the Interactive PythonShell add-in, it has list push button, why new add-in can't make more different kind of push button?

    5. You will need to use a startup script and manually create your ribbon panel if you want to get any fancier than just PushButtons for now. The only thing I can offer is using a tag that contains . That will work with the code as currently implemented. I will try to add more variety in the future, but you can really just create a startupscript, deploy that with your addin (https://daren-thomas.gitbooks.io/scripting-autodesk-revit-with-revitpythonshell/content/deploying_rpsaddins/creating_the_rpsaddin_manifest.html)

  15. Hi @Daren Thomas, first thanks you for reply to me. I have a problem with Revit 2014 when i use Deloying RPS addin, have an error !! I want to show you a picture, but i don't know how to send to you. May i get your mail address , let's me send you picture. Thanks

    1. Hi @Tran, please use the group forum for this - that way, questions can get answered by anyone registered: https://groups.google.com/forum/#!forum/revitpythonshell

      alternatively, you can use stackoverflow.com with the tag `revitpythonshell`

  16. Hi @Daren Thomas, i want to make install file for my add-in. I use your script abow for Innosetup. I have 2 things to ask you, first how i can set setup default dir always is path user ??
    And when i build to *.exe file, these *dll file installed, but the Main Addin File was not make. I don't know why this happend. Can you check this ??

    1. Ah just ok, i forgot close my Revit app, so at first question how i can set default dir set up is %APPDATA% folder, for people easy to install, just keep click Ok.

    2. @tran, this is an InnoSetup specific question. basically, you want to either change the {app} directory (check the InnoSetup documentation on how to do this - I don't know either) - or just specify another directory (similar to the way the revit addin manifest location is specified), but that will probably still create the directory in C:\Program Files (x86)...

      BTW: why oh why do you want to install to the APPDATA folder anyway? this could be an internal IT problem - your userbase not having the permissions to install software - but you would have a much easier time just going with the flow...

    3. I just want give setup files for somebody as my friend ...etc to help them easier to install add-in, and because Revit load add-in by that folder so i want to change default installation folder.
      Thanks you very much !

    4. ok... i think you misunderstand the "Revit load add-in by that folder"... Revit looks in a specific folder to find addin manifest files (*.addin) and loads those. Inside that file, the tag "Assembly" contains the path to the .dll (that gets created by DeployRpsAddin) and loads that dll. The dll can be wherever you like. Read more about this here: http://help.autodesk.com/view/RVT/2014/ENU/?guid=GUID-7577712B-B09F-4585-BE0C-FF16A5078D29 and here: http://thebuildingcoder.typepad.com/blog/2010/04/addin-manifest-and-guidize.html

    5. Ah yes, i missed that, in my point the addin mainfest file put same folder with dll files will be easier to control.
      Ok, thanks you for explain to me.
      Have a nice day !!

  17. Hi Daren! Can you go a bit more in depth about including extra modules so that they can be used in the .py scripts being packaged? For instance, I have multiple scripts that utilize the RevitPythonWrapper module and I would love to not have to go back and rewrite them all in order to package them to an .exe. Could you shed some light? Thank you for your time!

  18. This comment has been removed by the author.

    1. This comment has been removed by the author.

    2. This comment has been removed by the author.

    3. This comment has been removed by the author.