3
$\begingroup$

im pretty new to blender, and im facing the need to issue python scripts to blender outside of blender via command line.

Im using the following from a windows shell: "blender exe" --python "python script path" And it works as expected.

The problem is, the second time i issue the command, blender opens a new instance and that's not the behavior i need.

I need to be able to issue commands via command line to the same blender instance.

Is there something (like an argument specifiying the blender process id) i can use to execute different scripts in the same blender instance from the command line?

Thanks in advance

$\endgroup$
3
  • $\begingroup$ is it possible to have the blender bpy module library as standalone and operate with it via an import in a vanilla python 3.6 interpreter? if yes how? any guides on this? Would that work inside Blender script editor? $\endgroup$ Feb 23, 2018 at 10:02
  • $\begingroup$ I think that would be another possible solution for me $\endgroup$ Feb 23, 2018 at 10:03
  • $\begingroup$ Rather than using the CLI why not use blenders python console? You can open a script in blenders text editor and run it. The addon I link to here adds a menu to blenders pyconsole which lists scripts in a folder, you can select a script from the menu and it will open and run it in blenders console. $\endgroup$
    – sambler
    Feb 27, 2018 at 15:34

4 Answers 4

1
$\begingroup$

For a slightly 'hacky' way of achieving this, create a handler to look for the presence of a file and execute the contents of the file (and remove the file) whenever it is found. Once the handler is running all that is required is for any script to be executed to be copied into a specific location and Blender will 'consume' it.

In a text editor, click 'new' and paste the following code (and name it 'exec_from_file.py') :

import bpy
import os

filename = 'blender_script.run'

def scan_for_file_to_exec(scn):

    global filename

    try:
        file = open(filename)

    except FileNotFoundError:
        pass
    else:
        print("scan_for_file_to_exec: Got file")
        content = file.read()
        file.close()
        os.remove(filename)
        exec(content)

bpy.app.handlers.scene_update_pre.append(scan_for_file_to_exec)

print("scan_for_file_to_exec: Scanning for file '%s' in directory '%s'" % (filename, os.getcwd()))

Click 'Run Script' and optionally tick the 'Register' checkbox to automatically run the script whenever the blend file is opened. This will install a handler that will be triggered every time the scene is updated (which appears to occur continuously). The script outputs a line to the console window when it first runs, indicating the file and directory it is scanning for.

scan_for_file_to_exec: Scanning for file 'blender_script.run' in directory '/home/rich'

Whenever the file exists blender will read the contents, remove the file, and execute the code.

All that is now required outside of Blender to trigger the running of a script is to copy the required script into the relevant location and it should immediately disappear and be executed.

For Linux :

cp my_script.py /home/rich/blender_script.run

For Windows :

copy my_script.py c:/users/rich/blender_script.run

For multiple files, copy each file in place, waiting for each to be consumed before copying the next.

NOTE : You need to ensure that Blender is invoked from a location where your user has permissions to read/write files - or you need to run 'As Administrator'.

For example, once the above handler is running (and assuming both you and Blender have permission to read/write to the relevant directory) you can create Suzanne by running the following from the command line (in the relevant directory) :

echo bpy.ops.mesh.primitive_monkey_add(radius=2) >blender_script.run

Then create another different sized in a different location :

echo bpy.ops.mesh.primitive_monkey_add(radius=2, location=(-1,-1,-3)) >blender_script.run

An alternative method is to use a similar handler to read the input from the 'blender' process itself and execute any commands received on that stream. When you launch Blender you can direct the output of another process (eg, a python script) into the 'blender' process and Blender will execute them as they are received.

For this, create the following script in a Text Editor block named 'exec_from_stdin.py' and click Run Script. Don't forget to click 'Register' so that it runs automatically when the blend file is loaded.

import bpy
import select
import sys

def my_scene_handler(scn):

    #print("In scene handler")

    # Check to see if any input on STDIN
    if select.select([sys.stdin,],[],[],0.0)[0]:
        # Read a line from input
        line = sys.stdin.readline()
        print("Got a line '%s'" % line)
        exec(line)

bpy.app.handlers.scene_update_post.append(my_scene_handler)

Now when you launch Blender to load the file it will listen for commands on 'stdin' (standard input).

You can now 'pipe' any input into Blender by launching it using a command similar to the following :

<command> | blender

NOTE : This method doesn't appear to work correctly under MS Window - Linux (and, presumably Mac) only - due to differences in how Windows handles input streams.

$\endgroup$
1
  • $\begingroup$ uhmm also interesting this was my first approach, but unluckily im on windows 10 not linux or mac. Anyways im doing some testing with open_mainfile() and calling this those handlers makes blender crash so i guess these are not the right place to execute it. Im looking for a way to call any methods of the bpy blender api so for me its not enough to be able to call only a predefined set of methods. It has to work with all. $\endgroup$ Feb 28, 2018 at 9:21
0
$\begingroup$

uhmm interesting...

Here is my snippet code that should work. Basically i start a socket server in a background thread that listens to my "open file this file" request from a python 2 client.

I also have a handler that is responsible for executing script files. And the handler is registered to "scene_update_pre".

Both communicate via a shared datastructure, in this example a queue. One feeds the queue, the other consumes it, ok.

Now the problem is i got a blender crash when executing the code that opens a file from the handler function....¿why is that? The handler is executed in the main thread, right? Whats wrong here?

here is the code:

    ######
# TEST THREADS and HANDLERS
#
def execute_blender_util_file(filepath):
    with open(filepath, 'r') as f:
        exec(compile(f.read(), filepath, 'exec'),globals(),locals())


def start_server_inside_blender(port, q):

    import socket
    soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    soc.bind(('localhost',port))
    soc.listen(1)

    while True:
        conn, address = soc.accept() 
        data = conn.recv(8192)

        if data:
            filepath = data
            print('added execute task file: ' + filepath.decode('utf-8'))
            q.put(filepath.decode('utf-8'))
        soc.close()

import queue

q = queue.Queue()
ex = True
def handler(scn):
    global q
    global ex
    while not q.empty():
        filepath = q.get()
        print('--------')
        print(filepath)
        if ex:
            mypath = 'C:/Users/fulanito/test.blend'
            print(mypath)
            import bpy
            bpy.ops.wm.open_mainfile(filepath=mypath)
            ex = False
        #execute_blender_util_file(filepath)
        q.task_done()

import threading
t = threading.Thread(target=start_server_inside_blender,args=(56667,q))
t.start()

import bpy
bpy.app.handlers.scene_update_pre.append(handler)
$\endgroup$
1
  • 1
    $\begingroup$ Is this an answer? Perhaps it could be an edit to the question $\endgroup$
    – batFINGER
    Feb 27, 2018 at 17:36
0
$\begingroup$

Basically, the solution ive found instead of using a handler, is by using a timer in a modal operator!!

The timer should look into the queue from time to time to any inputs and if he queue is not empty process it right away.

Thanks to all.

$\endgroup$
0
$\begingroup$

Ok, now im facing another problem bu tseems different to the one of the original post.

when open_mainfile is called from the modal method of the operator it executes correctly and loads the blend file, but then the modal method is not called anymore so i cant keep processing requests.

Apparently, when opening a file in blender it changes the context or so ive read. Could you guys, anyone, develop a bit on this? the Doc is not very self explanatory... Is changing context the cause? if so, how can my modal operator keep working after calling such functions as open_mainfile?

$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .