0

I need a little help creating an executable python OS command for Blender (Windows and Mac). I am setting up a stand-alone blender package for a client to use. I have a python script that imports some data and I want to create an executable python script that runs OS commands to run Blender, run the python script and define the data directories.

This is a generic version of the Windows command I normally run to import the data, using the stand-alone version of Blender:

"C:\Users\username\Desktop\Package\system\blender\blender.exe" "C:\Users\username\Desktop\Package\system\version_data\CurrentVersion.blend" -P "C:\Users\username\Desktop\Package\system\version_data\BlenderScript.py" "C:\Users\username\Desktop\Package\input_data\\" -y

From my research I have worked out that I need to:

  1. import os
  2. make the directories in the command relative
  3. create an executable python file

My python experience is limited, but I believe it may be something like this:

import os

pythonDirectory = os.path.dirname(os.path.abspath(RunThisApp.exe))  # get the current dir of this file (which would be: C:\Users\username\Desktop\Package\)

os.path.join(pathDirectory, "//system\blender\blender.exe" "//system\version_data\CurrentVersion.blend" -P "//system\version_data\BlenderScript.py" "//input_data\\" -y)

However I had a look at this post and was a little fuzzy as to the best way to do this:

Calling an external command in Python

Then I could possibly use PyInstaller to create the python executable files. Which seems to be the simplest method suggested here:

How to make a Python script standalone executable to run without ANY dependency?

http://www.pyinstaller.org/

Am I close to the correct result here? I am guessing my syntax is off. I need to make sure it works for both Windows and Mac.

It should also be noted that the separate python script I run to import data into blender (which I have been using and updating for a couple of years), refers to OS arguments to get the desired path of data to import, so I need to make sure that I maintain that connection. Here is an example:

IMPORT_DATA_FILENAME = sys.argv[4]+'data.txt' 

Any advice or input would be greatly appreciated. Chris Lee

Community
  • 1
  • 1
Chris Lee
  • 1
  • 1
  • 1
    `"//system\blender\blender.exe"`: bad pick: `\b` is backspace. Use "r" prefix.... what is your question??? – Jean-François Fabre May 18 '17 at 16:43
  • [This answer](https://blender.stackexchange.com/a/34563/935) does what you are trying, not 100% sure it works on windows and a comment indicates an adjustment is needed for osx. You can [check what system](http://stackoverflow.com/a/1857/2684771) your on and use different executable paths if needed. – sambler May 19 '17 at 09:23
  • @Jean-FrançoisFabre I didn't understand your reply. Should paths not be written like this? How would you write such a path? My question was really just: am I using the best method to get the desired result across multiple operating systems. – Chris Lee May 23 '17 at 00:15
  • In windows a path uses the backslash - in a normal string that causes `\b` to be interpreted as an escape sequence that represents a backspace, like `\n` is a newline. You add the `r` prefix to the string as in `r"//system\blender\blender.exe"` to specify that it is a raw string to prevent the backslash being interpreted that way. [Read further here](https://docs.python.org/3/reference/lexical_analysis.html#literals). You can also use `os.path.join` to not use path separators in a string. – sambler May 23 '17 at 01:40
  • Thank you for clarifying that. I had seen that os.path.join looked like the best solution but did not realise that I had to make a raw string if using standard paths. – Chris Lee May 23 '17 at 06:21

2 Answers2

1

It looks like you have all the files and binaries you want to use included in one folder that you are sharing. If you place the initial script at the top of that directory you should be able to get it's location from argv[0] and calculate the location of the other files you want from that, which should allow the user to move your package folder anywhere they want.

You can use platform.system() to add any system specific variations to the paths.

#!/usr/bin/env python3

import os
from sys import argv
from platform import system
from subprocess import call

# define the package dir as the dir that this script is in
pkg_dir = os.path.abspath(os.path.dirname(argv[0]))
# use a package dir in the users home folder
#pkg_dir = os.path.join(os.path.expanduser('~'), 'Package')
# use a package dir in the users desktop folder
#pkg_dir = os.path.join(os.path.expanduser('~'), 'Desktop', 'Package')
sys_dir = os.path.join(pkg_dir, system())
vers_dir = os.path.join(pkg_dir,'version_data')
blend = os.path.join(vers_dir, 'CurrentVersion.blend')
script = os.path.join(vers_dir, 'BlenderScript.py')

if system() == 'Windows':
    blender_exe = os.path.join(sys_dir, 'blender', 'blender.exe')
elif system() == 'Darwin':
    blender_exe = os.path.join(sys_dir, 'blender', 'blender.app',
                                'Contents', 'MacOS', 'blender')
else:
    # linux?
    blender_exe = os.path.join(sys_dir, 'blender', 'blender')

calllist = [
    blender_exe,
    blend,
    '--python',
    script,
    ]

call(calllist)
sambler
  • 6,169
  • 1
  • 12
  • 22
  • _"It looks like you have all the files and binaries you want to use included in one folder that you are sharing. If you place the initial script at the top of that directory you should be able to get it's location from argv[0] and calculate the location of the other files you want from that, which should allow the user to move your package folder anywhere they want."_ Yes Sambler, that is exactly what I am trying to do. – Chris Lee May 23 '17 at 00:39
  • Sambler I hit an issue and with some help worked out where I went wrong. The "if system()" part will identify the system then look for the directory of the same name. I just had a directory named system, but it makes sense that I should have a directory for windows and mac. Does this mean for the Mac version it should be in a directory called "Darwin"? – Chris Lee May 23 '17 at 06:23
  • I am not expecting to set this up for Linux but if I do, would the files be stored in a directory named system? – Chris Lee May 23 '17 at 06:25
  • `system()` should return `'Linux'`, `'Darwin'` or `'Windows'`. See [this page](https://pymotw.com/2/platform/#operating-system-and-hardware-info) for more info on what you get from platform. I think using that value for the folder name holding each systems binary files would be the easiest. – sambler May 23 '17 at 06:39
0

Using Sambler's solution I have modified it slightly to got the following solution:

import os
from sys import argv
from platform import system
from subprocess import call

# define the package dir as the dir that this script is in
pkg_dir = os.path.abspath(os.path.dirname(argv[0]))

sys_dir = os.path.join(pkg_dir, 'private_sys', system())
vers_dir = os.path.join(pkg_dir, 'private_sys', '#version_data')
blend = os.path.join(vers_dir, 'CurrentVersion.blend')
script = os.path.join(vers_dir, 'BlenderScript.py')
input = os.path.join(pkg_dir, 'input_data')

if system() == 'Windows':
    blender_exe = os.path.join(sys_dir, 'blender', 'blender.exe')
elif system() == 'Darwin':
    blender_exe = os.path.join(sys_dir, 'blender', 'blender.app', 'Contents', 'MacOS', 'blender')

calllist = [
    blender_exe,
    blend,
    '--python',
    script,
    input,
    ]

call(calllist)

To clarify, within the Package directory the folder structure is as follows:

+---input_data 
    \---data.txt
    \---input.blend
\---private_sys
    +---#version_data
        \---BlenderScript.py
        \---CurrentVersion.blend
    +---Darwin
    |   \---blender
    \---Windows
        \---blender

Apart from removing the Linux and other suggested variations, I added an input directory. The BlenderScript.py that is called refers to the data.txt and the input.blend files and imports them into the CurrentVersion.blend file. I also moved the version data into a separate directory from the OS specific blender directories and inside the private_sys directory, so that the user doesn't have to see those files.

The last hurdle was lines like this inside BlenderScript.py:

IMPORT_DATA_FILENAME = sys.argv[4]+'data.txt' 

The problem was that I was getting an error because the script would end up looking for: "\input_datadata.txt".

I fixed this by changing it to:

IMPORT_DATA_FILENAME = sys.argv[4]+'/data.txt' 

Which returns: "\input_data/data.txt". I am assuming this will have the same result in OSX.

Thank you very much for your help. This is an area I am learning more in all the time, as the work I do for the client is far more complicated than 'past me' could have imagined. 'Future me' is going to be very pleased with himself when all this is done.

Chris Lee
  • 1
  • 1
  • I eventually got pyinstaller to work, once I realised that it isn't compatible with Python 3.6 yet and changed to Python 3.5. It is now working as intended. Will try Mac tomorrow. – Chris Lee May 23 '17 at 10:18