Skip to content

Using JISA in Python

William Wood edited this page Sep 2, 2022 · 32 revisions

Using JISA in Python

JISA packages can be used in Python by installing the pyjisa package. This makes use of jpype which "hooks" into the Java Virtual Machine to create and interact with Java objects from inside Python. Therefore, you will also need Java 11 (or newer) installed to uses JISA in Python.

The end result is that by installing the pyjisa package in Python you will be able to import JISA classes as if they were in Python packages themselves.

  1. Installing PyJISA
  2. Importing JISA into Python
  3. Specifying Java Path
  4. Note on GUIs
  5. Callbacks and Lambdas
  6. CLI Example
  7. GUI Example

Installing PyJISA

To install the pyjisa package you can use pip to get it from github like so:

pip install git+https://github.com/OE-FET/PyJISA.git

This will install both pyjisa and (if you need it) jpype1. Assuming this worked then you should now be able to import and start pyjisa like so:

import pyjisa
pyjisa.load()

Calling pyjisa.load() is important as this is what starts up the Java Virtual Machine and hooks your current Python session into it, enabling you to import JISA classes.

To update the copy of JISA used by PyJISA to its latest verion, you can use updateJISA() like so:

import pyjisa
pyjisa.updateJISA()

This will redownload JISA.jar from github, even if you already have the latest version. Therefore, it's a bad idea to incorporate this into a JISA program. Just run it manually in a python terminal when you want to update.

Importing JISA into Python

To start using JISA packages you need to import pyjisa and then call load() from it, then you can import JISA classes as you would any other Python classes:

import pyjisa
pyjisa.load()

from jisa.gui import GUI

GUI.infoAlert("Hello World!")

Specifying Java Path

If the wrong installation of Java is chosen by pyjisa when you call load() or it can't find it then you can specify which one to use by supplying the path to load() like so:

import pyjisa
pyjisa.load("/path/to/java")

For example, on Linux:

import pyjisa
pyjisa.load("/usr/lib/jvm/java-13-openjdk-amd64")

or on Windows:

import pyjisa
pyjisa.load("C:\\Program Files\\AdoptOpenJDK\\jdk-13.0.2.8-hotspot")

Note on GUIs

For whatever reason, Python terminates when it reaches the end of a script, even if the JISA GUI thread is still running. For this reason, if you were to make something like this:

import pyjisa
pyjisa.load()

from jisa.gui import Plot

plot = Plot("Plot", "X", "Y")
plot.setExitOnClose(True)
plot.show()

Then the script will terminate immediately after plot.show() has been called resulting in the plot window opening then immediately closing as the script terminating also terminates the JVM.

The normal behaviour you'd expect from this is for the script to remain running until the plot is closed because of plot.setExitOnClose(True). To make it behave like this, you will need to make the Python script block after creating and showing the GUI. You can do this by using GUI.waitForExit() like so:

import pyjisa
pyjisa.load()

from jisa.gui import Plot, GUI

plot = Plot("Plot", "X", "Y")
plot.setExitOnClose(True)
plot.show()

GUI.waitForExit()

This will cause the current Python thread to wait indefinitely. When the plot window is closed, JISA will call System.exit(0) (on the JVM) which will cause the waitForExit() call to be interrupted and return, thus also ending the Python script.

Callbacks and Lambdas

JPype has a bit of a limitation when it comes to passing function-type objects as arguments. To get around this, the pyjisa package provides a number of methods to wrap a Python method reference or lambda so that JISA methods can accept them.

For instance, the various methods to add buttons to GUI elements require a SRunnable object which you would normally define in Java with a method reference or lambda. To turn a Python method reference or lambda into a SRunnable you can use the SRunnable(...) factory in the pyjisa package. For example, adding a toolbar button to a Grid element using a method reference:

import pyjisa
pyjisa.load()

from pyjisa import SRunnable
from jisa.gui import Grid, GUI

grid = Grid("Grid Title")

def onClick():
    GUI.infoAlert("Button Clicked!")


grid.addToolbarButton("Click Me!", SRunnable(onClick))

or by using a lambda:

import pyjisa
pyjisa.load()

from pyjisa import SRunnable
from jisa.gui import Grid, GUI

grid = Grid("Grid Title")

grid.addToolbarButton("Click Me!", SRunnable(lambda: GUI.infoAlert("Button Clicked!")))

Similarly, there are Predicate(...) and Evaluable(...) methods for Predicate and Evaluable objects respectively.

CLI Example

import pyjisa
import time

pyjisa.load()
from jisa.devices import K2450
from jisa.addresses import GPIBAddress
from jisa.experiment import ResultList, Col
from jisa.maths import Range

def main():

    try:
        smu = K2450(GPIBAddress(24))
    except:
        print("Error connecting to SMU")
        quit()

    data  = ResultList(Col("Voltage", "V"), Col("Current", "A"))

    smu.setVoltage(0.0)
    smu.turnOn()

    for v in Range.linear(0, 5):
        print("Measuring at %s V..." % v)
        smu.setVoltage(v)
        time.sleep(0.5)
        data.addData(smu.getVoltage(), smu.getCurrent())

    smu.turnOff()
    smu.close()

    print("Complete, results:")
    data.outputTable()


main()

The resulting output would be something like:

Measuring at 0.0 V...
Measuring at 1.0 V...
Measuring at 2.0 V...
Measuring at 3.0 V...
Measuring at 4.0 V...
Measuring at 5.0 V...
Complete, results:
+==============+==============+
| Voltage [V]  | Current [A]  |
+==============+==============+
| 0.000000     | 0.000000     |
+--------------+--------------+
| 1.000000     | 0.004792     |
+--------------+--------------+
| 2.000000     | 0.009183     |
+--------------+--------------+
| 3.000000     | 0.013541     |
+--------------+--------------+
| 4.000000     | 0.018464     |
+--------------+--------------+
| 5.000000     | 0.022093     |
+--------------+--------------+

GUI Example

import pyjisa
import time
pyjisa.load()

from pyjisa import SRunnable
from jisa.gui  import Grid, Plot, Table, GUI
from jisa.devices import K2450
from jisa.addresses import GPIBAddress
from jisa.experiment import ResultList, Col
from jisa.maths import Range

def main():

    try:
        smu = K2450(GPIBAddress(24))
    except:
        GUI.errorAlert("Error connecting to Keithley 2450")
        return

    data   = ResultList(Col("Voltage", "V"), Col("Current", "A"))
    plot   = Plot("Plot of Results")
    series = plot.createSeries().watch(data, 0, 1)
    table  = Table("Table of Results", data)
    grid   = Grid("Voltage Sweep", plot, table)

    def runMeasurement():

        data.clear()
        series.clear()

        smu.turnOff()
        smu.setVoltage(0.0)
        smu.turnOn()

        for v in Range.linear(0, 5):
            smu.setVoltage(v)
            time.sleep(0.5)
            data.addData(smu.getVoltage(), smu.getCurrent())

        smu.turnOff()

    def save():

        file = GUI.saveFileSelect()

        if file is not None:
            data.output(file)


    grid.addToolbarButton("Start Measurement", SRunnable(runMeasurement))
    grid.addToolbarButton("Save Data", SRunnable(save))
    grid.setExitOnClose(True)
    grid.show()

    GUI.waitForExit()


main()

Resuling in the following when the "Start Measurement" button is pressed:

Clone this wiki locally