Skip to content

Commit 33b3d75

Browse files
romangolevjmcouffin
authored andcommitted
feat: mvp c# loader
1 parent b686515 commit 33b3d75

File tree

7 files changed

+92
-27
lines changed

7 files changed

+92
-27
lines changed
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# -*- coding: utf-8 -*-
2+
#pylint: disable=C0103,W1401,E0401,E0602
3+
"""
4+
██▓███▓██ ██▓ ██▀███ ▓█████ ██▒ █▓ ██▓▄▄▄█████▓
5+
▓██░ ██▒██ ██▒▓██ ▒ ██▒▓█ ▀▓██░ █▒▓██▒▓ ██▒ ▓▒
6+
▓██░ ██▓▒▒██ ██░▓██ ░▄█ ▒▒███ ▓██ █▒░▒██▒▒ ▓██░ ▒░
7+
▒██▄█▓▒ ▒░ ▐██▓░▒██▀▀█▄ ▒▓█ ▄ ▒██ █░░░██░░ ▓██▓ ░
8+
▒██▒ ░ ░░ ██▒▓░░██▓ ▒██▒░▒████▒ ▒▀█░ ░██░ ▒██▒ ░
9+
▒▓▒░ ░ ░ ██▒▒▒ ░ ▒▓ ░▒▓░░░ ▒░ ░ ░ ▐░ ░▓ ▒ ░░
10+
░▒ ░ ▓██ ░▒░ ░▒ ░ ▒░ ░ ░ ░ ░ ░░ ▒ ░ ░
11+
░░ ▒ ▒ ░░ ░░ ░ ░ ░░ ▒ ░ ░
12+
░ ░ ░ ░ ░ ░ ░
13+
░ ░ ░
14+
This is the starting point for pyRevit. At Revit loads the PyRevitLoader.dll
15+
addon at startup. This dll then creates an ironpython engine and runs
16+
pyRevitLoader.py (this script). It's the job of this script to setup the
17+
environment for the pyrevit module (pyrevitlib\pyrevit) and load a new pyRevit
18+
session. This script needs to add the directory path of the pyrevit lib folder
19+
so the pyrevit module can be imported and used.
20+
"""
21+
22+
import sys
23+
import os.path as op
24+
25+
# add the library location to the system search paths
26+
repo_path = op.dirname(op.dirname(op.dirname(op.dirname(__file__))))
27+
sys.path.append(op.join(repo_path, 'pyrevitlib'))
28+
29+
# now pyrevit can be imported
30+
from pyrevit.loader import sessionmgr
31+
32+
# ask sessionmgr to start a new session
33+
sessionmgr.load_session(light=True)

dev/pyRevitLoader/Source/PyRevitLoaderApplication.cs

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,8 @@ private static void LoadAssembliesInFolder(string folder)
7272
private static Result ExecuteStartupScript(UIControlledApplication uiControlledApplication)
7373
{
7474
//TODO: Implement a switcher here to be able to switch between Python/C# loaders
75-
//return ExecuteStartUpPython(uiControlledApplication);
76-
return ExecuteStartUpCsharp(uiControlledApplication);
75+
return ExecuteStartUpPython(uiControlledApplication);
76+
//return ExecuteStartUpCsharp(uiControlledApplication);
7777
}
7878

7979
public static Result ExecuteStartUpPython(UIControlledApplication uiControlledApplication)
@@ -86,7 +86,7 @@ public static Result ExecuteStartUpPython(UIControlledApplication uiControlledAp
8686
var uiApplication = (UIApplication)fi.GetValue(uiControlledApplication);
8787
// execute StartupScript
8888
Result result = Result.Succeeded;
89-
var startupScript = GetStartupScriptPath();
89+
var startupScript = GetStartupScriptPath(false);
9090
if (startupScript != null)
9191
{
9292
var executor = new ScriptExecutor(uiApplication); // uiControlledApplication);
@@ -123,6 +123,22 @@ public static Result ExecuteStartUpCsharp(UIControlledApplication uiControlledAp
123123
);
124124

125125
sessionManager.LoadSession();
126+
127+
// execute light version of StartupScript python script
128+
Result result = Result.Succeeded;
129+
var startupScript = GetStartupScriptPath(true);
130+
if (startupScript != null)
131+
{
132+
var executor = new ScriptExecutor(uiApplication); // uiControlledApplication);
133+
result = executor.ExecuteScript(startupScript);
134+
if (result == Result.Failed)
135+
{
136+
TaskDialog.Show("Error Loading pyRevit", executor.Message);
137+
}
138+
}
139+
140+
141+
126142
return Result.Succeeded;
127143
}
128144
catch (Exception ex)
@@ -131,13 +147,13 @@ public static Result ExecuteStartUpCsharp(UIControlledApplication uiControlledAp
131147
return Result.Failed;
132148
}
133149
}
134-
private static string GetStartupScriptPath()
150+
private static string GetStartupScriptPath(bool isLight)
135151
{
136152
var loaderDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
137153
var dllDir = Path.GetDirectoryName(loaderDir);
138-
return Path.Combine(dllDir, string.Format("{0}.py", Assembly.GetExecutingAssembly().GetName().Name));
154+
var formattedName = isLight ? "{0}Light.py" : "{0}.py";
155+
return Path.Combine(dllDir, string.Format(formattedName, Assembly.GetExecutingAssembly().GetName().Name));
139156
}
140-
141157
Result IExternalApplication.OnShutdown(UIControlledApplication application)
142158
{
143159
// FIXME: deallocate the python shell...

dev/pyRevitLoader/pyRevitAssemblyBuilder/AssemblyMaker/AssemblyBuilderService.cs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,21 @@ public ExtensionAssemblyInfo BuildExtensionAssembly(WrappedExtension extension)
3636
string code = _typeGenerator.GenerateExtensionCode(extension);
3737

3838
File.WriteAllText(Path.Combine(outputDir, $"{extension.Name}_Generated.cs"), code);
39-
4039
var syntaxTree = CSharpSyntaxTree.ParseText(code);
4140

41+
// Resolve relative paths to dependencies
42+
string currentAssemblyDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
43+
string revitApiPath = Path.Combine(AppContext.BaseDirectory, "RevitAPI.dll");
44+
string revitApiUIPath = Path.Combine(AppContext.BaseDirectory, "RevitAPIUI.dll");
45+
string runtimeDllPath = Path.Combine(currentAssemblyDir, $"pyRevitLabs.PyRevit.Runtime.{_revitVersion}.dll");
46+
4247
var references = new List<MetadataReference>
4348
{
4449
MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
4550
MetadataReference.CreateFromFile(typeof(Console).Assembly.Location),
46-
MetadataReference.CreateFromFile(@"C:\Program Files\Autodesk\Revit 2025\RevitAPI.dll"),
47-
MetadataReference.CreateFromFile(@"C:\Program Files\Autodesk\Revit 2025\RevitAPIUI.dll"),
48-
MetadataReference.CreateFromFile(@"C:\Users\Equipo\dev\romangolev\pyRevit\bin\netcore\engines\IPY342\pyRevitLabs.PyRevit.Runtime.2025.dll")
51+
MetadataReference.CreateFromFile(revitApiPath),
52+
MetadataReference.CreateFromFile(revitApiUIPath),
53+
MetadataReference.CreateFromFile(runtimeDllPath)
4954
};
5055

5156
string runtimePath = Path.Combine(Path.GetDirectoryName(typeof(object).Assembly.Location), "System.Runtime.dll");

dev/pyRevitLoader/pyRevitAssemblyBuilder/AssemblyMaker/CommandTypeGenerator.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,7 @@ public string GenerateExtensionCode(WrappedExtension extension)
1313
{
1414
var sb = new StringBuilder();
1515
sb.AppendLine("#nullable disable");
16-
//sb.AppendLine("using System;");
17-
//sb.AppendLine("using System.ComponentModel;");
1816
sb.AppendLine("using Autodesk.Revit.Attributes;");
19-
//sb.AppendLine("using Autodesk.Revit.UI;");
2017
sb.AppendLine("using PyRevitLabs.PyRevit.Runtime;");
2118
foreach (var cmd in extension.GetAllCommands())
2219
{

dev/pyRevitLoader/pyRevitAssemblyBuilder/SessionManager/UIManagerService.cs

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System;
1+

2+
using System;
23
using System.Collections.Generic;
34
using System.Linq;
45
using Autodesk.Revit.UI;
@@ -62,14 +63,14 @@ private void RecursivelyBuildUI(object obj, object parentComponent, RibbonPanel
6263

6364
if (subType == CommandComponentType.PushButton)
6465
{
65-
itemDataList.Add(new PushButtonData(subCmd.UniqueId, subCmd.Name, assemblyInfo.Location, subCmd.UniqueId));
66+
itemDataList.Add(CreatePushButton(subCmd, assemblyInfo));
6667
originalItems.Add(subCmd);
6768
}
6869
else if (subType == CommandComponentType.PullDown)
6970
{
7071
var pdData = new PulldownButtonData(subCmd.UniqueId, subCmd.Name);
7172
itemDataList.Add(pdData);
72-
originalItems.Add(subCmd); // to match later
73+
originalItems.Add(subCmd);
7374
}
7475
}
7576

@@ -81,7 +82,6 @@ private void RecursivelyBuildUI(object obj, object parentComponent, RibbonPanel
8182
else if (itemDataList.Count >= 3)
8283
stackedItems = parentPanel?.AddStackedItems(itemDataList[0], itemDataList[1], itemDataList[2]);
8384

84-
// Now post-process pulldowns to add nested pushbuttons
8585
if (stackedItems != null)
8686
{
8787
for (int i = 0; i < stackedItems.Count; i++)
@@ -96,7 +96,7 @@ private void RecursivelyBuildUI(object obj, object parentComponent, RibbonPanel
9696
if (child is FileCommandComponent subCmd &&
9797
CommandComponentTypeExtensions.FromExtension(subCmd.Type) == CommandComponentType.PushButton)
9898
{
99-
var subData = new PushButtonData(subCmd.UniqueId, subCmd.Name, assemblyInfo.Location, subCmd.UniqueId);
99+
var subData = CreatePushButton(subCmd, assemblyInfo);
100100
pdBtn.AddPushButton(subData);
101101
}
102102
}
@@ -105,9 +105,10 @@ private void RecursivelyBuildUI(object obj, object parentComponent, RibbonPanel
105105
}
106106
}
107107
break;
108+
108109
case CommandComponentType.PushButton:
109110
case CommandComponentType.SmartButton:
110-
var pbData = new PushButtonData(component.UniqueId, component.Name, assemblyInfo.Location, component.UniqueId);
111+
var pbData = CreatePushButton(component, assemblyInfo);
111112
var btn = parentPanel?.AddItem(pbData) as PushButton;
112113
if (!string.IsNullOrEmpty(component.Tooltip))
113114
btn.ToolTip = component.Tooltip;
@@ -128,7 +129,7 @@ private void RecursivelyBuildUI(object obj, object parentComponent, RibbonPanel
128129
if (child is FileCommandComponent subCmd &&
129130
CommandComponentTypeExtensions.FromExtension(subCmd.Type) == CommandComponentType.PushButton)
130131
{
131-
var subData = new PushButtonData(subCmd.UniqueId, subCmd.Name, assemblyInfo.Location, subCmd.UniqueId);
132+
var subData = CreatePushButton(subCmd, assemblyInfo);
132133
splitBtn.AddPushButton(subData);
133134
}
134135
}
@@ -152,18 +153,27 @@ private PulldownButtonData CreatePulldown(
152153
if (pdBtn == null)
153154
return null;
154155

155-
156156
foreach (var child in component.Children ?? Enumerable.Empty<object>())
157157
{
158158
if (child is FileCommandComponent subCmd &&
159159
CommandComponentTypeExtensions.FromExtension(subCmd.Type) == CommandComponentType.PushButton)
160160
{
161-
var subData = new PushButtonData(subCmd.UniqueId, subCmd.Name, assemblyInfo.Location, subCmd.UniqueId);
161+
var subData = CreatePushButton(subCmd, assemblyInfo);
162162
pdBtn.AddPushButton(subData);
163163
}
164164
}
165165

166166
return pdData;
167167
}
168+
169+
private PushButtonData CreatePushButton(FileCommandComponent command, ExtensionAssemblyInfo assemblyInfo)
170+
{
171+
return new PushButtonData(
172+
command.UniqueId,
173+
command.Name,
174+
assemblyInfo.Location,
175+
command.UniqueId
176+
);
177+
}
168178
}
169179
}

dev/pyRevitLoader/pyRevitExtensionParser/ExtensionParser.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,10 @@ private static List<ParsedComponent> ParseComponents(string baseDir, string exte
8080
var namePart = Path.GetFileNameWithoutExtension(dir)
8181
.Replace(" ", "");
8282

83-
// Construct full ID path with hyphens
83+
// Construct full ID path with underscores
8484
var fullPath = string.IsNullOrEmpty(parentPath)
85-
? $"{extensionName}-{namePart}"
86-
: $"{parentPath}-{namePart}";
85+
? $"{extensionName}_{namePart}"
86+
: $"{parentPath}_{namePart}";
8787

8888
var children = ParseComponents(dir, extensionName, fullPath);
8989
var scriptPath = Path.Combine(dir, "script.py");

pyrevitlib/pyrevit/loader/sessionmgr.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -248,13 +248,17 @@ def _new_session():
248248
uimaker.reflow_pyrevit_ui()
249249

250250

251-
def load_session():
251+
def load_session(light=False):
252252
"""Handles loading/reloading of the pyRevit addin and extensions.
253253
254254
To create a proper ui, pyRevit extensions needs to be properly parsed and
255255
a dll assembly needs to be created. This function handles these tasks
256256
through interactions with .extensions, .loader.asmmaker, and .loader.uimaker.
257257
258+
Load session now takes a light parameter that will skip the assembly creation
259+
and UI creation. This is for the case when pyRevitAssemblyMaker.dll is
260+
used to create the assembly and UI
261+
258262
Examples:
259263
```python
260264
from pyrevit.loader.sessionmgr import load_session
@@ -282,7 +286,7 @@ def load_session():
282286
_perform_onsessionloadstart_ops()
283287

284288
# create a new session
285-
_new_session()
289+
_new_session() if not light else None
286290

287291
# perform post-load tasks
288292
_perform_onsessionloadcomplete_ops()

0 commit comments

Comments
 (0)