Using Python in Xojo with MBS Plugins

For MBS Xojo Plugins version 24.3 we add a new Python plugin part. The MBS Xojo Python Plugin and the new PythonMBS class allows you to evaluate Python code in Xojo and run scripts. This enables you to integrate existing Python code and use it in Xojo within your application. That may be a quick calculation to calculate a checksum, process some text, JSON or XML data or to utilize an existing script by running it within your application. Python comes with hundreds of available modules that you can import and use.

Initialization

Depending on the platform, you install Python libraries in different ways. For macOS you can use Homebrew command line tools to install the python package. For Windows you can download the official Python installer and run it. For Linux you use your package manager to get the python package installed. When done, you have on each system a folder with the libraries and you tell the plug-in where to find them.

For macOS you usually check look for the Python.framework:

/opt/homebrew/Cellar/python@3.12/3.12.3/Frameworks/Python.framework or /usr/local/homebrew/Cellar/python@3.12/3.12.3/Frameworks/Python.framework

For Windows the path to the dll may include the version number, so please adjust as needed. If you like use the FolderItem class to check the names in the path in your script.

C:\Users\cs\AppData\Local\Programs\Python\Python312\python312.dll

For Linux the package is usually in the library path, so you can just refer to it by the name.

Let’s put it all together into a sample script:

Sub Opening()
	#If TargetMacOS Then
		Const path = "/opt/homebrew/Cellar/python@3.12/3.12.3/Frameworks/Python.framework"
	#ElseIf Targetwindows
		Const path = "C:\Users\cs\AppData\Local\Programs\Python\Python312\python312.dll"
	#ElseIf TargetLinux Then
		Const path = "libpython3.11.so"
	#Else
		#Pragma error Invalid Platform?
	#EndIf
	
	Var loaded As Boolean = PythonMBS.Load(path)
	
	If loaded Then
		// okay
	else
		MessageBox "Error: "+PythonMBS.LibraryError
	End If
End EventHandler

Check Version

Once you have Python library loaded, you can check the version with the PythonMBS.LibraryError property. You get back a string like “3.12.3 (main, Apr 9 2024, 08:09:14) [Clang 15.0.0 (clang-1500.3.9.4)]” to describe the version with compilation date and compiler used. For Windows the output may be “3.12.3 (tags/v3.12.3:f6650f9, Apr 9 2024, 14:05:25) [MSC v.1938 64 bit (AMD64)]”.

Sub CheckVersion()
	MessageBox "Version: "+PythonMBS.LibraryVersion
End Sub

Print Hello World

Let us run our first Python code. We create a new environment with new PythonMBS. But usually you subclass our class to add events as needed. You can have multiple objects around and each of them has their own local variables. Inside that environment we can run script lines with Run method and passing the script code. The script may be as simple as this:

print("Hello World")

We only call print function and pass the text “Hello World”. Print is very useful to output debug messages. Here is the sample code:

Var p As New Python
			
var ScriptCode As String
			
Call p.Run "print(""Hello World"")"

Our plugin raises the Print event to provide the text to you, so you have a print event like this:

Sub Print(text as String) MainWindow.PrintLog.AddText Text End EventHandler

When you are done with a python environment, you can release the object. But it is absolutely fine to create it once on start of the application and use the same one throughout the application. If you use threads, please please have one instance per thread to avoid accessing the same one from different threads.

When we run above Python script using the given Xojo code, the output will be “Hello World” as returned in the Print eventz. There is a Chr(13) on the end to indicate the new line.

Process JSON in Python

Let’s do something in Python like processing some JSON. For that we use the json module in Python to get the json.loads function to parse JSON and json.dumps to encode to JSON. The script will decode the given JSON in the variable InputValues, append a new number and then store the final JSON text in OutputValues variable.

import json

j = json.loads(InputValues)
j.append(5)
OutputValues = json.dumps(j)

In our Xojo code we put in the variable into Value() method to fill. Later we can read with Value() again to get the output. Our plugin can pass numeric and text values directly and converts if needed. This includes passing arrays and dictionaries.

	Var p As New Python
			
	p.Value("InputValues") = "[1,2,3]"
	p.run Code
			
	Dim result As Variant = p.Value("OutputValues")

When you run this, you see the JSON [1, 2, 3, 5] in the result as string. We could also pass variant arrays with the numbers directly.

Evaluate

If you have a Python environment setup, you can evaluate something at any time. Like using XojoScript in your application to perform some calculations at runtime, just pass the text to evaluate to the Evaluate() function. Let’s setup somewhere a global variable pointing to a Python environment:

	p = New Python
	Call p.run "import math"

Later we can use it to perform calculations:

	Var r As Variant
	r = p.Evaluate("1+2")

You may even use that in a method to do something in Xojo. Or you add your own custom functions to Python to call back to Xojo. Since we imported math module directly above after creating the environment, we can now use the math module in expressions:

	v = p.Evaluate("math.sqrt(5)")

Please try yourself in 24.3 pre-release of MBS Xojo Plugins.

6 Likes

This is very neat Christian. I actually did something like this a couple of years ago before MBS had this feature. My app wraps 7GB of python libraries for our machine learning work.

The problem I found was that the app was not self-contained, requiring every user’s machine to have a python installation, then you run into different python and library versions, etc.

What I ended up doing is installing python using Miniconda (Latest Miniconda installer links by Python version — Anaconda documentation) for both x86 and arm64, then pip installing all required libraries. You can then move the entire thing into your app’s package, set the paths to be app-relative, and now you have a uniform self-contained installation.

Output (text, pdf graphics, etc) back to the wrapper app is done via temp files so not as elegant as the MBS approach. I imagine with some path resetting PythonMBS could also be made to work with an in-app Miniconda installation.

3 Likes