Skip to content

Commit

Permalink
WIP: testing out parameter for componentizer
Browse files Browse the repository at this point in the history
  • Loading branch information
9and3 committed Jan 25, 2024
1 parent 5e7e88a commit f0e9805
Show file tree
Hide file tree
Showing 13 changed files with 227 additions and 177 deletions.
82 changes: 48 additions & 34 deletions .github/actions/ghpython-components/componentize.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,21 @@
import System
import System.IO

GHPYTHON_SCRIPT_GUID = System.Guid("410755b1-224a-4c1e-a407-bf32fb45ea7e")

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="35915213-5534-4277-81b8-1bdc9e7383d2",
ghdoc="87f87f55-5b71-41f4-8aea-21d494016f81",
float="39fbc626-7a01-46ab-a18e-ec1c0c41685b",
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="37261734-eec7-4f50-b6a8-b8d1f3c4396b",
str="3aceb454-6dbd-4c5b-9b6b-e71f8c1cdf88",
datetime="09bcf900-fe83-4efa-8d32-33d89f7a3e66",
guid="5325b8e1-51d7-4d36-837a-d98394626c35",
color="24b1d1a3-ab79-498c-9e44-c5b14607c4d3",
Expand All @@ -46,7 +48,7 @@
surface="f4070a37-c822-410f-9057-100d2e22a22d",
subd="20f4ca9c-6c90-4fd6-ba8a-5bf9ca79db08",
brep="2ceb0405-fdfe-403d-a4d6-8786da45fb9d",
geometrybase="c37956f4-d39c-49c7-af71-1e87f8031b26",
geometrybase="c37956f4-d39c-49c7-af71-1e87f8031b26"
)

EXPOSURE = dict(valid=set([-1, 2, 4, 8, 16, 32, 64, 128]), default=2)
Expand Down Expand Up @@ -85,8 +87,14 @@ def find_ghio_assembly(libdir):

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())
return System.Convert.FromBase64String(img_string)
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):
Expand Down Expand Up @@ -132,9 +140,8 @@ def validate_source_bundle(source):
)

ghpython = data.get("ghpython")
sdk_mode = ghpython and ghpython.get("isAdvancedMode", False)

if r'"""' not in python_code and sdk_mode is False:
if r'"""' not in python_code:
python_code = r'"""{}"""{}{}'.format(
data.get("description", "Generated by Componentizer"),
os.linesep,
Expand Down Expand Up @@ -224,8 +231,7 @@ def create_ghuser_component(source, target, version=None, prefix=None):
prefix = prefix or ""

root = GH_LooseChunk("UserObject")

root.SetGuid("BaseID", GHPYTHON_SCRIPT_GUID)
root.SetGuid("BaseID", SCRIPT_COMPONENT_GUID)
root.SetString("Name", prefix + data["name"])
root.SetString("NickName", data["nickname"])
root.SetString("Description", data.get("description", ""))
Expand All @@ -236,36 +242,38 @@ def create_ghuser_component(source, target, version=None, prefix=None):
root.SetByteArray("Icon", icon)

ghpython_data = data["ghpython"]

ghpython_root = GH_LooseChunk("UserObject")
ghpython_root.SetString("Description", data.get("description", ""))
ghpython_root.SetBoolean("HideOutput", ghpython_data.get("hideOutput", True))
ghpython_root.SetBoolean("HideInput", ghpython_data.get("hideInput", True))
ghpython_root.SetBoolean(
"IsAdvancedMode", ghpython_data.get("isAdvancedMode", False)
)
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(
"MarshalOutGuids", ghpython_data.get("marshalOutGuids", True)
)
ghpython_root.SetString("CodeInput", code)
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("84fa917c-1ed8-4db3-8be1-7bdc4a6495a2")
"InputId", i, System.Guid.Parse("08908df5-fa14-4982-9ab2-1aa0927566aa")
)
params.SetInt32("OutputCount", len(outputParam))
for i, _po in enumerate(outputParam):
if i == 0:
params.SetGuid("OutputId", i, System.Guid.Parse("3ede854e-c753-40eb-84cb-b48008f14fd4")) # out parameters on FIXME: whatch xml to see how it works
continue
params.SetGuid(
"OutputId", i, System.Guid.Parse("8ec86459-bf01-4409-baee-174d0d2b13d0")
"OutputId", i, System.Guid.Parse("08908df5-fa14-4982-9ab2-1aa0927566aa")
)

for i, pi in enumerate(inputParam):
Expand All @@ -281,7 +289,7 @@ def create_ghuser_component(source, target, version=None, prefix=None):
"ScriptParamAccess",
parse_param_access(pi.get("scriptParamAccess", ACCESS["default"])),
)
pi_chunk.SetInt32("SourceCount", 0)
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(
Expand All @@ -290,7 +298,6 @@ def create_ghuser_component(source, target, version=None, prefix=None):
)
pi_chunk.SetBoolean("ReverseData", pi.get("reverse", False))
pi_chunk.SetBoolean("SimplifyData", pi.get("simplify", False))
# Mutually exclusive options
if pi.get("flatten", False):
pi_chunk.SetInt32("Mapping", 1)
elif pi.get("graft", False):
Expand All @@ -303,19 +310,27 @@ def create_ghuser_component(source, target, version=None, prefix=None):
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.SetInt32("SourceCount", 0)
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))
# Mutually exclusive options
if po.get("flatten", False):
po_chunk.SetInt32("Mapping", 1)
elif po.get("graft", False):
po_chunk.SetInt32("Mapping", 2)

# print(ghpython_root.Serialize_Xml())
root.SetByteArray("Object", ghpython_root.Serialize_Binary())
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())


Expand Down Expand Up @@ -376,14 +391,13 @@ def create_ghuser_component(source, target, version=None, prefix=None):
os.mkdir(targetdir)
print("[x]")

print("[ ] GH_IO assembly: {}\r".format(gh_io or args.ghio), end="")
if not gh_io:
print("[-]")
print(" Cannot find GH_IO Assembly! Aborting.")
print("[-] Cannot find GH_IO Assembly! Aborting.")
sys.exit(-1)
clr.AddReferenceToFileAndPath(gh_io)
print("[x]")
print()

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:
Expand Down
61 changes: 5 additions & 56 deletions .github/workflows/build-components.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,67 +12,16 @@ jobs:
- name: Install Python and pythonnet
uses: actions/setup-python@v2
with:
python-version: '3.9'
python-version: '3.9.10'
- name: Install pythonnet
run: |
pip install pythonnet
- uses: ./.github/actions/ghpython-components
with:
source: components
target: build
# upload them as artifacts:
source: GH\PyGH\components
target: GH\PyGH\build

- uses: actions/upload-artifact@v2
with:
name: ghuser-components
path: build

build_release_on_tag:
needs: build_ghuser_components
runs-on: windows-latest
name: Build release
if: startsWith(github.ref, 'refs/tags/v')
steps:
- uses: actions/checkout@v2
- uses: NuGet/[email protected]

# create a new release
- name: Create Release
id: create_release
uses: actions/[email protected]
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: Release ${{ github.ref }}
draft: false
prerelease: false

# download the artifacts from the previous job
- name: Download artifacts
uses: actions/download-artifact@v2
with:
name: ghuser-components
path: build/ghuser-components

# verify the artifacts are downloaded
- name: List artifacts
run: |
ls -l build/ghuser-components
# zip the artifacts
- name: Zip artifacts
run: |
cd build
7z a ghuser-components.zip ghuser-components
# upload the downloaded artifacts as release assets
- name: Upload Release Asset
id: upload-release-asset
uses: actions/[email protected]
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: build/ghuser-components.zip
asset_name: ghuser-components
asset_content_type: application/zip
path: GH\PyGH\build
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,9 @@ temp/
*.rhp
*.rui
*.3dmbak

######################
# General
######################
# get rid of all build folders
build/
Binary file modified CsRhino/EmbeddedResources/logo/scriptsync_480.xcf
Binary file not shown.
Binary file added GH/PyGH/assets/img/scriptsync_24.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added GH/PyGH/assets/img/scriptsync_24.xcf
Binary file not shown.
105 changes: 105 additions & 0 deletions GH/PyGH/components/scriptsynccpy/code.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
from ghpythonlib.componentbase import executingcomponent as component

import System
import Rhino
import Grasshopper as gh
import sys
import os
import time

from System.Threading import Thread
from functools import partial

import rhinoscriptsyntax as rs

def update_component():
""" Fire the recalculation of the component solution. """
# clear the output
ghenv.Component.Params.Output[0].ClearData()
# expire the component

ghenv.Component.ExpireSolution(True)

def check_file_change(path):
"""
Check if the file has changed on disk.
:param path: The path of the file to check.
:returns: True if the file has changed, False otherwise.
"""
last_modified = os.path.getmtime(path)
while True:
System.Threading.Thread.Sleep(1000)
current_modified = os.path.getmtime(path)
if current_modified != last_modified:
last_modified = current_modified
update_component()
break
return

def safe_exec(path, globals, locals):
"""
Execute Python3 code safely.
:param path: The path of the file to execute.
:param globals: The globals dictionary.
:param locals: The locals dictionary.
"""
try:
with open(path, 'r') as f:
code = compile(f.read(), path, 'exec')
exec(code, globals, locals)
return locals # return the locals dictionary
except Exception as e:
err_msg = str(e)
return e


class ScriptSyncCPy(component):
def __init__(self):
super(ScriptSyncCPy, self).__init__()
self._var_output = ["None"]
ghenv.Component.Message = "ScriptSyncCPy"

def RunScript(self, x, y):
""" This method is called whenever the component has to be recalculated. """
# check the file is path
path = r"F:\script-sync\GH\PyGH\test\runner_script.py" # <<<< test
if not os.path.exists(path):
raise Exception("script-sync::File does not exist")

print(f"script-sync::x value: {x}")

# non-blocking thread
thread = Thread(partial(check_file_change, path))
thread.Start()

# we need to add the path of the modules
path_dir = path.split("\\")
path_dir = "\\".join(path_dir[:-1])
sys.path.insert(0, path_dir)

# run the script
res = safe_exec(path, globals(), locals())
if isinstance(res, Exception):
err_msg = f"script-sync::Error in the code: {res}"
print(err_msg)
raise Exception(err_msg)

# get the output variables defined in the script
outparam = ghenv.Component.Params.Output
outparam_names = [p.NickName for p in outparam if p.NickName != "out"]
for k, v in res.items():
if k in outparam_names:
self._var_output.append(v)

return self._var_output

# FIXME: problem with indexing return
def AfterRunScript(self):
outparam = ghenv.Component.Params.Output
for idx, outp in enumerate(outparam):
if outp.NickName != "out":
ghenv.Component.Params.Output[idx].VolatileData.Clear()
ghenv.Component.Params.Output[idx].AddVolatileData(gh.Kernel.Data.GH_Path(0), 0, self._var_output[idx])
self._var_output = ["None"]
Binary file added GH/PyGH/components/scriptsynccpy/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit f0e9805

Please sign in to comment.