檢查專案的 bin 資料夾是否有 .dll。如果建置成功,我們可以將 .dll 加入 Dynamo。
Dynamo 資源庫中的自訂 RectangularGrids 節點
圖元區上的自訂節點
將 .dll 加入 Dynamo 的「加入」按鈕
修改自訂節點
在以上範例中,我們建立了一個相當簡單的節點,此節點除了 RectangularGrids 方法外沒有定義太多其他內容。但是,我們也許需要建立輸入埠的工具提示,或為節點提供類似標準 Dynamo 節點的摘要。將這些功能加入自訂節點,可以讓使用者更輕鬆地使用這些節點,尤其是當使用者想要在資源庫中搜尋這些節點時。
using Autodesk.DesignScript.Geometry;
using System.Collections.Generic;
namespace CustomNodes
{
public class Grids
{
public static List<Rectangle> RectangularGrid(int xCount, int yCount)
{
//The method for creating a rectangular grid will live in here
}
}
}
using Autodesk.DesignScript.Geometry;
using System.Collections.Generic;
namespace CustomNodes
{
public class Grids
{
public static List<Rectangle> RectangularGrid(int xCount, int yCount)
{
double x = 0;
double y = 0;
var pList = new List<Rectangle>();
for (int i = 0; i < xCount; i++)
{
y++;
x = 0;
for (int j = 0; j < yCount; j++)
{
x++;
Point pt = Point.ByCoordinates(x, y);
Vector vec = Vector.ZAxis();
Plane bP = Plane.ByOriginNormal(pt, vec);
Rectangle rect = Rectangle.ByWidthLength(bP, 1, 1);
pList.Add(rect);
}
}
return pList;
}
}
}
using Autodesk.DesignScript.Geometry;
using System.Collections.Generic;
namespace CustomNodes
{
public class Grids
{
/// <summary>
/// This method creates a rectangular grid from an X and Y count.
/// </summary>
/// <param name="xCount">Number of grid cells in the X direction</param>
/// <param name="yCount">Number of grid cells in the Y direction</param>
/// <returns>A list of rectangles</returns>
/// <search>grid, rectangle</search>
public static List<Rectangle> RectangularGrid(int xCount = 10, int yCount = 10)
{
double x = 0;
double y = 0;
var pList = new List<Rectangle>();
for (int i = 0; i < xCount; i++)
{
y++;
x = 0;
for (int j = 0; j < yCount; j++)
{
x++;
Point pt = Point.ByCoordinates(x, y);
Vector vec = Vector.ZAxis();
Plane bP = Plane.ByOriginNormal(pt, vec);
Rectangle rect = Rectangle.ByWidthLength(bP, 1, 1);
pList.Add(rect);
Point cPt = rect.Center();
}
}
return pList;
}
}
}
將延伸當作套件
將延伸當作套件
概述
Dynamo 延伸可以部署到 Package Manager,就像一般 Dynamo 節點資源庫一樣。如果安裝的套件包含視圖延伸,在 Dynamo 載入的執行時期會載入該延伸。您可以查看 Dynamo 主控台,以確認延伸已正確載入。
將任何組合檔放在 bin 資料夾中,將資訊清單檔案放在 extra 資料夾中。此資料夾中也可以放置任何其他資產。
範例資訊清單 .XML 檔案:
上傳
一旦您有包含上述子目錄的資料夾,即可推送 (上傳) 到 Package Manager。需要注意的一點是,您目前無法從 Dynamo Sandbox 發佈套件。這表示您需要使用 Dynamo Revit。在 Dynamo Revit 內瀏覽到「套件」=>「發佈新套件」。這會提示使用者登入要與套件關聯的 Autodesk 帳戶。
如果專案已成功建置,則專案的 bin 資料夾中會有一個名為 MyCustomNode 的 .dll。在此範例中,我們將專案的檔案路徑保留為 Visual Studio 的預設路徑:c:\users\username\documents\visual studio 2015\Projects。我們來看看專案的檔案結構。
bin 資料夾包含從 Visual Studio 建置的 .dll。
Visual Studio 專案檔。
現在我們可以開啟 Dynamo 並匯入 .dll。使用「加入」功能,瀏覽至專案的 bin 位置,然後選取要開啟的 .dll。
選取「加入」按鈕以匯入 .dll
瀏覽至專案位置。我們的專案位於 Visual Studio 的預設檔案路徑:C:\Users\username\Documents\Visual Studio 2015\Projects\MyCustomNode
如果在名為 MyCustomNode 的資源庫中建立了品類,則表示已成功匯入 .dll!但是,Dynamo 建立了兩個節點,而我們只想要一個節點。在下一節中,我們將說明發生這個狀況的原因,以及 Dynamo 如何讀取 .dll。
Dynamo 資源庫中的 MyCustomNode。「資源庫」品類由 .dll 名稱決定。
圖元區上的 SampleFunctions.MultiplyByTwo。
Dynamo 如何讀取類別和方法
Dynamo 載入 .dll 時,會將所有公用靜態方法顯示為節點。建構函式、方法和性質將分別轉換為「建立」、「動作」和「查詢」節點。在乘法範例中,MultiplyByTwo() 方法會變成 Dynamo 中的「動作」節點。這是因為節點已根據其方法和類別命名。
Core - Dynamo 的單位與系統測試基礎架構,包含以下資源庫:DSIronPython.dll、DynamoApplications.dll、DynamoCore.dll、DynamoInstallDetective.dll、DynamoUtilities.dll、ProtoCore.dll、VMDataBridge.dll
Tests - Dynamo 的單位與系統測試基礎架構,包含以下資源庫:DynamoCoreTests.dll、SystemTestServices.dll、TestServices.dll
DynamoCoreNodes - 用於為 Dynamo 建置核心節點的套件,包含以下資源庫:Analysis.dll、GeometryColor.dll、DSCoreNodes.dll
namespace MyCustomNode
{
public class SampleFunctions
{
public static double MultiplyByTwo(double inputNumber)
{
return inputNumber * 2.0;
}
}
}
namespace MyCustomNode
{
public class SampleFunctions
{
//The empty private constructor.
//This will be not imported into Dynamo.
private SampleFunctions() { }
//The public multiplication method.
//This will be imported into Dynamo.
public static double MultiplyByTwo(double inputNumber)
{
return inputNumber * 2.0;
}
}
}
為 Dynamo for Revit 開發
使用 GeometryPrimitiveConverter.cs 中的方法
DynamoRevit 程式碼資源庫中的 GeometryPrimitiveConverter 類別提供各種方法,在 Revit 和 Dynamo 幾何類型之間進行轉換。在與 Revit 模型互動的 Dynamo 腳本中處理幾何圖形時,這些方法非常有用。
方法品類
GeometryPrimitiveConverter.cs 中的方法可分為四個主要品類:
Proto 轉換為 Revit 類型:將 Dynamo (Proto) 類型轉換為 Revit 類型的方法。
Revit 轉換為 Proto 類型:將 Revit 類型轉換為 Dynamo (Proto) 類型的方法。
度與弳度:在度與弳度之間轉換的方法。
Proto 轉換為 Revit 類型
ToRevitBoundingBox
從 Dynamo 座標系統和兩個定義點 (最小值和最大值) 建立 Revit BoundingBoxXYZ。
-o, -O, --OpenFilePath 指示 Dynamo 開啟位於此路徑的指令檔案,並執行該檔案包含的指令。只有從 DynamoSandbox 執行時才支援此選項。
-c, -C, --CommandFilePath 指示 Dynamo 開啟位於此路徑的指令檔案,並執行該檔案包含的指令。只有從 DynamoSandbox 執行時才支援此選項。
-v, -V, --Verbose 指示 Dynamo 將其執行的所有演算結果輸出到指定路徑中的 XML 檔案。
-g, -G, --Geometry 指示 Dynamo 將所有演算結果中的幾何圖形輸出到此路徑的 JSON 檔案。
-h, -H, --help
namespace SampleLibraryUI.Examples
// Class Attribute
[NodeName("MyNodeModel")]
public class MyNewNodeModel : NodeModel
// or
// Constructor
public ButtonCustomNodeModel()
{
this.Name = "MyNodeModel";
}
SampleLibraryUI/Examples/MyNodeModel
// Class Attribute
[NodeCategory("NewSampleLibraryUI.Examples")]
// or
// Constructor
public ButtonCustomNodeModel()
{
this.Category = "NewSampleLibraryUI.Examples";
}
NewSampleLibraryUI/Examples/MyNodeModel
namespace MyZTLibrary
public class Utilities
{
public double doubleValue(double num)
{
return num * 2;
}
}
{
"Uuid": "85066088-1616-40b1-96e1-c33e685c6948",
"IsCustomNode": true,
"Category": "MyCustomNodes.Utilities.Actions",
"Description": "This is an example custom nodes.",
"Name": "doubleValue",
"ElementResolver": {
"ResolutionMap": {}
},...
<Workspace Version="1.3.0.0000" X="100" Y="100" zoom="1.0000000" Description="This is an example custom nodes." Category="MyCustomNodes.Utilities.Actions" Name="doubleValue" ID="85066088-1616-40b1-96e1-c33e685c6948">
namespace SampleLibraryUI.Examples
{
[NodeName("Drop Down Example")]
[NodeDescription("An example drop down node.")]
[IsDesignScriptCompatible]
[AlsoKnownAs("SampleLibraryUI.Examples.DropDownExample")]
public class DropDownExampleRENAMED : DSDropDownBase
{
...
}
{
指令行介面 (DynamoCLI) 是對 DynamoSandbox 的補充內容。它是一個 DOS/終端機指令行公用程式,設計來提供指令行引數方便執行 Dynamo。在一開始的實作中,它無法獨立執行,必須從 Dynamo 二進位檔所在的資料夾執行,因為它依賴與沙箱相同的核心 DLL。無法與其他 Dynamo 建置版本互通。
有四種方式可以執行 CLI:從 DOS 命令提示執行、從 DOS 批次檔執行,以及以 Windows 桌面捷徑執行 (其路徑修改為包括指定的指令行旗標)。DOS 檔規格可以是完整的或相對的,也支援對映的磁碟機和 URL 語法。它也可以使用 Mono 建置,並在 Linux 或 Mac 上從終端機執行。
公用程式支援 Dynamo 套件,但是您無法載入自訂節點 (dyf),只能載入獨立圖表 (dyn)。
在初步測試中,CLI 公用程式支援本土化的 Windows 版本,而且可以使用大寫的 Ascii 字元指定 filespec 引數。
可透過 DynamoCLI.exe 應用程式存取 CLI。此應用程式允許使用者或其他應用程式使用指令字串叫用 DynamoCLI.exe 與 Dynamo 演算模型互動。看起來可能會像下面這樣:
using IronPython.Hosting;
using Microsoft.Scripting.Hosting;
namespace PythonLibrary
{
public class PythonZT
{
// Unless a constructor is provided, Dynamo will automatically create one and add it to the library
// To avoid this, create a private constructor
private PythonZT() { }
// The method that executes the Python string
public static string executePyString(string pyString)
{
ScriptEngine engine = Python.CreateEngine();
ScriptScope scope = engine.CreateScope();
engine.Execute(pyString, scope);
// Return the value of the 'output' variable from the Python script below
var output = scope.GetVariable("output");
return (output);
}
}
}
using IronPython.Hosting;
using Microsoft.Scripting.Hosting;
using System.Collections.Generic;
using Autodesk.DesignScript.Runtime;
namespace PythonLibrary
{
public class PythonZT
{
private PythonZT() { }
[MultiReturn(new[] { "output1", "output2" })]
public static Dictionary<string, object> executePyString(string pyString)
{
ScriptEngine engine = Python.CreateEngine();
ScriptScope scope = engine.CreateScope();
engine.Execute(pyString, scope);
// Return the value of 'output1' from script
var output1 = scope.GetVariable("output1");
// Return the value of 'output2' from script
var output2 = scope.GetVariable("output2");
// Define the names of outputs and the objects to return
return new Dictionary<string, object> {
{ "output1", (output1) },
{ "output2", (output2) }
};
}
}
}
延伸的作者也可能需要進行一些潛在的變更 - 取決於他們在延伸中使用多少 Dynamo 核心 API。
一般封裝規則:
請勿將 Dynamo 或 Dynamo Revit .dll 與套件一起封裝。這些 dll 將由 Dynamo 載入。如果您組合的版本與使用者載入的版本不同 (例如您散發 dynamo core 1.3,但使用者在 dynamo 2.0 上執行套件),將會發生神秘的執行階段錯誤。這包括 dll,例如 DynamoCore.dll、DynamoServices.dll、DSCodeNodes.dll、ProtoGeometry.dll
如果可以避免,請勿將 newtonsoft.json.net 與套件一起封裝並散發。此 dll 也將由 Dynamo 2.x 載入。可能會發生與上述相同的問題。
先前,開發人員可以透過 SerializeCore 和 DeserializeCore 方法將特定模型資料序列化和還原序列化為 xml 文件。這些方法仍存在於 API 中,但在未來版本的 Dynamo 中將棄用 (在可找到範例)。現在實作 JSON.NET,NodeModel 衍生類別的 public 性質可以直接序列化為 .dyn 檔案。JSON.Net 提供多個屬性,可控制如何序列化性質。
using System;
using System.Collections.Generic;
using Dynamo.Graph.Nodes;
using CustomNodeModel.CustomNodeModelFunction;
using ProtoCore.AST.AssociativeAST;
using Autodesk.DesignScript.Geometry;
namespace CustomNodeModel.CustomNodeModel
{
[NodeName("RectangularGrid")]
[NodeDescription("An example NodeModel node that creates a rectangular grid. The slider randomly scales the cells.")]
[NodeCategory("CustomNodeModel")]
[InPortNames("xCount", "yCount")]
[InPortTypes("double", "double")]
[InPortDescriptions("Number of cells in the X direction", "Number of cells in the Y direction")]
[OutPortNames("Rectangles")]
[OutPortTypes("Autodesk.DesignScript.Geometry.Rectangle[]")]
[OutPortDescriptions("A list of rectangles")]
[IsDesignScriptCompatible]
public class GridNodeModel : NodeModel
{
private double _sliderValue;
public double SliderValue
{
get { return _sliderValue; }
set
{
_sliderValue = value;
RaisePropertyChanged("SliderValue");
OnNodeModified(false);
}
}
public GridNodeModel()
{
RegisterAllPorts();
}
public override IEnumerable<AssociativeNode> BuildOutputAst(List<AssociativeNode> inputAstNodes)
{
if (!HasConnectedInput(0) || !HasConnectedInput(1))
{
return new[] { AstFactory.BuildAssignment(GetAstIdentifierForOutputIndex(0), AstFactory.BuildNullNode()) };
}
var sliderValue = AstFactory.BuildDoubleNode(SliderValue);
var functionCall =
AstFactory.BuildFunctionCall(
new Func<int, int, double, List<Rectangle>>(GridFunction.RectangularGrid),
new List<AssociativeNode> { inputAstNodes[0], inputAstNodes[1], sliderValue });
return new[] { AstFactory.BuildAssignment(GetAstIdentifierForOutputIndex(0), functionCall) };
}
}
}
public ButtonCustomNodeModel()
{
// When you create a UI node, you need to do the
// work of setting up the ports yourself. To do this,
// you can populate the InPorts and the OutPorts
// collections with PortData objects describing your ports.
InPorts.Add(new PortModel(PortType.Input, this, new PortData("inputString", "a string value displayed on our button")));
// Nodes can have an arbitrary number of inputs and outputs.
// If you want more ports, just create more PortData objects.
OutPorts.Add(new PortModel(PortType.Output, this, new PortData("button value", "returns the string value displayed on our button")));
OutPorts.Add(new PortModel(PortType.Output, this, new PortData("window value", "returns the string value displayed in our window when button is pressed")));
// This call is required to ensure that your ports are
// properly created.
RegisterAllPorts();
// Listen for input port disconnection to trigger button UI update
this.PortDisconnected += ButtonCustomNodeModel_PortDisconnected;
// The arugment lacing is the way in which Dynamo handles
// inputs of lists. If you don't want your node to
// support argument lacing, you can set this to LacingStrategy.Disabled.
ArgumentLacing = LacingStrategy.Disabled;
// We create a DelegateCommand object which will be
// bound to our button in our custom UI. Clicking the button
// will call the ShowMessage method.
ButtonCommand = new DelegateCommand(ShowMessage, CanShowMessage);
// Setting our property here will trigger a
// property change notification and the UI
// will be updated to reflect the new value.
ButtonText = defaultButtonText;
WindowText = defaultWindowText;
}
// This constructor is called when opening a Json graph.
[JsonConstructor]
ButtonCustomNodeModel(IEnumerable<PortModel> inPorts, IEnumerable<PortModel> outPorts) : base(inPorts, outPorts)
{
this.PortDisconnected += ButtonCustomNodeModel_PortDisconnected;
ButtonCommand = new DelegateCommand(ShowMessage, CanShowMessage);
}
深入瞭解 Zero-Touch
瞭解如何建立 Zero-Touch 專案後,我們可以逐步瀏覽 Dynamo Github 上的 ZeroTouchEssentials 範例,更深入地瞭解建立節點的詳細資訊。
許多 Dynamo 的標準節點本質上是 Zero-Touch 節點,如上面大多數的 Math、Color 和 DateTime 節點。
除非您使用 Dynamo 2.5 版或更高版本,否則必須手動管理未從函數傳回的幾何圖形資源。在 Dynamo 2.5 和更高版本中,幾何圖形資源由系統內部處理,但是,如果您的使用案例較複雜,或者您必須在決定性時間減少消耗記憶體,您可能仍需要手動處置幾何圖形。Dynamo 引擎將處理從函數傳回的任何幾何圖形資源。透過以下方式可以手動處理未傳回的幾何圖形資源:
如果還原序列化因某些原因失敗,則可能會發生此情況。最好只序列化您需要的性質。我們可以對您不需要載入或儲存的複雜性質使用 [JsonIgnore] 加以忽略。例如 function pointer, delegate, action, 或 event 等性質。這些性質不應該序列化,因為它們通常無法還原序列化而導致執行階段錯誤。
進階 Dynamo 節點自訂
我們已經具備 ZeroTouch 的基礎知識,本節將深入探討自訂 Dynamo 節點來增強功能和使用者體驗的優點。透過增加警告訊息、資訊訊息和自訂圖示等功能,您可以建立更直覺、資訊量更大、視覺上更吸引人的節點。這些自訂功能不僅能協助使用者瞭解潛在的問題或最佳化其工作流程,還能讓您的節點脫穎而出,成為專業且容易使用的工具。
自訂節點是一種絕佳方式,能確保您的解決方案清晰、可靠,專門為滿足特定專案需求而量身打造。
使用 OnLogWarningMessage 產生自訂警告訊息
在 Dynamo 中,OnLogWarningMessage 方法提供一種將警告訊息直接記錄到 Dynamo 主控台的方法。這是一項強大的功能,尤其是對於 Zero Touch 節點,因為它允許開發人員在輸入或參數出現可能導致非預期行為的問題時提醒使用者。本指南將教您如何在任何 Zero Touch 節點中實施
在方法中加入 [MultiReturn(new[] { "string1", "string2", ... more strings here })] 屬性。字串會參考字典中的鍵,並成為輸出埠名稱。
從函數傳回 Dictionary<>,其中的鍵與屬性中的參數名稱相符:return new Dictionary<string, object>
例如:/// <summary>...</summary>
在 Visual Studio 中選取「Project > [Project] Properties > Build > Output」並勾選「Documentation file」以啟用 XML 文件
將建立輸出參數的文件
/// <returns name = "outputName">...</returns> 將建立多個輸出參數的文件
namespace ZeroTouchEssentials
{
public class ZeroTouchEssentials
{
// Set the method parameter to a default value
public static double MultiplyByTwo(double inputNumber = 2.0)
{
return inputNumber * 2.0;
}
}
}
using System.Collections.Generic;
using Autodesk.DesignScript.Runtime;
namespace ZeroTouchEssentials
{
public class ZeroTouchEssentials
{
[MultiReturn(new[] { "add", "mult" })]
public static Dictionary<string, object> ReturnMultiExample(double a, double b)
{
return new Dictionary<string, object>
{ "add", (a + b) },
{ "mult", (a * b) }
};
}
}
}
using Autodesk.DesignScript.Geometry;
namespace ZeroTouchEssentials
{
public class ZeroTouchEssentials
{
/// <summary>
/// This method demonstrates how to use a native geometry object from Dynamo
/// in a custom method
/// </summary>
/// <param name="curve">Input Curve. This can be of any type deriving from Curve, such as NurbsCurve, Arc, Circle, etc</param>
/// <returns>The twice the length of the Curve </returns>
/// <search>example,curve</search>
public static double DoubleLength(Curve curve)
{
return curve.Length * 2.0;
}
}
}
namespace ZeroTouchEssentials
{
public class ZeroTouchEssentials
{
private double _a;
private double _b;
// Make the constructor internal
internal ZeroTouchEssentials(double a, double b)
{
_a = a;
_b = b;
}
// The static method that Dynamo will convert into a Create node
public static ZeroTouchEssentials ByTwoDoubles(double a, double b)
{
return new ZeroTouchEssentials(a, b);
}
}
}
using Autodesk.DesignScript.Geometry;
namespace ZeroTouchEssentials
{
public class ZeroTouchEssentials
{
// "Autodesk.DesignScript.Geometry.Curve" is specifying the type of geometry input,
// just as you would specify a double, string, or integer
public static double DoubleLength(Autodesk.DesignScript.Geometry.Curve curve)
{
return curve.Length * 2.0;
}
}
}
public class SomeGenericClass<T>
{
public SomeGenericClass()
{
Console.WriteLine(typeof(T).ToString());
}
}
public class SomeWrapper
{
public object wrapped;
public SomeWrapper(SomeGenericClass<double> someConstrainedType)
{
Console.WriteLine(this.wrapped.GetType().ToString());
}
}
using System;
using System.Collections.Generic;
using Dynamo.Graph.Nodes;
using CustomNodeModel.CustomNodeModelFunction;
using ProtoCore.AST.AssociativeAST;
using Autodesk.DesignScript.Geometry;
namespace CustomNodeModel.CustomNodeModel
{
[NodeName("RectangularGrid")]
[NodeDescription("An example NodeModel node that creates a rectangular grid. The slider randomly scales the cells.")]
[NodeCategory("CustomNodeModel")]
[InPortNames("xCount", "yCount")]
[InPortTypes("double", "double")]
[InPortDescriptions("Number of cells in the X direction", "Number of cells in the Y direction")]
[OutPortNames("Rectangles")]
[OutPortTypes("Autodesk.DesignScript.Geometry.Rectangle[]")]
[OutPortDescriptions("A list of rectangles")]
[IsDesignScriptCompatible]
public class GridNodeModel : NodeModel
{
private double _sliderValue;
public double SliderValue
{
get { return _sliderValue; }
set
{
_sliderValue = value;
RaisePropertyChanged("SliderValue");
OnNodeModified(false);
}
}
public GridNodeModel()
{
RegisterAllPorts();
}
public override IEnumerable<AssociativeNode> BuildOutputAst(List<AssociativeNode> inputAstNodes)
{
if (!HasConnectedInput(0) || !HasConnectedInput(1))
{
return new[] { AstFactory.BuildAssignment(GetAstIdentifierForOutputIndex(0), AstFactory.BuildNullNode()) };
}
var sliderValue = AstFactory.BuildDoubleNode(SliderValue);
var functionCall =
AstFactory.BuildFunctionCall(
new Func<int, int, double, List<Rectangle>>(GridFunction.RectangularGrid),
new List<AssociativeNode> { inputAstNodes[0], inputAstNodes[1], sliderValue });
return new[] { AstFactory.BuildAssignment(GetAstIdentifierForOutputIndex(0), functionCall) };
}
}
}
using Autodesk.DesignScript.Geometry;
using Autodesk.DesignScript.Runtime;
using System;
using System.Collections.Generic;
namespace CustomNodeModel.CustomNodeModelFunction
{
[IsVisibleInDynamoLibrary(false)]
public class GridFunction
{
[IsVisibleInDynamoLibrary(false)]
public static List<Rectangle> RectangularGrid(int xCount = 10, int yCount = 10, double rand = 1)
{
double x = 0;
double y = 0;
Point pt = null;
Vector vec = null;
Plane bP = null;
Random rnd = new Random(2);
var pList = new List<Rectangle>();
for (int i = 0; i < xCount; i++)
{
y++;
x = 0;
for (int j = 0; j < yCount; j++)
{
double rNum = rnd.NextDouble();
double scale = rNum * (1 - rand) + rand;
x++;
pt = Point.ByCoordinates(x, y);
vec = Vector.ZAxis();
bP = Plane.ByOriginNormal(pt, vec);
Rectangle rect = Rectangle.ByWidthLength(bP, scale, scale);
pList.Add(rect);
}
}
pt.Dispose();
vec.Dispose();
bP.Dispose();
return pList;
}
}
}
using Dynamo.Controls;
using Dynamo.Wpf;
namespace CustomNodeModel.CustomNodeModel
{
public class CustomNodeModelView : INodeViewCustomization<GridNodeModel>
{
public void CustomizeView(GridNodeModel model, NodeView nodeView)
{
var slider = new Slider();
nodeView.inputGrid.Children.Add(slider);
slider.DataContext = model;
}
public void Dispose()
{
}
}
}
在 Dynamo 中,DynamoServices 名稱空間中的 OnLogInfoMessage 可讓開發人員將資訊訊息直接記錄到 Dynamo 的主控台。這有助於確認運算成功、傳達進度或提供有關節點動作的其他見解。本指南將教您如何在任何 Zero Touch 節點中加入 OnLogInfoMessage,以增強回饋並改善使用者體驗。
public static List<Rectangle> CreateGrid(int xCount, int yCount)
{
// Check if xCount and yCount are positive
if (xCount <= 0 || yCount <= 0)
{
LogWarningMessageEvents.OnLogWarningMessage("Grid count values must be positive integers.");
return new List<Rectangle>(); // Return an empty list if inputs are invalid
}
// Proceed with grid creation...
}
using Autodesk.DesignScript.Geometry;
using DynamoServices;
namespace CustomNodes
{
public class Grids
{
// The empty private constructor.
// This will not be imported into Dynamo.
private Grids() { }
/// <summary>
/// This method creates a rectangular grid from an X and Y count.
/// </summary>
/// <param name="xCount">Number of grid cells in the X direction</param>
/// <param name="yCount">Number of grid cells in the Y direction</param>
/// <returns>A list of rectangles</returns>
/// <search>grid, rectangle</search>
public static List<Rectangle> RectangularGrid(int xCount = 10, int yCount = 10)
{
// Check for valid input values
if (xCount <= 0 || yCount <= 0)
{
// Log a warning message if the input values are invalid
LogWarningMessageEvents.OnLogWarningMessage("Grid count values must be positive integers.");
return new List<Rectangle>(); // Return an empty list if inputs are invalid
}
double x = 0;
double y = 0;
var pList = new List<Rectangle>();
for (int i = 0; i < xCount; i++)
{
y++;
x = 0;
for (int j = 0; j < yCount; j++)
{
x++;
Point pt = Point.ByCoordinates(x, y);
Vector vec = Vector.ZAxis();
Plane bP = Plane.ByOriginNormal(pt, vec);
Rectangle rect = Rectangle.ByWidthLength(bP, 1, 1);
pList.Add(rect);
Point cPt = rect.Center();
}
}
return pList;
}
}
}
public static Polygon CreatePolygonFromPoints(List<Point> points)
{
if (points == null || points.Count < 3)
{
LogWarningMessageEvents.OnLogWarningMessage("Point list cannot be null or have fewer than three points.");
return null; // Return null if the input list is invalid
}
// Proceed with polygon creation...
}
public static void ProcessFile(string filePath)
{
if (!filePath.EndsWith(".csv"))
{
LogWarningMessageEvents.OnLogWarningMessage("Only CSV files are supported.");
return;
}
// Proceed with file processing...
}
LogWarningMessageEvents.OnLogInfoMessage("Your info message here.");
public static List<Rectangle> CreateGrid(int xCount, int yCount)
{
var pList = new List<Rectangle>();
// Grid creation code here...
// Confirm successful grid creation
LogWarningMessageEvents.OnLogInfoMessage($"Successfully created a grid with dimensions {xCount}x{yCount}.");
return pList;
}
using Autodesk.DesignScript.Geometry;
using DynamoServices;
namespace CustomNodes
{
public class Grids
{
// The empty private constructor.
// This will not be imported into Dynamo.
private Grids() { }
/// <summary>
/// This method creates a rectangular grid from an X and Y count.
/// </summary>
/// <param name="xCount">Number of grid cells in the X direction</param>
/// <param name="yCount">Number of grid cells in the Y direction</param>
/// <returns>A list of rectangles</returns>
/// <search>grid, rectangle</search>
public static List<Rectangle> RectangularGrid(int xCount = 10, int yCount = 10)
{
double x = 0;
double y = 0;
var pList = new List<Rectangle>();
for (int i = 0; i < xCount; i++)
{
y++;
x = 0;
for (int j = 0; j < yCount; j++)
{
x++;
Point pt = Point.ByCoordinates(x, y);
Vector vec = Vector.ZAxis();
Plane bP = Plane.ByOriginNormal(pt, vec);
Rectangle rect = Rectangle.ByWidthLength(bP, 1, 1);
pList.Add(rect);
Point cPt = rect.Center();
}
}
// Log an info message indicating the grid was successfully created
LogWarningMessageEvents.OnLogInfoMessage($"Successfully created a grid with dimensions {xCount}x{yCount}.");
return pList;
}
}
}
public static List<Point> ProcessPoints(List<Point> points)
{
var processedPoints = new List<Point>();
foreach (var point in points)
{
// Process each point...
processedPoints.Add(point);
}
// Log info about the count of processed points
LogWarningMessageEvents.OnLogInfoMessage($"{processedPoints.Count} points were processed successfully.");
return processedPoints;
}
public static void ExportData(string filePath, List<string> data)
{
// Code to write data to the specified file path...
// Log the file path used for export
LogWarningMessageEvents.OnLogInfoMessage($"Data exported successfully to {filePath}.");
}
由於 Dynamo 3.x 現在於 .NET8 執行階段上執行,因此針對 Dynamo 2.x 建置的套件 (使用 .NET48) 不保證可在 Dynamo 3.x 中運作。嘗試在 Dynamo 3.x 中下載從低於 3.0 的 Dynamo 版本發佈的套件時,您會收到警告,指出套件來自舊版 Dynamo。
這不表示套件無法運作。這只是警告您可能有相容性問題。一般而言,建議您檢查是否有專為 Dynamo 3.x 建置的較新版本。
載入套件時,您也可能會在 Dynamo 記錄檔中看到此類警告。如果所有一切都正常運作,即可忽略警告。
在 Dynamo 2.x 中使用 Dynamo 3.x 套件
針對 Dynamo 3.x 建置的套件 (使用.Net8) 在 Dynamo 2.x上很可能無法運作。若使用舊版,下載為更新版 Dynamo 建置的套件時,您也會看到警告。
延伸
延伸是 Dynamo 生態系統中功能強大的開發工具。這些工具可讓開發人員根據 Dynamo 的互動和邏輯驅動自訂功能。延伸可以分為兩個主要品類:延伸和視圖延伸。如同字面意思,視圖延伸架構可讓您透過註冊自訂功能表項目的方式來延伸 Dynamo 使用者介面。一般延伸則以非常相似的方式 (除了使用者介面之外) 運作。例如,我們可以建置一個延伸,將特定資訊記錄到 Dynamo 主控台。此情況不需要自訂使用者介面,因此也可以使用延伸完成。
using System;
using System.Windows;
using System.Windows.Controls;
using Dynamo.Wpf.Extensions;
namespace SampleViewExtension
{
public class SampleViewExtension : IViewExtension
{
private MenuItem sampleMenuItem;
public void Dispose()
{
}
public void Startup(ViewStartupParams p)
{
}
public void Loaded(ViewLoadedParams p)
{
// Save a reference to your loaded parameters.
// You'll need these later when you want to use
// the supplied workspaces
sampleMenuItem = new MenuItem {Header = "Show View Extension Sample Window"};
sampleMenuItem.Click += (sender, args) =>
{
var viewModel = new SampleWindowViewModel(p);
var window = new SampleWindow
{
// Set the data context for the main grid in the window.
MainGrid = { DataContext = viewModel },
// Set the owner of the window to the Dynamo window.
Owner = p.DynamoWindow
};
window.Left = window.Owner.Left + 400;
window.Top = window.Owner.Top + 200;
// Show a modeless window.
window.Show();
};
p.AddExtensionMenuItem(sampleMenuItem);
}
public void Shutdown()
{
}
public string UniqueId
{
get
{
return Guid.NewGuid().ToString();
}
}
public string Name
{
get
{
return "Sample View Extension";
}
}
}
}
using System;
using Dynamo.Core;
using Dynamo.Extensions;
using Dynamo.Graph.Nodes;
namespace SampleViewExtension
{
public class SampleWindowViewModel : NotificationObject, IDisposable
{
private string activeNodeTypes;
private ReadyParams readyParams;
// Displays active nodes in the workspace
public string ActiveNodeTypes
{
get
{
activeNodeTypes = getNodeTypes();
return activeNodeTypes;
}
}
// Helper function that builds string of active nodes
public string getNodeTypes()
{
string output = "Active nodes:\n";
foreach (NodeModel node in readyParams.CurrentWorkspaceModel.Nodes)
{
string nickName = node.Name;
output += nickName + "\n";
}
return output;
}
public SampleWindowViewModel(ReadyParams p)
{
readyParams = p;
p.CurrentWorkspaceModel.NodeAdded += CurrentWorkspaceModel_NodesChanged;
p.CurrentWorkspaceModel.NodeRemoved += CurrentWorkspaceModel_NodesChanged;
}
private void CurrentWorkspaceModel_NodesChanged(NodeModel obj)
{
RaisePropertyChanged("ActiveNodeTypes");
}
public void Dispose()
{
readyParams.CurrentWorkspaceModel.NodeAdded -= CurrentWorkspaceModel_NodesChanged;
readyParams.CurrentWorkspaceModel.NodeRemoved -= CurrentWorkspaceModel_NodesChanged;
}
}
}
using System.Windows;
namespace SampleViewExtension
{
/// <summary>
/// Interaction logic for SampleWindow.xaml
/// </summary>
public partial class SampleWindow : Window
{
public SampleWindow()
{
InitializeComponent();
}
}
}
<ViewExtensionDefinition>
<AssemblyPath>C:\Users\username\Documents\Visual Studio 2015\Projects\SampleViewExtension\SampleViewExtension\bin\Debug\SampleViewExtension.dll</AssemblyPath>
<TypeName>SampleViewExtension.SampleViewExtension</TypeName>
</ViewExtensionDefinition>
針對 Dynamo 4.x 更新套件與 Dynamo 資料庫
簡介
本部分包含將圖表、套件和資源庫移轉至 Dynamo 4.x 時,可能會遇到之問題相關資訊。Dynamo 4.0 引入:
顯著的效能改進
穩定性和錯誤修正更新
將程式碼庫現代化
移除先前在 1.x 中標記為舊型的 API
主要執行階段從 .NET 8 更新到 .NET 10
PythonNet 3 現在是所有新 Python 節點的預設 Python 引擎
.NET 10 移轉工作確保 Dynamo 與 Microsoft 的技術路線圖保持一致,遠遠早於 2026 年 11 月 .NET 8 的終止支援。
當您啟動 Dynamo 4.0 時,系統會要求您更新至 .NET 10 (如果尚未更新)。套件作者必須將其專案更新為目標 .NET 10,以確保完全相容。
所有在 Dynamo 4.0+ 中建立的新 Python 節點都從 PythonNet3 開始。不須擔心向下相容性:對於在多版本產品 (例如,Revit 或 Civil 3D 2025/2026) 中工作的使用者,請在 Dynamo 3.3-3.6 中安裝 PythonNet3 引擎套件以維持相容性。您可以在這裡找到更多資訊。
在 1.x 中標記為舊型的 API 與節點在 Dynamo 4.0 中已移除。您可以參考這裡完整的變更清單。
套件相容性
在 Dynamo 4.x 中使用 Dynamo 2.x 和 3.x 套件
由於 Dynamo 4.x 現在於 .NET 10 執行階段執行,因此針對 Dynamo 2.x 建置的套件 (使用 .NET48) 和 Dynamo 3.x (使用 .NET 8) 不保證可在 Dynamo 4.x 中運作。嘗試在 Dynamo 4.x 中下載從低於 4.0 的 Dynamo 版本發佈的套件時,您會收到警告,指出套件來自舊版 Dynamo。
這不表示套件無法運作。這只是警告您可能有相容性問題。一般而言,建議您檢查是否有專為 Dynamo 4.x 建置的較新版本。
載入套件時,您也可能會在 Dynamo 記錄檔中看到此類警告。如果所有一切都正常運作,即可忽略警告。
在 Dynamo 2.x 中使用 Dynamo 4.x 套件
針對 Dynamo 4.x 建置的套件 (使用.Net 10) 在 Dynamo 2.x 上很可能無法運作。當您嘗試在 Dynamo 2.x 中安裝針對 Dynamo 4.x 建置的套件時,您也會看到以下警告。
在 Dynamo 3.x 中使用 Dynamo 4.x 套件
針對 Dynamo 4.x 建置的套件 (使用 .NET 10) 可能可以在 Dynamo 3.x 上運作 (但套件中使用的所有 API 都必須在 .NET 8 中)。但並不保證套件能夠運作。當您嘗試在 Dynamo 3.x 中安裝針對 Dynamo 4.x 建置的套件時,您也會看到以下警告。
public class Application
{
string m_sProdID = "SomeNamespace.Application";
public dynamic m_oApp = null; // Use dynamic so you do not need the PIA types
public Application()
{
this.GetApplication();
}
internal void GetApplication()
{
try
{
m_oApp = System.Runtime.InteropServices.Marshal.GetActiveObject(m_sProdID);
}
catch (Exception /*ex*/)
{}
}
}
}
Dynamo 整合
您已經找到 Dynamo 視覺程式設計語言的整合文件。
本指南將探討如何在應用程式中主控 Dynamo 的各種面向,讓使用者能夠使用視覺程式設計與應用程式互動。
目前,每個追蹤字串都使用 gzip 壓縮和 base64 編碼進行序列化。這是對舊式 SOAP xml 格式設定的改進。
相容性
在 Dynamo 3.0 之前的版本中儲存的追蹤物件使用 SOAP 儲存,因此在較新版本中不支援。先前儲存的元素繫結資料將會忽略,並在 Dynamo 3.0 及更高版本中顯示以下訊息。下次執行並儲存工作區時,將儲存元素繫結資料。
ElementBinding 預設應該開啟嗎?
有一些使用案例不需要元素繫結。例如一個使用案例是,一位資深 Dynamo 使用者在開發一個程式,要執行多次以產生隨機分組元素。程式的目的就是每次執行程式時都要額外建立元素。如果沒有停止元素繫結運作的解決方法,這個使用案例就不容易達成。也許可以在整合層級就停用 elementBinding- 但這可能應該是核心 Dynamo 功能。目前還不清楚此功能應該引入的層級:節點層級?Callsite 層級?整個 Dynamo 工作階段?工作區?等等。
如果基於某些原因,也必須將套件發佈到 Package Manager,您必須先發佈缺少這些文化子目錄的套件版本,然後使用 DynamoUI publish package version 發佈新版本的套件。在 Dynamo 中上傳新版本的作業,不能刪除您使用 Windows 檔案總管手動加入的 /bin 下的資料夾和檔案。在 Dynamo 中的套件上傳程序未來將會更新,以便處理本地化檔案的需求。
public static string GetTraceData(string key)
///Returns the data that is bound to a particular key
public static void SetTraceData(string key, string value)
///Set the data bound to a particular key
public IDictionary<Guid, List<CallSite.RawTraceData>>
GetTraceDataForNodes(IEnumerable<Guid> nodeGuids, Executable executable)
/*
* After a graph update, Dynamo typically disposes of all
* objects created during the graph update. But what if there are
* objects which are expensive to re-create, or which have other
* associations in a host application? You wouldn't want those those objects
* re-created on every graph update. For example, you might
* have an external database whose records contain data which needs
* to be re-applied to an object when it is created in Dynamo.
* In this example, we use a wrapper class, TraceExampleWrapper, to create
* TraceExampleItem objects which are stored in a static dictionary
* (they could be stored in a database as well). On subsequent graph updates,
* the objects will be retrieved from the data store using a trace id stored
* in the trace cache.
*/
[IsVisibleInDynamoLibrary(false)]
public class TraceableId
{
}
private void InitWall(Curve curve, Autodesk.Revit.DB.WallType wallType, Autodesk.Revit.DB.Level baseLevel, double height, double offset, bool flip, bool isStructural)
{
// This creates a new wall and deletes the old one
TransactionManager.Instance.EnsureInTransaction(Document);
//Phase 1 - Check to see if the object exists and should be rebound
var wallElem =
ElementBinder.GetElementFromTrace<Autodesk.Revit.DB.Wall>(Document);
bool successfullyUsedExistingWall = false;
//There was a modelcurve, try and set sketch plane
// if you can't, rebuild
if (wallElem != null && wallElem.Location is Autodesk.Revit.DB.LocationCurve)
{
var wallLocation = wallElem.Location as Autodesk.Revit.DB.LocationCurve;
<SNIP>
if(!CurveUtils.CurvesAreSimilar(wallLocation.Curve, curve))
wallLocation.Curve = curve;
<SNIP>
}
var wall = successfullyUsedExistingWall ? wallElem :
Autodesk.Revit.DB.Wall.Create(Document, curve, wallType.Id, baseLevel.Id, height, offset, flip, isStructural);
InternalSetWall(wall);
TransactionManager.Instance.TransactionTaskDone();
// delete the element stored in trace and add this new one
ElementBinder.CleanupAndSetElementForTrace(Document, InternalWall);
}