Python
This article covers two aspects of python scripts applied to Jmol. The first is initiating Jmol from a python script, similar to using the shell scripts Jmol.bat or Jmol.sh in Windows or Linux. The second aspect is using sockets via the Jmol sync command (see also Sockets and the Inter-Application Communication protocols) and the python socket standard library to communicate between a Jmol instance and a python script.
Python script starts Jmol
The preferred approach for starting another application or running an OS command from python is the subprocess standard library. The following script will start Jmol in a subprocess with an initial ‘’sync -8008’’ Jmolscript command, thus preparing it to act as sync master for socket communication on port 8008.
import subprocess as sp from platform import system as osname port = 8008 if osname()== 'Windows': jmol_path = 'c:\\compchem\\jmol14\\' java_path ='"C:\\Program Files (x86)\\Java\\jre1.8.0_341\\bin\\' elif osname()== 'Linux': jmol_path='//home//compchem//jmol14//' java_path='"//usr//bin//' java = java_path + 'java" -jar ' jmol = jmol_path + 'Jmol.jar -o -j "sync -' + str(port) + '"' jmol_cmd = java + jmol print(jmol_cmd) # The 'Windows' block ignores stdout and stderr in deference to Win10 # hanging after one command received. The Linux block works in Ubuntu. if osname() == 'Windows': jmol = sp.Popen(jmol_cmd, shell=False, stdin=sp.PIPE, bufsize=0, universal_newlines=True, stdout=sp.DEVNULL, stderr=sp.DEVNULL) elif osname()== 'Linux': jmol = sp.Popen(jmol_cmd, shell=False, stdin=sp.PIPE, bufsize=0, universal_newlines=True, stdout=sp.PIPE, stderr=sp.PIPE)
The script is adapted for Windows or Linux, both in terms of paths and with regard to a Windows idiosyncrasy noted in the comment. The Jmol app will respond to a socket request from a second Jmol app or a python script or can be controlled directly from its menu or console.
Python–Jmol communication
The python socket standard library provides socket communication methods. This minimal script will connect to the Jmol app using the prescribed “magic” terms.
import socket sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect(('localhost', port)) sock.send('{"magic":"JmolApp","role":"out"}\n'.encode('utf-8'))
Jmol will respond with the string “OK” over the socket and the connection is good to go. Jmol users have posted more extensive python modules on github (GWhitwell, MFusè) and one is also provided here File:Jmolsocket.zip. In these, the approach is to start Jmol, set up the connection and send a JSON-encoded message such as {"type" : "command", "command" : "background white"} to the Jmol app, which will process the message and execute the command. Subsequently, the Jmol app will reply with three or more JSON-encoded reply messages, which the python module will receive. As implemented, this is a serial process – send a command embedded in a JSON-encoded message {"type" : "command", "command" : "Jmol relevant stuff"} and wait for a reply until timeout.
Communicating arbitrary data
The Sockets page of this Wiki demonstrates a method to have a client Jmol load a structure and then send an xyz format listing of that structure back to the server. This is an example of communicating arbitrary data. When using a python-Jmol serial send/receive process, the technique described for Jmol-Jmol fails, probably because the receive is initiated after the Jmol app has already sent the message.
As noted above, the Jmol app acknowledges socket communication with a series of "reply" messages. These include simple statements that scripts were received and completed. Some commands, including load and print generate replies with a format containing {reply":"ECHO: ..."} where the ECHO: is followed by pertinent content. For the command load $CC, the ECHO: string is C2H6 just as on the Jmol console. For the command print write("coords") the ECHO string will also match the xyz format structure listing as on the console. It is straightforward to isolate and parse these messages into data to be used in the python script. An example is provided in jmolsocket.py as the demo() function.