Recently I have been trying to use Blender from Julia.
I have got a pretty good workflow using the rpyc package in Python. With this, we can import bpy in Julia using PythonCall.jl
This is the Python script to be run in Blender to achieve this:
import importlib
import threading
import time
if importlib.util.find_spec('rpyc') is None:
import pip
pip.main(['install', 'rpyc'])
from rpyc.utils.server import ThreadedServer
import rpyc
class SessionService(rpyc.Service):
def __init__(self):
self.session_globals = {}
def exposed_execute(self, code):
exec(code, self.session_globals)
return self.session_globals.get('_')
def exposed_get(self, name):
return self.session_globals[name]
def exposed_set(self, name, value):
self.session_globals[name] = value
class LimitedThreadedServer(ThreadedServer):
def __init__(self, *args, max_clients=0, **kwargs):
self.max_clients = max_clients
self._sem = threading.BoundedSemaphore(max_clients) if max_clients > 0 else None
super().__init__(*args, **kwargs)
def _accept_method(self, sock):
if self._sem is not None:
if not self._sem.acquire(blocking=False):
print(f"Connection rejected: max {self.max_clients} clients reached")
sock.close()
return
def wrapped_handler(sock):
try:
self._authenticate_and_serve_client(sock)
finally:
self._sem.release()
t = threading.Thread(target=wrapped_handler, args=(sock,))
else:
t = threading.Thread(target=self._authenticate_and_serve_client, args=(sock,))
t.daemon = True
t.start()
if __name__ == '__main__':
server = LimitedThreadedServer(
SessionService,
port=18861,
max_clients=0, # 0 means unlimited number of clients can connect to the server.
protocol_config={
'allow_public_attrs': True,
'allow_setattr': True,
'allow_delattr': True,
}
)
print("Server starting on port 18861...")
server._start_in_thread()
Save this as blender_rpyc.py and then run Blender with:
blender --python path/to/blender_rpyc.py.
One can also load the script after opening Blender, and going to “Scripting” tab (on center-right at the top).
Then to connect to it from Julia:
begin
using PythonCall, CondaPkg
CondaPkg.add("rpyc")
bpy = let
rpyc = pyimport("rpyc")
conn = rpyc.connect("localhost", 18861)
conn.root.execute("import bpy")
conn.root.get("bpy")
end
end
This is a demo of this workflow, from Pluto.jl
Any suggestions or issue reports are welcome here!
The script to initialize rpyc if its not installed, is adapted from the workflow here: Running Julia inside Blender through VSCode (using PythonCall/JuliaCall) which required using juliacall (which in my testing, had issues with GPU-related packages, and frequently crashed Blender in general)