Python Integration Notes

Introduction

Blockpad integrates with Python to run Python scripts and use Python libraries within Blockpad files.

Blockpad uses the interpreter on your local computer. This is different from Excel's Python integration, which entirely relies on the Microsoft cloud. In Blockpad, no data ever leaves your computer unless you direct it to.

Because Blockpad uses your local interpreter, you can use any Python libraries you have downloaded.

Some warnings

Blockpad does not sandbox scripts that it runs in Python. That is, Python running in Blockpad can do all of the same things that Python running somewhere else can, including modifying files on your computer.

Because of this, you should only run Python scripts that you trust. Blockpad will confirm you trust a file before allowing Python resources in a file to run.

When working with large Python resources in Blockpad, it can become important to think of memory and processor utilization. Please keep in mind:

  • Python objects are kept alive in the Python process as long as they are referenced in a Blockpad file. If you have large or many objects retained in formula results, this may cause large memory usage.
  • If you use large Python arrays in an array formula, you can cause slow behavior as Blockpad will have to copy over many values from Python.
  • Python function calls are allowed to run indefinitely, but you have the option to cancel them with the cancel bottom to the bottom left.

The Blockpad Python integration has special behaviors with the Pint Python library around handling units (read more below). Any units calculations done in a Python script are handled using Pint, not Blockpad.

Basics

You can access Python from the functions in Library.Scripts.Python. Because the scripts library is included by default, you can just use Python as a shortcut. You can also create a shortcut name, like py = Python.

  • Eval() - runs a Python expression and returns the result.
  • Exec() - runs a Python script and returns a key-value object of the variable values.
  • Module() - creates a reference to a Python module, which you can use to run functions on Blockpad values.
  • InterpreterInfo - returns the file path of the python interpreted Blockpad is linked to.
Eval()

Runs a Python expression in "eval" mode and returns the result.

The first input is a Python expression as a text value. The second optional input is a key-value object that assigns values to variables used in the expression.

Examples:

  • Python.Eval("2+2")
  • Python.Eval("a+b", {a: 4, b: 10})
  • Python.Eval("a+b", {a: A_1, b: B_1}) (where A_1 and B_1 are values in the Blockpad document)
  • Python.Eval("a+b", {a: 4m, b: 10ft})

Note that the example with units uses the Blockpad-Pint integration, which you can read about below.

See the Eval page for more.

Exec()

Runs a Python script in "exec" mode and returns a key-value object of all the resulting variable values.

The first input is a Python script as a text value (you can store long text values by pasting them into an inline field). The second optional input is a key-value object that assigns values to variables used in the expression.

When typed in directly, the text value needs to include "\n" to mark the end of a line. If the script is put into an inline field, then the lines of text in the field will automatically have the "\n" marked.

Example:

  • Python.Exec("a=4\nb=a**2")
  • Python.Exec("n=1\nfor i in range(1, a):\n n *= 2", {a: 4})
  • Python.Exec(TextJoin("\n", ["n=1", "for i in range(1, a):", " n *= 2"]), {a: 4})
  • Python.Exec(code_1, {a: a_1}) (where code_1 and a_1 are values in the Blockpad document)

To access the outputs from Exec(), name the equation that uses it, reference the name, and use dot notation. E.g. pyResults_1 = Python.Exec("a=4\nb=a**2"), then b_1 = pyResults_1.b = 16

In practice, this is the best process for using Exec():

  1. Write the python code in a text editor like VScode.
  2. Copy/paste the code into an inline field.
  3. Name the inline field and reference it as the text value for Exec().
  4. Use the second input for Exec() to pass in any Blockpad values into the python code.

See the Exec page for more.

Module

Gets a reference to a Python module that you can use on Blockpad values. The particular Python module needs to be downloaded on your computer.

Example:

  • Get the reference - np = Python.Module("numpy")
  • Use the reference - matrix_1 = np.diag([1, 2, 3])
  • InterpreterInfo

    Gets the file path of the Python interpreter that Blockpad is linked to.

    Using Blockpad as a Jupyter-style notebook

    The current python integration enables something like a Jupyter-style notebook.

    The basic idea:

    • Use inline fields to store the script as text and display it.
    • Use the Exec() function to run the scripts stored in the fields.
    • Use the results from the Exec() functions for further work.
      • Reference them for further calculations.
      • Use them as inputs to future scripts.
      • Use them with the Image() formula to display results (see below).

    Python values in Blockpad

    Simple Python values are translated directly as Blockpad values. These include numbers, strings, and booleans, and datetime values.

    Likewise, simple Blockpad values are translated into corresponding numbers, strings, and booleans in Python.

    Other Python values are kept alive in a pool in Python so that they may be passed back and forth. This allows functions, arrays, tuples, and other objects to be handled, because these values cannot be directly translated between Blockpad and Python.

    For these objects, Blockpad keeps a reference to the original Python object, which you can use to get members, call Python functions, and access arrays seamlessly from Blockpad formulas.

    Units

    If you have the Pint library in Python downloaded, then Blockpad number values with units will be translated into Pint values with units if possible, and vice versa. If a python script does arithmetic with units, it is using Pint units intelligence, and not Blockpad units intelligence to do that arithmetic.

    In some cases it may be preferable to strip units in Blockpad before passing them into python. Then you can add the units back to the python result.

    Displaying images generated with Python

    You can create images generated from python that update when values change.

    The basic flow for this is to use Python to generate an image definition as text or as a matrix, then use the Image() function or a formula-defined image.

    The Image() function

    The Image() function returns an image given an image definition and a pixel format.

    For raster pixel formats (BGRA, BGR, RGB, and GRAY), the image definition is expected as an array of arrays, with the numbers inside representing the color values from 0 to 255. (The array of arrays can be a large array of 3-item arrays, e.g. [[rVal1, gVal1, bVal1], [rVal2, gVal2, bVal2], etc] or as an array of 3 large arrays. E.g. [rVal1, rVal2, ...], [gVal1, gVal2, ...], [bVal1, bVal2, ...]].)

    For SVG, the image definition is expected as a string.

    Examples:

    • Library.Docs.Image(Identity(1000)*255, "GRAY")
    • Library.Docs.Image(MakeArray(255, 255, (i, j) => Max(i, j)), "GRAY")
    • Library.Docs.Image(MakeArray(255, 255, (i, j) => [i, j, Min(i, j)]), "RGB")
    • Library.Docs.Image([Sheet1.B2:L12,Sheet1.B16:L26,Sheet1.B30:L40], "RGB")
    • Library.Docs.Image('<svg><path fill="none" stroke="#150" d="M1 5 L75 200 L225 200 Z"/></svg>', "SVG")

    For displaying images from Python specifically, you want one of the results from a Python Exec() function to be an array of arrays in a format that you specify. Note that Blockpad does not automatically know if the array is BGR, RBG, etc.

    Formula-defined images

    The Image() formula outputs the image in it's exact size or scaled down to fit. To have more control over the size and placement of an image, you can use the Image() function to define an inserted image in Blockpad.

    Control image with formula:
    1. Select the Insert tool in the toolbar (or press ctrl+k).
    2. Select "Image" and click OK.
    3. Click Use Formula.
    4. Type in a formula that uses the Image() function (as in the examples above).
      • You can also reference a named value that results in an image.
    5. Click OK
    Image from Python examples:
    Display an image saved on your computer:

    (The opencv-python library needs to be installed for the following code to work).

    Insert the below into Blockpad as formulas:

    cv2 = Library.Scripts.Python.Module("cv2")

    image data = cv2.imread("C:\\path to image... .jpg")

    Library.Docs.Image(image data, "bgr")

    Create a chart in Blockpad using matplotlib:

    (The matplotlib and stringIO libraries need to be installed for the following example to work.)

    The image below can be summarized to these steps:

    1. Have your plot data in Blockpad (xSeries and ySeries in the example).
    2. Write the python code:
      • Takes the data and generates a chart using matplotlib.
      • Use stringIO to create a variable for an SVG string of the chart.
    3. Put the python code into an inline field, and name it.
    4. Run the Exec() function on the code.
    5. Insert an image, and define it using a formula.
    6. That formula uses the Image() function and references the svg string from the Exec() code.

    Special libraries

    Blockpad has special behaviors with the following libraries, if they are installed with your Python interpreter:

    Pint

    Blockpad sends and receives numbers with units to and from Python via quantity types in the Pint library.

    The Pint library needs to be downloaded for this to work. Also, note that units intelligence done inside of the Python script is handled with Pint, not Blockpad.

    Numpy

    Blockpad is optimized to be able to receive binary data from NumPy arrays for displaying images.

    Future plans

    Future plans for the Python integration include:

    • Ability to define virtual environments for a specific Python version and specified set of pip libraries, that will have consistent behavior across Windows, Mac, and web platforms
    • Ability to run Python in a secure sandbox
    • Built-in SymPy to improve solver section features and provide other math features
    • Python script frames, both embedded in reports and as a top level frame.

    If you have any thoughts about how we should develop the Python integration in the future, please contact us. We would love to hear from you as we continue to develop this feature.


    See also: