Maya PyMel & Qt tool to .exe/.msi file

maya
qt
python

#1

Hi everyone,

I just finished developing a simple Maya tool using PyMel + PyQt and I was wondering… What’s the easiest way to wrap a tool into an .exe/.msi type file with all its dependencies, that will add the tool into maya when the .exe/.msi file is executed?

If the answer is that there’s no other solution than building my own tool to do that from scratch, let me know, please.

Thanks in advance,
Alex.


#2

This one is a complex problem, one you have the fact that python packaging is funky and awful. But you’re probably looking at a combination of distutils and setuptools to handle some amount of packaging.

But on top of that you’re talking about Maya, which is a non-standard python environment. So here you probably want to look into Maya’s module system, and plugin distribution options.

Alternatively you can look at things like NSIS or Inno tools, or even visual studio has some kind of installer packaging system to just make a generic installer application.


#3

Nice, thank you for the help Mr. White! I will do a bit more research and see what I do.


#4

It sounds like this is really about an installer, rather than about exe packaging in all it’s gory details. You might want to consider something like this:


The goal would be to provide a zip file with all your code (you may need to be able to unzip binaries, like QT, into a user directory – but you don’t need the full MSI / registry / uninstaller ball of wax). Because it hits the registry and program files, the MSi/Exe route will also require admin rights for most users which will complicate things if you’re dealing with outsource houses where machines are quite locked down.


#5

First of Theodox, thank you for your help. The method you describe in your posts sounds perfect for what I’m intending to do. But since I’m quite new to Python/MEL, I’m not sure what I need to run this properly. I managed to get a .mel file out of your Keystone script pointing to my project directory, but I have no clue of what I should do to make it work properly.

What I’m trying to say is that, for example, my tool is a simple .fbx exporter that is opened like this:

from FBXExportLibrary import fbxExporter
reload(fbxExporter)

ui = fbxExporter.showUI() <----- Where should I put this

Where should I include that code?

Another question I have is, how can I take advantage of the “main.py” file you describe? So, I understood that if I want to run something when Maya starts up, I have to include it in the “main.py” file. But for example, if I wanted to include the tool in one of Maya’s menus (I still don’t know if that is possible) or in a custom menu I created (like the ones “Bonus Tools” & “Arnold” have), which part of the code should be included in that “main.py” file so that everytime I open Maya, the tool is loaded and placed in a certain menu?


#6

What the stuff in those articles does is just to make sure that you can bundle up a bunch of python and distribute it as a unit – it’s an alternative to installers.

So : you have a directory with all your scripts in it. If you run keystone on it, they all get bundled up into a zip file and that gets hidden inside the mel.

When you run the mel, the zipfile is extracted to a hidden directory and its contents go onto your PATH – so if you include a module in that folder, you’ll be able to import it in your running maya session. If you had a file in the zip called foo.py, launching the keystone mel file will allow you to import foo from the maya listener.

If you have a __main__.py in the root of the folder you’re zipping up, then it will run when you launch the mel file. So, if you add a __main__.py like this:

import foo
foo.do_something()

then the foo module will be imported and foo.do_something() will be run on startup. If foo.do_something() wants to add a menu or something like that it’s welcome to do so ; this code should just run whenever Maya is started using the keystone mel file .

A main part of the design is that it’s opt-in – if you run the mel it works, if you just run maya nothing special happens. That makes it easy to distribute and ‘uninstall’. A good way to manage it on windows is to make a shortcut which actually runs the mel file and another which does not – that way you can choose at startup.

Here’s a sample that illustrates the process: https://www.dropbox.com/s/5st06fwiy221gax/menu_test.py.mel?dl=0

If you download that, and either double-click it or run maya.exe c:/path/to/menu_test.py.mel from the command line, it should work. You can see the actual files in your maya scripts folder – there will be folder there called ‘keystones’ and in it a zip file called 'menu_test.py.mel.zip` – that zip file shows you how the files were laid out


#7

Amazing! Got the tool running without any trouble thanks to your explanation! Thank you Theodox!

Now, if you don’t mind, I have some other questions to ask you.

Lets say I want my tool to have backwards compatibility with older versions of Maya that instead of PySide2, they carry PySide instead. Apart from tweaking the code, would you include Mottosso’s Qt.py inside the MEL file? I tried it, and I got an error in Maya 2016, but worked with Maya 2018 (so it wasn’t a problem of the Qt.py script).

 # Error: line 1: ZipImportError: file \pathOfTheScript\script.py line 6: bad local file header in \pathOfTheZIPFile\zipFile.mel.zip #

And now lets say I want to remove that tool completely from Maya. What I encountered is that deleting the .zip file isn’t enough. How can I remove the reference that Maya has of the scripts that were contained in the .zip? Because I noticed that Maya still has reference to the scripts, and the program gave me this error when I called methods from them:

# Error: IOError: file <maya console> line 2: zipimport: can not open file \path...\keystones\zipFile.mel.zip 

Apart from that, what else do I need to remove to leave the program like if I hadn’t imported that MEL script?


#8

Any plain old python (modules or packages) will work fine from inside the zip. The only thing that’s a bit annoying is that pyd or so files – ie, binary extensions like the perforce api – can’t run from inside the zip. If I have one of those, I include the file in the zip and then include a couple of lines in the __main__.py to extract it to the same folder and to make sure that folder is on the user’s path. You should only need to do it if the file(s) in question don’t exist, so the very first run is a bit slow but after that it should be fine

@marcuso is around here somewhere, he might have a better idea why it works in 2018 and not 2016


#9

Does it work outside the mel hack / zipimport land?


#10

Oh my, thanks for the advise, that made me think. I wasn’t tweaking all the code that needed to be tweaked… Shame on me!

Thank you both for helping me this week, and thanks Theodox for sharing your “keystone” method. It sure is a good way to pack and distribute python-made tools.