diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 69736fe..0ee9f05 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -3,22 +3,46 @@ name: build on: [push] jobs: - build: - name: windows-ironpython + build_cpy_ghuser_components: runs-on: windows-latest steps: - uses: actions/checkout@v2 - uses: NuGet/setup-nuget@v1.0.5 + + - name: Install CPython and pythonnet package + run: | + choco install python --version=3.9.10 + python -m pip install pythonnet==3.0.3 + + - name: Run + uses: ./ + with: + source: examples/cpy + target: build + interpreter: cpython + + - uses: actions/upload-artifact@v2 + with: + name: cpy_ghuser-components + path: build + + build_ipy_ghuser_components: + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + - uses: NuGet/setup-nuget@v1.0.5 + - name: Install IronPython run: | choco install ironpython --version=2.7.8.1 - - name: Install dependencies - run: | - nuget install Grasshopper -OutputDirectory ./lib -source https://api.nuget.org/v3/index.json + - name: Run - run: | - ipy componentize.py examples build + uses: ./ + with: + source: examples/ipy + target: build + - uses: actions/upload-artifact@v2 with: - name: ghuser-components - path: build + name: ipy_ghuser-components + path: build \ No newline at end of file diff --git a/.gitignore b/.gitignore index b6e4761..6881b55 100644 --- a/.gitignore +++ b/.gitignore @@ -127,3 +127,6 @@ dmypy.json # Pyre type checker .pyre/ + +# avoid temp folder +temp/ \ No newline at end of file diff --git a/README.md b/README.md index 151fa1f..06db679 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # GHPython Componentizer -> A github action to make Grasshopper development 164% [1] version-control friendlier and 82% more pleasant. +> A github action to make Grasshopper development 165% [1] version-control friendlier and 83% more pleasant. -Imagine if you could write your grasshopper components in Python code in an actual text file with a powerful editor? +Imagine if you could write your grasshopper components in Python code (both IronPython for RhinoV7 and less, or CPython for RhinoV8) in an actual text file with a powerful editor? Git wouldn't hate you and life would be so much beautiful. 🐵 Well, here's an action for you then! 🦸‍♀️ @@ -14,25 +14,29 @@ Well, here's an action for you then! 🦸‍♀️ ### Usage from Github Actions The recommended way to use this tool is as a Github Action. -It needs to be run on a windows runner and IronPython/NuGet need to be pre-installed. +It needs to be run on a windows runner and IronPython/NuGet or Python3/pythonnet/Nuget depending of which component you want to build, needs to be pre-installed. Copy the following workflow code into a `.github/workflows/main.yml` file in your repository. Make sure you have the components definition (see below for details) stored in a source folder. Replace the `source` and `target` to match your folder structure. +To specify the interpreter to use, you can define the action parameter `interpreter` to either `ironpython` or `python3` (by default it is `ironpython`). + +For IronPython (RhinoV7 and less): ```yaml on: [push] jobs: - build_ghuser_components: + build_ipy_ghuser_components: runs-on: windows-latest - name: Build components steps: - uses: actions/checkout@v2 - uses: NuGet/setup-nuget@v1.0.5 + - name: Install IronPython run: | choco install ironpython --version=2.7.8.1 + - uses: compas-dev/compas-actions.ghpython_components@v2 with: source: components @@ -43,28 +47,60 @@ jobs: # upload them as artifacts: - uses: actions/upload-artifact@v2 with: - name: ghuser-components + name: ipy_ghuser-components path: build +``` +For Python3 (RhinoV8): + +```yaml +on: [push] + +jobs: + build_cpy_ghuser_components: + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + - uses: NuGet/setup-nuget@v1.0.5 + + - name: Install CPython and pythonnet package + run: | + choco install python --version=3.9.10 + python -m pip install pythonnet==3.0.3 + + - uses: compas-dev/compas-actions.ghpython_components@v2 + with: + source: components + target: build + interpreter: cpython # optional, defaults to ironpython + + - uses: actions/upload-artifact@v2 + with: + name: cpy_ghuser-components + path: build ``` + Commit, push and enjoy! 🍿 ### Usage on the command line Alternatively, you can also use this tool directly from the command line. -Make sure to have IronPython installed and the `GH_IO.dll` assembly available. +Make sure to have IronPython or Python3/pythonnet installed and the `GH_IO.dll` assembly available. Then start the script pointing it to a source and target folder, e.g.: - ipy componentize.py examples build + ipy componentize_ipy.py examples/ipy build + python componentize_cpy.py examples/cpy build Optionally, tag it with a version: - ipy componentize.py examples build --version 0.1.2 + ipy componentize_ipy.py examples/ipy build --version 0.1.2 + python componentize_cpy.py examples/cpy build --version 0.1.2 An optional name prefix can help tell components apart from other similarly named ones: - ipy componentize.py examples build --prefix "(PACKAGE-NAME)" + ipy componentize_ipy.py examples/ipy build --prefix "(PACKAGE-NAME)" + python componentize_cpy.py examples/cpy build --prefix "(PACKAGE-NAME)" ## How to create components @@ -96,8 +132,7 @@ An alternative is to include them in your packaging steps, e.g. calling `python ## Python code -* Supports both procedural and GH_Component SDK modes (see `isAdvancedMode` in metadata) -* Supports a small set of templated variables that can be used in code: +Supports a small set of templated variables that can be used in code: * `{{version}}`: Gets replaced with the version, if specified in the command-line. * `{{name}}`: Gets replaced with the name of the component as defined in the metadata file. * `{{ghuser_name}}`: Gets replaced with the name of the `.ghuser` file being generated. @@ -120,10 +155,11 @@ An alternative is to include them in your packaging steps, e.g. calling `python * `128`: Expose the object in the seventh section on the toolbar. * `instanceGuid`: **(optional)** Statically define a GUID for this instance. Defaults to a new Guid. * `ghpython` - * `hideOutput`: **(optional)** Defines whether to hide or not `out` output parameter. Defaults to `True`. - * `hideInput`: **(optional)** Defines whether to hide or not the `code` input parameter. Defaults to `True`. - * `isAdvancedMode`: **(optional)** Defines whether the script is in advanced mode (aka GH_Component SDK mode) or procedural mode. Defaults to `False`. - * `marshalOutGuids`: **(optional)** Defines whether output Guids will be looked up or not. Defaults to `True`. Change to `False` to preserve output Guids. + * `hideOutput`: **(optional ⚠️ only IronPython)** Defines whether to hide or not `out` output parameter. Defaults to `True`. + * `hideInput`: **(optional ⚠️ only IronPython)** Defines whether to hide or not the `code` input parameter. Defaults to `True`. + * `isAdvancedMode`: **(optional ⚠️ only IronPython)** Defines whether the script is in advanced mode (aka GH_Component SDK mode) or procedural mode. Defaults to `False`. + * `marshalOutGuids`: **(optional ⚠️ only IronPython)** Defines whether output Guids will be looked up or not. Defaults to `True`. Change to `False` to preserve output Guids. + * `marshalGuids`: **(optional ⚠️ only CPython)** Defines whether input Guids will be looked up or not. Defaults to `True`. Change to `False` to preserve input Guids. * `iconDisplay`: **(optional)** Defines whether to display the icon or not. Defaults to `0`. * `0` : Application setting * `1` : Text display diff --git a/action.yml b/action.yml index a24768c..2628e73 100644 --- a/action.yml +++ b/action.yml @@ -10,15 +10,29 @@ inputs: prefix: description: 'Add this prefix to the name of each generated component' required: false + interpreter: + description: 'Python interpreter to use: ironpython, or cpython' + required: false + default: 'ironpython' + runs: using: 'composite' steps: - - run: nuget install Grasshopper -OutputDirectory ./lib -source https://api.nuget.org/v3/index.json + - name: Install Grasshopper + run: nuget install Grasshopper -OutputDirectory ./lib -source https://api.nuget.org/v3/index.json shell: pwsh - - run: | - $command="ipy" - $params="${{ github.action_path }}/componentize.py", "${{ inputs.source }}", "${{ inputs.target }}", "--ghio", "./lib" - $prefix="${{ inputs.prefix }}" + + - name: Launch componentizer + run: | + if ("${{ inputs.interpreter }}" -eq "cpython") { + $command="python" + $componentizer="${{ github.action_path }}/componentize_cpy.py" + } else { + $command="ipy" + $componentizer="${{ github.action_path }}/componentize_ipy.py" + } + $params=$componentizer, "${{ inputs.source }}", "${{ inputs.target }}", "--ghio", "./lib" + $prefix="${{ inputs.prefix }}" if( $prefix ) { $params=$params + "--prefix", "$prefix" diff --git a/componentize_cpy.py b/componentize_cpy.py new file mode 100644 index 0000000..cdffe82 --- /dev/null +++ b/componentize_cpy.py @@ -0,0 +1,408 @@ +import argparse +import base64 +import json +import os +import re +import sys +import tempfile +import urllib.request, urllib.parse, urllib.error +import zipfile +from io import BytesIO + +import clr +import System +import System.IO + + +SCRIPT_COMPONENT_GUID = System.Guid("c9b2d725-6f87-4b07-af90-bd9aefef68eb") +CPY_VER = "3.-1" +TEMPLATE_VER = re.compile("{{version}}") +TEMPLATE_NAME = re.compile("{{name}}") +TEMPLATE_GHUSER_NAME = re.compile("{{ghuser_name}}") + +TYPES_MAP = dict( + none="6a184b65-baa3-42d1-a548-3915b401de53", + ghdoc="1c282eeb-dd16-439f-94e4-7d92b542fe8b", + float="9d51e32e-c038-4352-9554-f4137ca91b9a", + bool="d60527f5-b5af-4ef6-8970-5f96fe412559", + int="48d01794-d3d8-4aef-990e-127168822244", + complex="309690df-6229-4774-91bb-b1c9c0bfa54d", + str="3aceb454-6dbd-4c5b-9b6b-e71f8c1cdf88", + datetime="09bcf900-fe83-4efa-8d32-33d89f7a3e66", + guid="5325b8e1-51d7-4d36-837a-d98394626c35", + color="24b1d1a3-ab79-498c-9e44-c5b14607c4d3", + point="e1937b56-b1da-4c12-8bd8-e34ee81746ef", + vector="15a50725-e3d3-4075-9f7c-142ba5f40747", + plane="3897522d-58e9-4d60-b38c-978ddacfedd8", + interval="589748aa-e558-4dd9-976f-78e3ab91fc77", + uvinterval="74c906f3-db02-4cea-bd58-de375cb5ae73", + box="f29cb021-de79-4e63-9f04-fc8e0df5f8b6", + transform="c4b38e4c-21ff-415f-a0d1-406d282428dd", + line="f802a8cd-e699-4a94-97ea-83b5406271de", + circle="3c5409a1-3293-4181-a6fa-c24c37fc0c32", + arc="9c80ec18-b48c-41b0-bc6e-cd93d9c916aa", + polyline="66fa617b-e3e8-4480-9f1e-2c0688c1d21b", + rectangle="83da014b-a550-4bf5-89ff-16e54225bd5d", + curve="9ba89ec2-5315-435f-a621-b66c5fa2f301", + mesh="794a1f9d-21d5-4379-b987-9e8bbf433912", + surface="f4070a37-c822-410f-9057-100d2e22a22d", + subd="20f4ca9c-6c90-4fd6-ba8a-5bf9ca79db08", + brep="2ceb0405-fdfe-403d-a4d6-8786da45fb9d", + geometrybase="c37956f4-d39c-49c7-af71-1e87f8031b26" +) + +EXPOSURE = dict(valid=set([-1, 2, 4, 8, 16, 32, 64, 128]), default=2) +ACCESS = dict(valid=set([0, 1, 2]), map=dict(item=0, list=1, tree=2), default=0) +PARAM_TYPE = dict( + valid=set(TYPES_MAP.values()), map=TYPES_MAP, default=TYPES_MAP["ghdoc"] +) +WIRE_DISPLAY = dict( + valid=set([0, 1, 2]), map=dict(default=0, faint=1, hidden=2), default=0 +) + + +def fetch_ghio_lib(target_folder="temp"): + """Fetch the GH_IO.dll library from the NuGet packaging system.""" + ghio_dll = "GH_IO.dll" + filename = "lib/net48/" + ghio_dll + + response = urllib.request.urlopen("https://www.nuget.org/api/v2/package/Grasshopper/") + dst_file = os.path.join(target_folder, ghio_dll) + zip_file = zipfile.ZipFile(BytesIO(response.read())) + + with zip_file.open(filename, "r") as zipped_dll: + with open(dst_file, "wb") as fp: + fp.write(zipped_dll.read()) + + return dst_file + + +def find_ghio_assembly(libdir): + for root, _dirs, files in os.walk(libdir): + for basename in files: + if basename.upper() == "GH_IO.DLL": + filename = os.path.join(root, basename) + return filename + + +def bitmap_from_image_path(image_path): + with open(image_path, "rb") as imageFile: + # Ensure img_string is a string, not a bytes object + img_string = base64.b64encode(imageFile.read()) + if isinstance(img_string, bytes): + img_string = img_string.decode() + + # Now you can pass img_string to the FromBase64String method + return System.Convert.FromBase64String(img_string) + # return System.Convert.FromBase64String(img_string) + + +def validate_source_bundle(source): + icon = os.path.join(source, "icon.png") + code = os.path.join(source, "code.py") + data = os.path.join(source, "metadata.json") + + if not os.path.exists(icon): + raise ValueError( + "icon missing, make sure icon.png is present in the source bundle: {}".format( + source + ) + ) + if not os.path.exists(code): + raise ValueError( + "code missing, make sure code.py is present in the source bundle: {}".format( + source + ) + ) + if not os.path.exists(data): + raise ValueError( + "metadata missing, make sure metadata.json is present in the source bundle: {}".format( + source + ) + ) + + icon = bitmap_from_image_path(icon) + + with open(code, "r") as f: + python_code = f.read() + + with open(data, "r") as f: + data = json.load(f) + + if "exposure" not in data: + data["exposure"] = EXPOSURE["default"] + + if data["exposure"] not in EXPOSURE["valid"]: + raise ValueError( + "Invalid exposure value. Accepted values are {}".format( + sorted(EXPOSURE["valid"]) + ) + ) + + ghpython = data.get("ghpython") + + if r'"""' not in python_code: + python_code = r'"""{}"""{}{}'.format( + data.get("description", "Generated by Componentizer"), + os.linesep, + python_code, + ) + + return icon, python_code, data + + +def parse_param_access(access): + try: + access = int(access) + except ValueError: + # Maybe string? + access = ACCESS["map"].get(access) + + if access not in ACCESS["valid"]: + raise ValueError( + "Invalid param access value. Valid values are {}".format( + sorted(ACCESS["valid"]) + ) + ) + + return access + + +def parse_wire_display(wire_display): + try: + wire_display = int(wire_display) + except ValueError: + wire_display = WIRE_DISPLAY["map"].get(wire_display) + + if wire_display not in WIRE_DISPLAY["valid"]: + raise ValueError( + "Invalid wire display value. Valid values are {}".format( + sorted(WIRE_DISPLAY["valid"]) + ) + ) + + return wire_display + + +def parse_param_type_hint(type_hint_id): + type_hint_id = type_hint_id or PARAM_TYPE["default"] + + if type_hint_id in TYPES_MAP: + type_hint_id = TYPES_MAP[type_hint_id] + + if type_hint_id not in PARAM_TYPE["valid"]: + raise ValueError( + 'Invalid param type hint ID ("{}"). Valid values are {}'.format( + type_hint_id, sorted(PARAM_TYPE["valid"]) + ) + ) + + try: + type_hint_id = System.Guid.Parse(type_hint_id) + except SystemError: + raise ValueError("Unable to parse type hint ID: {}".format(type_hint_id)) + + return type_hint_id + + +def replace_templates(code, version, name, ghuser_name): + if version: + code = TEMPLATE_VER.sub(version, code) + + code = TEMPLATE_NAME.sub(name, code) + code = TEMPLATE_GHUSER_NAME.sub(ghuser_name, code) + + return code + + +def create_ghuser_component(source, target, version=None, prefix=None): + from GH_IO.Serialization import GH_LooseChunk + + icon, code, data = validate_source_bundle(source) + + code = replace_templates(code, version, data["name"], os.path.basename(target)) + + instance_guid = data.get("instanceGuid") + if not instance_guid: + instance_guid = System.Guid.NewGuid() + else: + instance_guid = System.Guid.Parse(instance_guid) + + prefix = prefix or "" + + root = GH_LooseChunk("UserObject") + root.SetGuid("BaseID", SCRIPT_COMPONENT_GUID) + root.SetString("Name", prefix + data["name"]) + root.SetString("NickName", data["nickname"]) + root.SetString("Description", data.get("description", "")) + root.SetString("ToolTip", data.get("description", "")) + root.SetInt32("Exposure", data.get("exposure", EXPOSURE["default"])) + root.SetString("Category", data["category"]) + root.SetString("SubCategory", data["subcategory"]) + root.SetGuid("InstanceGuid", instance_guid) + root.SetByteArray("Icon", icon) + + ghpython_data = data["ghpython"] + + ghpython_root = GH_LooseChunk("UserObject") + ghpython_root.SetString("Description", data.get("description", "")) + bitmap_icon = System.Drawing.Bitmap.FromStream(System.IO.MemoryStream(icon)) + ghpython_root.SetDrawingBitmap("IconOverride", bitmap_icon) + ghpython_root.SetBoolean("UsingLibraryInputParam", False) + ghpython_root.SetBoolean("UsingScriptInputParam", False) + ghpython_root.SetBoolean("UsingStandardOutputParam", False) + ghpython_root.SetInt32("IconDisplay", ghpython_data.get("iconDisplay", 0)) + ghpython_root.SetString("Name", data["name"]) + ghpython_root.SetString("NickName", data["nickname"]) + ghpython_root.SetBoolean("MarshalGuids", ghpython_data.get("marshalGuids", True)) + + # ghpython_root.CreateChunk('Attributes') + # for mf in ('Bounds', 'Pivot', 'Selected'): + + params = ghpython_root.CreateChunk("ParameterData") + inputParam = ghpython_data.get("inputParameters", []) + outputParam = ghpython_data.get("outputParameters", []) + + params.SetInt32("InputCount", len(inputParam)) + for i, _pi in enumerate(inputParam): + params.SetGuid( + "InputId", i, System.Guid.Parse("08908df5-fa14-4982-9ab2-1aa0927566aa") + ) + params.SetInt32("OutputCount", len(outputParam)) + for i, _po in enumerate(outputParam): + params.SetGuid( + "OutputId", i, System.Guid.Parse("08908df5-fa14-4982-9ab2-1aa0927566aa") + ) + + for i, pi in enumerate(inputParam): + input_instance_guid = System.Guid.NewGuid() + pi_chunk = params.CreateChunk("InputParam", i) + pi_chunk.SetString("Name", pi["name"]) + pi_chunk.SetString("NickName", pi.get("nickname") or pi["name"]) + pi_chunk.SetString("Description", pi.get("description")) + pi_chunk.SetBoolean("Optional", pi.get("optional", True)) + pi_chunk.SetString("ToolTip", pi.get("description", "")) + pi_chunk.SetBoolean("AllowTreeAccess", pi.get("allowTreeAccess", True)) + pi_chunk.SetBoolean("ShowTypeHints", pi.get("showTypeHints", True)) + pi_chunk.SetInt32( + "ScriptParamAccess", + parse_param_access(pi.get("scriptParamAccess", ACCESS["default"])), + ) + pi_chunk.SetInt32("SourceCount", pi.get("sourceCount", 0)) + pi_chunk.SetGuid("InstanceGuid", input_instance_guid) + pi_chunk.SetGuid("TypeHintID", parse_param_type_hint(pi.get("typeHintID"))) + pi_chunk.SetInt32( + "WireDisplay", + parse_wire_display(pi.get("wireDisplay", WIRE_DISPLAY["default"])), + ) + pi_chunk.SetBoolean("ReverseData", pi.get("reverse", False)) + pi_chunk.SetBoolean("SimplifyData", pi.get("simplify", False)) + if pi.get("flatten", False): + pi_chunk.SetInt32("Mapping", 1) + elif pi.get("graft", False): + pi_chunk.SetInt32("Mapping", 2) + + for i, po in enumerate(outputParam): + output_instance_guid = System.Guid.NewGuid() + po_chunk = params.CreateChunk("OutputParam", i) + po_chunk.SetString("Name", po["name"]) + po_chunk.SetString("NickName", po.get("nickname") or po["name"]) + po_chunk.SetString("Description", po.get("description")) + po_chunk.SetBoolean("Optional", po.get("optional", False)) + po_chunk.SetString("ToolTip", po.get("description", "")) + po_chunk.SetInt32("SourceCount", po.get("sourceCount", 0)) + po_chunk.SetGuid("InstanceGuid", output_instance_guid) + po_chunk.SetBoolean("ReverseData", po.get("reverse", False)) + po_chunk.SetBoolean("SimplifyData", po.get("simplify", False)) + if po.get("flatten", False): + po_chunk.SetInt32("Mapping", 1) + elif po.get("graft", False): + po_chunk.SetInt32("Mapping", 2) + + script = ghpython_root.CreateChunk("Script") + + code_base64 = base64.b64encode(code.encode("utf-8")) + code_base64 = str(code_base64)[2:-1] + script.SetString("Text", code_base64) + script.SetString("Title", "S") + language_spec = script.CreateChunk("LanguageSpec") + language_spec.SetString("Taxon", "*.*.python") + language_spec.SetString("Version", CPY_VER) + + # xml_serialized = ghpython_root.Serialize_Xml() + root.SetByteArray("Object", ghpython_root.Serialize_Binary()) + System.IO.File.WriteAllBytes(target, root.Serialize_Binary()) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description="Create GHUser components out of python code." + ) + parser.add_argument( + "source", + type=str, + help="Source directory where code for all components is stored", + ) + parser.add_argument("target", type=str, help="Target directory for ghuser files") + parser.add_argument( + "--ghio", + type=str, + required=False, + help="Folder where the GH_IO.dll assembly is located. Defaults to ./lib", + ) + parser.add_argument( + "--version", type=str, required=False, help="Version to tag components" + ) + parser.add_argument( + "--prefix", + type=str, + required=False, + help="Add this prefix to the name of each generated component", + ) + args = parser.parse_args() + + sourcedir = args.source + if not os.path.isabs(sourcedir): + sourcedir = os.path.abspath(sourcedir) + + targetdir = args.target + if not os.path.isabs(targetdir): + targetdir = os.path.abspath(targetdir) + + if args.ghio is None: + libdir = tempfile.mkdtemp("ghio") + fetch_ghio_lib(libdir) + else: + libdir = os.path.abspath(args.ghio) + gh_io = find_ghio_assembly(libdir) + source_bundles = [ + d + for d in os.listdir(sourcedir) + if os.path.isdir(os.path.join(sourcedir, d)) + and d not in ("__pycache__", ".git") + ] + + print("GHPython componentizer") + print("======================") + + print("[x] Source: {} ({} components)".format(sourcedir, len(source_bundles))) + print("[ ] Target: {}\r".format(targetdir), end="") + if not os.path.exists(targetdir): + os.mkdir(targetdir) + print("[x]") + + if not gh_io: + print("[-] Cannot find GH_IO Assembly! Aborting.") + sys.exit(-1) + + clr.AddReference(os.path.splitext(gh_io)[0]) + + print("[x] GH_IO assembly: {}".format(gh_io)) + + print("Processing component bundles:") + for d in source_bundles: + source = os.path.join(sourcedir, d) + target = os.path.join(targetdir, d + ".ghuser") + print(" [ ] {}\r".format(d), end="") + create_ghuser_component(source, target, args.version, args.prefix) + print(" [x] {} => {}".format(d, target)) \ No newline at end of file diff --git a/componentize.py b/componentize_ipy.py similarity index 99% rename from componentize.py rename to componentize_ipy.py index 53cf436..d737904 100644 --- a/componentize.py +++ b/componentize_ipy.py @@ -393,4 +393,4 @@ def create_ghuser_component(source, target, version=None, prefix=None): target = os.path.join(targetdir, d + ".ghuser") print(" [ ] {}\r".format(d), end="") create_ghuser_component(source, target, args.version, args.prefix) - print(" [x] {} => {}".format(d, target)) + print(" [x] {} => {}".format(d, target)) \ No newline at end of file diff --git a/examples/cpy/Test_KitchenSink/code.py b/examples/cpy/Test_KitchenSink/code.py new file mode 100644 index 0000000..14465a4 --- /dev/null +++ b/examples/cpy/Test_KitchenSink/code.py @@ -0,0 +1,26 @@ +""" +Do something silly in python3. + +This component does nothing useful, it's only a kitchen sink (but in python3) example showing most available options. + + Args: + x: X value + y: Y value + z: Z value + Returns: + result: The sum of all three values. +""" +from ghpythonlib.componentbase import executingcomponent as component + +import System +import platform +import Rhino +import Grasshopper +import rhinoscriptsyntax as rs + + +class MyComponent(component): + def RunScript(self, x: float, y: float, z: float): + ghenv.Component.Message = 'COMPONENT v{{version}}' + result = x + y + z + return result diff --git a/examples/Test_KitchenSink/icon.png b/examples/cpy/Test_KitchenSink/icon.png similarity index 100% rename from examples/Test_KitchenSink/icon.png rename to examples/cpy/Test_KitchenSink/icon.png diff --git a/examples/cpy/Test_KitchenSink/metadata.json b/examples/cpy/Test_KitchenSink/metadata.json new file mode 100644 index 0000000..3cfdda9 --- /dev/null +++ b/examples/cpy/Test_KitchenSink/metadata.json @@ -0,0 +1,65 @@ +{ + "name": "Kitchen sink component example", + "nickname": "Everything", + "category": "Componentizer", + "subcategory": "ALL", + "description": "This is an example with the everything and the kitchen sink in it, just to show all available options.", + "exposure": 4, + "instanceGuid": "cdd47086-f902-4b77-825b-6b79c3aaecc1", + "ghpython": { + "marshalGuids": true, + "iconDisplay": 2, + "inputParameters": [ + { + "name": "x", + "nickname": "x", + "description": "The X value of the component.", + "optional": true, + "allowTreeAccess": true, + "showTypeHints": true, + "scriptParamAccess": "item", + "wireDisplay": "faint", + "sourceCount": 0, + "typeHintID": "float", + "reverse": true, + "simplify": false + }, + { + "name": "x", + "nickname": "y", + "description": "The Y value of the component.", + "optional": true, + "allowTreeAccess": true, + "showTypeHints": true, + "scriptParamAccess": "item", + "wireDisplay": "default", + "sourceCount": 0, + "typeHintID": "float", + "simplify": true + }, + { + "name": "z", + "nickname": "z", + "description": "The Z value of the component.", + "optional": true, + "allowTreeAccess": true, + "showTypeHints": true, + "scriptParamAccess": "item", + "wireDisplay": "faint", + "sourceCount": 0, + "typeHintID": "float", + "flatten": true + } + ], + "outputParameters": [ + { + "name": "result", + "nickname": "result", + "description": "Result of the computation", + "optional": false, + "sourceCount": 0, + "graft": true + } + ] + } +} \ No newline at end of file diff --git a/examples/cpy/Test_Minimal/code.py b/examples/cpy/Test_Minimal/code.py new file mode 100644 index 0000000..098c18d --- /dev/null +++ b/examples/cpy/Test_Minimal/code.py @@ -0,0 +1,17 @@ +""" +Do something silly in python3. + +This component does nothing useful, it's only a minimal example of python3 in grasshopper. + + Args: + x: X value + y: Y value + z: Z value + Returns: + a: The sum of all three values. +""" +import platform + +ghenv.Component.Message = 'COMPONENT v{{version}}' + +a = x + y + z diff --git a/examples/Test_Minimal/icon.png b/examples/cpy/Test_Minimal/icon.png similarity index 100% rename from examples/Test_Minimal/icon.png rename to examples/cpy/Test_Minimal/icon.png diff --git a/examples/Test_Minimal/metadata.json b/examples/cpy/Test_Minimal/metadata.json similarity index 100% rename from examples/Test_Minimal/metadata.json rename to examples/cpy/Test_Minimal/metadata.json diff --git a/examples/Test_KitchenSink/code.py b/examples/ipy/Test_KitchenSink/code.py similarity index 100% rename from examples/Test_KitchenSink/code.py rename to examples/ipy/Test_KitchenSink/code.py diff --git a/examples/Test_MinimalSDK/icon.png b/examples/ipy/Test_KitchenSink/icon.png similarity index 100% rename from examples/Test_MinimalSDK/icon.png rename to examples/ipy/Test_KitchenSink/icon.png diff --git a/examples/Test_KitchenSink/metadata.json b/examples/ipy/Test_KitchenSink/metadata.json similarity index 100% rename from examples/Test_KitchenSink/metadata.json rename to examples/ipy/Test_KitchenSink/metadata.json diff --git a/examples/Test_Minimal/code.py b/examples/ipy/Test_Minimal/code.py similarity index 100% rename from examples/Test_Minimal/code.py rename to examples/ipy/Test_Minimal/code.py diff --git a/examples/ipy/Test_Minimal/icon.png b/examples/ipy/Test_Minimal/icon.png new file mode 100644 index 0000000..c8df236 Binary files /dev/null and b/examples/ipy/Test_Minimal/icon.png differ diff --git a/examples/ipy/Test_Minimal/metadata.json b/examples/ipy/Test_Minimal/metadata.json new file mode 100644 index 0000000..d4c5a89 --- /dev/null +++ b/examples/ipy/Test_Minimal/metadata.json @@ -0,0 +1,26 @@ +{ + "name": "Minimal component example", + "nickname": "Minimal", + "description": "This is an example with the minimum of options set, relying on defaults for everything else", + "category": "Componentizer", + "subcategory": "MAIN", + + "ghpython": { + "inputParameters": [ + { + "name": "x" + }, + { + "name": "y" + }, + { + "name": "z" + } + ], + "outputParameters": [ + { + "name": "a" + } + ] + } +} \ No newline at end of file diff --git a/examples/Test_MinimalSDK/code.py b/examples/ipy/Test_MinimalSDK/code.py similarity index 100% rename from examples/Test_MinimalSDK/code.py rename to examples/ipy/Test_MinimalSDK/code.py diff --git a/examples/ipy/Test_MinimalSDK/icon.png b/examples/ipy/Test_MinimalSDK/icon.png new file mode 100644 index 0000000..c8df236 Binary files /dev/null and b/examples/ipy/Test_MinimalSDK/icon.png differ diff --git a/examples/Test_MinimalSDK/metadata.json b/examples/ipy/Test_MinimalSDK/metadata.json similarity index 100% rename from examples/Test_MinimalSDK/metadata.json rename to examples/ipy/Test_MinimalSDK/metadata.json