Distributing toolbar and scripts to Mac

I am looking into a way to make a toolbar with scripts and distribute that. If I store the scripts in a folder in my documents folder, I thought I could do it this way:

-RunPythonscript "~/Documents/Github/pythonscripts/myscript.py"

but this doesn’t work. I have to type out the full path which I don’t like, because in the end I want to be able to share this toolbar.

I also tried adding the full path to the files location, and simply do it this way:

-RunPythonscript "myscript.py"

but that also doesn’t work

On the wiki it says Rhino will look in this scripts-folder:
~/Library/Application Support/McNeel/Rhinoceros/7.0/Scripts
That works, so how can I make sure that once I distribute the toolbar/scripts that the scripts get copied to that location?

Have you tried this?

import sys

sys.path.append(MODULE_PATH)

import MODULE

I believe the rhi installer format doesn’t work with Python scripts, which is a bummer! Rhino should support something like the installation of zipped plug-ins via drag and drop or an add-on menu, like Blender does! Currently it’s a nightmare to distribute Pythonic plug-ins, and not everybody wants run a Rhino server on a VPS somewhere just to get a free plug-in rolling.

would that also be compatible with windows then? I want to keep my scripts universal.

@will do you have any recommendations here? I would think yak packages would be the way to go

Yes, that’s how @antoinemaes and I do it in our Tapeworm plugin for Grasshopper, which is 100% written in Python and cross-platform compatible. It’s been tested on macOS and Windows.

Interesting! Does Yak allow you to put files in Scripts or just in a predefined directory, because for instance Grasshopper add-ons seem to get placed somewhere separately, when installed with the PackageManager, and not in the usual location?

Yak packages get unzipped as folders in a location that the package manager controls. The package manager informs Rhino and Grasshopper about these locations and RH/GH need to do whatever is appropriate to use files from these locations. If Gijs used the script compiler to gather up his scripts into an rhp, he could package a toolbar with the rhp in a yak package.

What if you have a Python module and Grasshopper user objects that use the same module? The user objects usually need to go into the UserObjects folder, the module is more flexible, however the UserObjects each have a hardcoded sys.path.append(MODULE_PATH) to where they expect to be able to import the module from.
Can Yak be used in this case, or rather would it know what to do?

thanks, this is interesting to dive into.

@diff-arch thanks, I’ll look into your solution as well.

In my case I’m aiming to distribute the page layout tools to my students, so it needs to be a Rhino toolbar and a collection of scripts, no gh for the moment.

Are your students all on V7? If so, I think a yak package is the way to go.

Most of them yes. My tools don’t work in older versions anyway.

Good to know, thanks!

@piac explored this scenario recently. By adding a little more logic to the beginning of each user-object, we can find the folder where the package was unpacked and add the additional modules to the path. An additional benefit (that you’ll find when opening the example file below) is that the user can be prompted to install the package if it isn’t yet on their machine. This works on Rhino 6 and Rhino 7 on both platforms.

auto-install3.gh (5.6 KB)

3 Likes

Thanks for the reply, Will!

Neither Python files, nor user objects seem to be supported by the current PackageManager though! At least that’s what the documentation states.
Only these formats seem to be supported: .rhp , .gha , and .ghpy .
.ghpy isn’t even a macOS compatible format, thus not an option for cross-platform Pythonic plug-ins!

Unless you allow the distribution of Python modules - as a folder with an __init__.py and other .py files - as well as .ghuser files, the workflow you describe above doesn’t make much sense, since the relevant plug-in files can’t be distributed via the PackageManager in the first place.

We haven’t documented this yet, but it does work. Did you try the example file that I shared?

Here are a few clarifications…

  • There’s no restriction on files that you include in the package alongside your plug-in.
  • Grasshopper will load user objects from packages.
  • Rhino won’t do anything directly with the .py files – that’s the job of the find_with_package_manager() function in the example that I shared. You would need to include the same find_with_package_manager() function (definition and call) at the start of any python scripts in your user objects.
1 Like

Oh, wow, my bad. That’s good to hear! :slight_smile:

Would it be possible to get an example on how to structure such a plug-in release directory-wise please?

OK, I seem to have missed that. I understood that the PackageManager would distribute only the .ghuser objects, and then would look on a remote server for a separate Python module.

Furthermore, is there any way to test a PackageManager release locally, before releasing it to the public?

Here’s the structure of the package used by the auto-install3.gh example. As you can see, it’s pretty simple.

ghpythonautoinstall-0.3.0.0-any-any.yak
├── ghpythonautoinstall.py
├── ghpythonautoinstall_special_adder.ghuser
└── manifest.yml

When the package is installed, any directory structure is maintained. Grasshopper will load .ghuser files from subdirectories in the package, so you could organise them this way if you like.

The location of the .py files in the package are up to you, but note that the find_with_package_manager() is set up to add the root package directory to sys.path, hence why ghpythonautoinstall.py is located at the root of the package.

You can set up a custom package repository on your local machine for testing.

1 Like

Defining an additional macOS directory path for Rhino.PackageManager.Sources doesn’t seem to do anything?
Currently it looks like this:

https://yak.rhino3d.com;/Users/<user>/Desktop/rhino_packages

Inside rhino_packages, I’ve placed a folder named according to the Yak documentation: myplugin-1.0.0-any-any.yak

It’s directory contents are:

|--- FirstUserObject.ghuser
|--- SecondUserObject.ghuser
|--- ThirdUserObject.ghuser
|--- module
        |--- __init__.py
        |--- foo.py
        |--- bar.py
|--- misc
        |--- README.md
        |--- LICENSE.txt
└-- manifest.yml

Do I need to validate the package somehow?

Is you’re trying to use a custom package repository, then you need to build the package. “myplugin-1.0.0-any-any.yak” should be a file, produced by running yak.exe build in the same directory as manifest.yml (see the end of this guide).

The other difference that I see if that you have a Python module instead of a python file. @piac, should this structure work?

1 Like

Got it! Packaging was a success and the plug-in is now available in my PackageManager! :slight_smile:

I’ll wait for confirmation that this could potentially work, until I proceed further with testing! I thought this was clear, since above I was writing about a module the entire time. :wink:

Sorry, it absolutely should work. To confirm, the directory added to sys.path (i.e. the root of the package wherever Rhino installs it) can contain both .py modules and/or python packages (directories with __init__.py). The terminology is particularly confusing here!

Did you try installing it via Rhino’s Package Manager and were you able to use the User Objects in Grasshopper?

Send the .yak package over if you want us to take a look (via PM if necessary).

2 Likes

No need to apologize. You’ve been tremendously helpful! Thanks.

No, I haven’t yet. I need to modify the code of the User Objects first so that they are able to import the module. Unfortunately, I probably won’t get around to that today, since I’m quite busy! I’ll report back as soons as I’ve tested it though.

Will do, if I’ll run into any problems! Thanks again.

1 Like