Python

From Jmol
Revision as of 00:50, 31 January 2023 by GWhitwell (talk | contribs) (Created page with "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 i...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

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. 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 send/receive serial process, the specific technique described fails, probably because the receive is initiated after the Jmol app has already sent the message.

Contributors

GWhitwell