The Blender Command Port - Blender Python Server and Client

This document refers to the newest version of the Command Port Blender branch on launchpad.
For other versions see here:

The Blender Command Port makes it possible to start Blender in Python server mode. Clients can connect and send Python commands via the command port to the server.

  • The Command Port allows to edit objects simultaneously using the Blender GUI and one or more clients connected to Blender via the command port.
  • The patch also includes blash, a Blender Python shell client.
  • blash can be used with the emacs python mode - the result is a formidable Blender Python development environment.
  • Clients can be written in any language: lisp, Python, Perl, C, C++, Java etc.
  • The patch was only tested on Debian sid until now but should run on any platform after a little customisation.
  • If you have comments please send me an email.


Console, Blender Server, emacs client and shell client

Links

Example - a simple pyramid

The following example shows how the command port can be used to model a simple pyramid:

  • start the Blender server in some xterm
    $ blender -w -p 10 10 800 600 --command-port 6789 &
    and erase the default cube
  • start blash in another xterm
  • $ blash --port 6789
    This is Blash - the GNU BLender-Again SHell :)
    Connection to Blender Server established.   (IP address: 127.0.0.1, port: 6789)
    
  • enter blender python commands
  • from Blender import *
    
    vertices = [[1, 1, 0], [-1, 1, 0], [-1, -1, 0], [1, -1, 0], [0, 0, 1.27]]
    faces    = [[3, 2, 1, 0], [0, 1, 4], [1, 2, 4], [2, 3, 4], [3, 0, 4]]
    
    mesh = Mesh.New('mesh')
    mesh.verts.extend(vertices)
    mesh.faces.extend(faces)
    
    scene  = Scene.GetCurrent()
    object = scene.objects.new(mesh, 'object')
    Redraw()
    
    bye
    

Dependencies

Installation

  • make a directory to build Blender
    BCP=$HOME/bcp
    mkdir -p $BCP
    cd $BCP
    
  • download the sources of the blender command port branch from the Blender Command Port page at launchpad.
    bzr branch lp:~diresu/blender/blender-command-port blender
  • build blash and blender
    cd $BCP/blender
    scons WITH_COMMAND_PORT=true blash
    
  • the (linux) binaries
    $BCP/install/linux2/blender
    $BCP/install/linux2/blash
    

Note: Of course it is also possible to generate a patch and merge it into an existing blender source tree.

If you have problems to build Blender you might have a look on the Building Blender section on the Blender home page. Blender has lots of Dependencies and, depending on the platform, there are different ways to build Blender. If you decide to use scons but only have an old version installed, you might try to use the scons version distributed with the Blender sources and use the command python scons/scons.py WITH_COMMAND_PORT=true blash at the place of scons WITH_COMMAND_PORT=true blash to compile Blender [Thanks to Peter Vittali for this hint!]. For more usable output you could add the following options to the scons command line: scons -k BF_FANCY=0 BF_QUIET=0 <...other options...>. If you still need help, try the Coding Blender forum or drop me an email :)

Blash Meta Commands

Some commands - the blash meta commands - are handeled directly by blash and not send to the Blender server:

help                  - Print a simple help text (not implemented yet).
bye, quit, exit       - Disconnect from the Blender server and exit blash.
.shell, .single, .s   - Switch to shell mode.
.eval, .e             - Switch to eval mode.
.file, .f             - Switch to file mode.
.                     - All text until the next line consisting of a dot only
                        are send to Blender for evaluating in file mode.

Blash Input Modes

  • Shell Mode (prompt: >>>)

    Shell mode is the blash default mode and corresponds to the interactive Python shell. If the first line of an input is a complete Python statement, it is evaluated immediately and the result, stdout and stderr are printed; if the first line is not a complete Python statement, all following lines are appended to the input until an empty line is entered. The complete entered text is evaluated and the result, stdout and stderr are printed;

    Shell mode is convenient when testing Blender Python code. It is inconvenient when there are empty lines inside of the entered code.

    Shell mode is the default mode when blash is started. It is possible to switch from another blash input mode to shell mode by entering .shell or simply .s.

    >>> print "This is a one-line command."
    This is a one-line command.
    >>> def foo():
    ...   print "This is a multi-line command."
    ... 
    >>> 
    

    Note: The three dots (...) at the beginning of the lines of a multi line command are called continuation prompt and have the same meaning as in the Python shell: they signal that the current line is the 2nd, 3rd, ... line of a multi line input.

  • Eval Mode (prompt: EM>)

    In Eval mode only single expressions are allowed. Every expression has to be finished with a line consisting only of a single dot. The entered expression is evaluated and the result is printed.

    It is possible to switch from another mode to eval mode by entering .eval or simply .e. When blash is started with the command line option --eval-mode or -e, blash will start up in eval mode immediately - or better should start in eval mode immediately ... here comes my first bug report: For the moment, even when started with --eval-mode or -e, file mode is entered... How can I commit a patch to a patch correcting this?

     
    >>> .eval
    ...eval mode
    EM> 123 + 321
    ... .
    444
    EM> "this evaluates to a string"
    ... .
    'this evaluates to a string'
    EM> .shell
    ... .
    ...shell/single mode
    >>> 
    
  • File Mode (prompt: FM>)

    In file mode all lines until the next line consisting only of a single dot are send to the Blender server. The whole text, which can consist of any sequence of Python code, is evaluated by the Blender server. This is convenient when a long sequence of commands (which might also include empty lines) should be evaluated and the result of expressions included in the code is not of interest.

    It is possible to switch from another blash input mode to file mode by entering .file or simply .f. When blash is started with the command line option --file-mode or -f, blash will start up in file mode immediately.

    >>> .file
    ...file mode
    FM> 
    ... 
    ... from Blender import *
    ... 
    ... vertices = [[1, 1, 0], [-1, 1, 0], [-1, -1, 0], [1, -1, 0], [0, 0, 1.27]]
    ... faces    = [[3, 2, 1, 0], [0, 1, 4], [1, 2, 4], [2, 3, 4], [3, 0, 4]]
    ... 
    ... mesh = Mesh.New('mesh')
    ... mesh.verts.extend(vertices)
    ... mesh.faces.extend(faces)
    ... 
    ... scene  = Scene.GetCurrent()
    ... object = scene.objects.new(mesh, 'object')
    ... Redraw()
    ... 
    ... .
    None
    FM> .shell
    ... .
    ...shell/single mode
    >>> 
    

    From shell mode it is also possible to enter a sequence of lines in file mode by starting and terminating the input with a single dot line:

    >>> .
    ...single text
    SF> from Blender import *
    ... 
    ... vertices = [[1, 1, 0], [-1, 1, 0], [-1, -1, 0], [1, -1, 0], [0, 0, 1.27]]
    ... faces    = [[3, 2, 1, 0], [0, 1, 4], [1, 2, 4], [2, 3, 4], [3, 0, 4]]
    ... mesh = Mesh.New('mesh')
    ... mesh.verts.extend(vertices)
    ... mesh.faces.extend(faces)
    ... scene  = Scene.GetCurrent()
    ... object = scene.objects.new(mesh, 'object')
    ... Redraw()
    ... .
    None
    >>>
    

The BCP Protocol

For exchanging commands and evaluation results via the BCP socket a very simple protocol is used: The evaluation mode, the Python command input, the result of the evaluation, stdout and stderr, everything is sent forth and back packed into a simple c-string byte sequence via the command port socket.

The following lines explain the simple scheme used to pack meta commands, integers and strings into a c-string and the order used in command and result packages to pack the necessary information.

The encoded format can be seen when the Blender server is started with the debug option --debug bcp:2 or - when a hex dump is wished also - --debug bcp:3 :

blender --debug bcp:2 --geometry 800x600+10+10 --bcp 6789 &

Here some examples of the simple BCP coding scheme:

A simple Blash read / evaluate / print loop protocol:

$ blash --port 7777
This is Blash - the GNU BLender-Again SHell :)
Connection to Blender Server established.   (IP address: 127.0.0.1, port: 7777)
>>> 123
123
>>> 

And (some of) the corresponding (for readability slightly edited) Blash server output:

$ blender --debug bcp:2 --geometry 800x600+10+10 --bcp 7777
[DEBUG] Turning debugging on for module `bcp' (level: 2).
Starting the Blender command port - listening on port 7777.
Handling client 127.0.0.1
...
>>> the packed command package: >>>#cS
#s3
123
<<<
...
>>> the packed result package: >>>#i0
#s0

#s4
123

#s0

<<<
...

And here the command and result packages analysed:

  • Blash input:

    123
  • The command package send to the Blender server (everything between >>> and <<<):

    >>>#cS
    #s3
    123
    <<<

    Interpretation of the command package:

    • The first line codes the evaluation mode to use: #cS stands for shell mode, #cE for eval mode and #cS for file mode. The prefix #c stands for BCP meta command.
    • The rest of the package contains the blash command input:
      • #s3 - a string (#s) of length 3 (3) follows
      • 123 - the string itself
  • The result package send back to blash:

    >>>#i0
    #s0
    
    #s4
    123
    
    #s0
    
    <<<

    Interpretation of the result package:

    • The first line contains the result code of the evaluation: #i signals that the rest of the line is an integer, 0 is the integer itself: 0 for a successful evaluation.

    The rest of the package consists in three strings coded in the same way already seen when looking at the command package. The order of the strings is as follows:

    • result string - the result string is only used in evaluation mode. In shell mode the result is printed to stdout and therefore returned in the stdout string and not the result string. In our case the result string is empty and of length 0.
    • stdout string - everything printed to stdout during the evaluation of the command. In shell mode also the result is printed to stdout and therefore returned in the stdout string - In our case the whole stdout string is the result: 123. It has the length 4 as it is terminated with a newline character.
    • stderr string - the last string contains everything printed to stderr during the evaluation of the command. In our case nothing was printed to stdout.

    In summary the result contained the following information:

    • result code: 0 (success)
    • result string (only used in eval mode): ""
    • stdout string (contains also the result when in shell mode): "123\n"
    • stderr string: ""
  • Blash output:

    123

See the next paragraph for a very simple Python BCP client.

The very simplest "hello world" Python client :)

Here comes a very very simple Python "hello world" client to answer to the posting of Chris Want - thank you :)

  • Start the Blender server in some xterm
    $ blender --geometry 800x600+10+10 --bcp 7777
  • Start python in some other xterm
    $ python
  • Enter the following commands into the Python shell:
    import socket
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect(("127.0.0.1", 7777))
    sock.send('#cS\n#s13\n"hello world"\n\0')
    sock.recv(1024)
    sock.close()
    
  • ...and here comes the resulting protocol:
    $ python
    Python 2.4.4 (#2, Apr 26 2007, 00:02:45) 
    [GCC 4.1.2 20061115 (prerelease) (Debian 4.1.1-21)] on linux2
    Type "help", "copyright", "credits" or "license" for more information.
    >>> import socket
    >>> sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    >>> sock.connect(("127.0.0.1", 7777))
    >>> sock.send('#cS\n#s13\n"hello world"\n\0')
    24
    >>> sock.recv(1024)
    "#i0\n#s0\n\n#s14\n'hello world'\n\n#s0\n\n\x00"
    >>> sock.close()
    >>> 
    

Drawing a simple pyramid with a Python client

And here the simple pyramid example as simple python client:

  • The code:

    import socket
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect(("127.0.0.1", 7777))
    sock.send("""#cF
    #s323
    from Blender import *
    vertices = [[1, 1, 0], [-1, 1, 0], [-1, -1, 0], [1, -1, 0], [0, 0, 1.27]]
    faces    = [[3, 2, 1, 0], [0, 1, 4], [1, 2, 4], [2, 3, 4], [3, 0, 4]]
    mesh = Mesh.New('mesh')
    mesh.verts.extend(vertices)
    mesh.faces.extend(faces)
    scene  = Scene.GetCurrent()
    object = scene.objects.new(mesh, 'object')
    Redraw()
    
    \0""")
    sock.recv(1024)
    sock.close()
    
  • The protocol:

    $ python
    Python 2.4.4 (#2, Apr 26 2007, 00:02:45) 
    [GCC 4.1.2 20061115 (prerelease) (Debian 4.1.1-21)] on linux2
    Type "help", "copyright", "credits" or "license" for more information.
    >>> import socket
    >>> sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    >>> sock.connect(("127.0.0.1", 7777))
    >>> sock.send("""#cF
    ... #s323
    ... from Blender import *
    ... vertices = [[1, 1, 0], [-1, 1, 0], [-1, -1, 0], [1, -1, 0], [0, 0, 1.27]]
    ... faces    = [[3, 2, 1, 0], [0, 1, 4], [1, 2, 4], [2, 3, 4], [3, 0, 4]]
    ... mesh = Mesh.New('mesh')
    ... mesh.verts.extend(vertices)
    ... mesh.faces.extend(faces)
    ... scene  = Scene.GetCurrent()
    ... object = scene.objects.new(mesh, 'object')
    ... Redraw()
    ... 
    ... \0""")
    335
    >>> sock.recv(1024)
    '#i0\n#s4\nNone\n#s0\n\n#s0\n\n\x00'
    >>> sock.close()
    >>> 
    

fin