User Guide Cancel

Create a Plugin with Python and QML | Substance 3D Sampler

  1. Substance 3D home
  2. Home
  3. Getting Started
    1. Getting Started overview
    2. Activation and licenses
    3. System requirements
    4. Shortcuts
    5. Importing Resources
    6. Report a bug
    7. Project Management
    8. Export
      1. Export overview
      2. Export Window
      3. Default Presets
        1. Default Presets overview
        2. Arnold 5
        3. Blender Cycles/Eevee
        4. Corona Renderer
        5. Enscape - Revit
        6. Keyshot9+
        7. Lens Studio
        8. Spark AR Studio
        9. Unity HDRP Standard
        10. Unity HDRP Specular
        11. Unity Standard
        12. Unity Specular
        13. Unreal Engine 4
        14. Redshift
        15. V-Ray Next
      4. Managing custom presets
      5. Managing Presets
  4. Interface
    1. Interface overview
    2. The Home Screen
    3. 2D and 3D Viewport
    4. Sidebars
    5. Panels
      1. Panels overview
      2. Project panel
      3. Assets panel
      4. Layers panel
      5. Properties panel
      6. Viewer Settings panel
      7. Shader Settings panel
      8. Channel Settings panel
      9. Metadata panel
      10. Export panel
      11. Physical Size Panel
      12. Exposed Parameters Panel
      13. Resources
    6. Tools and Widgets
      1. Tools and Widgets overview
      2. Sliders
      3. Color Picker
    7. Preferences
      1. Preferences overview
      2. Normal Format
      3. Layer Resolution
  5. Filters
    1. Filters overview
    2. Custom Filters
    3. Compound Filters
    4. Generators
      1. Generators overview
      2. Atlas Scatter
      3. Brickwall
      4. Cloth Weave
      5. Decal
      6. Embossing
      7. Embroidery
      8. Floor Tiles
      9. Gravel
      10. Panel
      11. Parquet
      12. Pattern
      13. Pavement
      14. Perforate
      15. Quilt Stitch
      16. Splatter
      17. Stonewall
      18. Surface Relief
      19. Weave
    5. Adjustments
      1. Adjustments overview
      2. Blur
      3. Brightness/Contrast
      4. Colorize
      5. Color Replace
      6. Color Variation
      7. Equalize
      8. Fill
      9. Hue/Saturation
      10. Invert
      11. Sharpen
      12. Vibrance
    6. Tools
      1. Tools overview
      2. Atlas Creator
      3. Atlas Splitter
      4. Channels Generation
      5. Channel Switch
      6. Clone Stamp
      7. Crop tool
      8. Delight (AI Powered)
      9. Height to AO
      10. Height to Normal
      11. Image To Material
      12. Make it Tile
      13. Match
      14. Multiangle To Material
      15. Normal to Height
      16. Paint Wrap *missing*
      17. PBR Validate
      18. Perspective Correction
      19. Tiling
      20. Transform
      21. Warp
      22. Warp Transform
      23. Upscale
    7. HDRI Tools
      1. HDRI Tools overview
      2. Color Temperature Adjustment
      3. Exposure
      4. Exposure Preview
      5. HDR Merge
      6. Line Light
      7. Nadir Extract
      8. Nadir Patch
      9. Panorama Patch
      10. Plane Light
      11. Shape Light
      12. Sphere Light
      13. Straighten Horizon
    8. Wear and Finish
      1. Wear and Finish overview
      2. Corrode
      3. Cracks
      4. Dirt
      5. Discarded Gums
      6. Dust
      7. Erode
      8. Metal Finish
      9. Moss
      10. Oxidate
      11. Paint
      12. Rust
      13. Scratch
      14. Snow
      15. Stylization
      16. Water
      17. Varnish
  6. Technical Support
    1. Technical Support overview
    2. Exporting the log file
    3. Configuration
      1. Configuration overview
      2. Retrieving the installation path
      3. Update Checker
      4. NVIDIA Driver Settings
      5. 3D Capture set-up on Linux  
    4. Technical Issues
      1. Technical Issues overview
    5. Data or project issues
      1. Data or project issues overview
      2. Import Substance Alchemist projects in Substance 3D Sampler
    6. Filter issues
      1. Filter issues overview
      2. Image to Material and Delighter are missing
      3. Image to Material visual artefacts
    7. Interface issues
      1. Interface issues overview
      2. Fonts are not displayed correctly
      3. Main interface is transparent
    8. Performance issues
      1. Performance issues overview
      2. Color picker takes long time to open the first time
      3. Interface lags when interacting with the layer stack or other elements
    9. Stability issues
      1. Stability issues overview
      2. Crash when exporting a material
      3. Crash when using the Image to Material or Delighter
    10. Startup issues
      1. Startup issues overview
      2. Application doesn't start on Linux
      3. Crash at start up - Old OBS version
  7. Features and workflows
    1. Features and workflows overview
    2. 3D Capture
    3. Export parametric assets
    4. End to end Physical Size Workflow
    5. Generative Workflow
    6. Texture Import
    7. Texture Generators
    8. Use As Bitmap
    9. Adobe Standard Material
  8. Pipeline and integrations
    1. Pipeline and integrations overview
    2. Environment variables
    3. Substance Send-to
  9. Scripting and Development
    1. Scripting and Development overview
    2. Manage installed plugins and scripts
    3. Create a Plugin with Python and QML
    4. Create a Script with Python
      1. Create a Script with Python overview
      2. Example Scripts
  10. 3D Capture
    1. 3D Capture equipment
    2. Camera settings - Exposure
    3. Camera settings - Focus
    4. 3D Capture lighting
    5. Cross-polarizing for 3D Capture
    6. Processing advanced 3D Captures
    7. Editing 3D Captured meshes
  11. Release Notes
    1. Release Notes overview
    2. All Changes
    3. Beta
    4. Version 4.5
    5. Version 4.4
    6. Version 4.3
    7. Version 4.2
    8. Version 4.1
    9. Version 4.0
    10. Version 3.4
    11. Version 3.3
    12. Old Versions
      1. Version 3.2
      2. Version 3.1
      3. Version 3.0
      4. Version 2020.3 (2.3)
      5. Version 2020.2 (2.2)
      6. Version 2020.1 (2.1)
      7. Version 2019.1
      8. Version 0.8.1
      9. Version 0.8.0
      10. Version 0.7.0
      11. Version 0.6.1
      12. 0.6.0
      13. 0.5.4
      14. 0.5.3
      15. 0.5.2
      16. 0.5.1
      17. 0.5.0
  12. FAQ
    1. FAQ  Overview

Create a Plugin with Python and QML

This guide describes how to create a simple autosave plugin with Python and QML.

Plugin structure

Sampler plugins require at least a Python and QML file in order to be imported, but other files can also be included such as images used for icons in the plugin panel. In the example below, there are 3 files:

  • autosave.py contains the logic of the plugin and determines how it works.
  • autosave.qml defines the appearance of the plugin in Sampler.
  • autosave.svg is a vector graphic that is used as the icon for the plugin.

Once you have the files needed for your plugin in a single folder, you can add the plugin to Sampler through Edit > Preferences > Plugins and Scripts. To learn more about managing plugins, go here.

Python

The code below is the full python file for the autosave plugin. Below is a brief description of what the code is doing, but the code also includes comments with more information:

  1. Import relevant modules.
    1. Qt is a multiplatform GUI toolkit. QtcCore, QtQml, and QtQuick are modules that we use to communicate between autosave.py and autosave.qml.
  2. Define a method save() that saves the project every X minutes.
  3. Create an autosave class. This class specifies how the save() method connects to the plugin UI so that parameters can change the behavior of the plugin
  4. Define a method register_qml_type() that performs the setup for the plugin.
  5. Call the plugin from within Sampler.
autosave.py
# Import QT & QML modules to create the UI 
from PySide2 import QtCore, QtQml, QtQuick 
# Import Sampler API 
import substance_sampler as ssa 
# Import other modules for this specific example 
import datetime 
import os 
import threading 
 
 
# Save the project every X minutes 
def save(interval): 
    global t 
    ssa.save_project() 
    if ssa.save_project(): 
        now = datetime.datetime.now() 
        print("Autosave: %d:%d:%d" % (now.hour, now.minute, now.second)) 
    t = threading.Timer(interval, save, [interval]) 
    t.start() 
 
 
t = None 
 
 
# Declare the API AutoSave 
class AutoSave(QtQuick.QQuickItem): 
    def __init__(self, parent=None): 
        super(AutoSave, self).__init__(parent) 
 
    # Declare a first API function 
    # This function can be called from the QML file 
    # with 2 arguments, one string and one integer 
    @QtCore.Slot(str, int) 
    def start_auto_save(self, default_path, interval): 
        if not ssa.save_project(): 
            ssa.save_project_as(os.path.join(default_path, "autosave.ssa")) 
        global t 
        t = threading.Timer(10, save, [interval]) 
        t.start() 
        print("Launch Autosave") 
 
    # Second function of the API 
    # With no argument 
    @QtCore.Slot(None) 
    def stop_auto_save(self): 
        global t 
        t.cancel() 
        print("Stop Autosave") 
 
 
# Function to declare the API and the panel 
# First argument is Python class of your API 
# Second argument is name of the API you will use in the QML file 
# Third and fourth is the API version. In this case, 1.0 
# Last is the name of the panel in Sampler UI 
def register_qml_type(): 
    QtQml.qmlRegisterType(AutoSave, "AutoSave", 1, 0, "AutoSave") 
 
 
# Execute the plugin in Sampler UI thread 
ssa.run_in_main_thread(register_qml_type)

QML 

The QML file defines the UI of the plugin. QML stands for Qt Markup Language and behaves similarly to other markup languages like HTML and XML. You can learn more about QML here.

The general structure of autosave.qml is as follows:

  1. Import modules.
    1. The Qt modules imported are necessary for the UI elements used in the file.
    2. The Autosave API class created in autosave.py is also imported. The QML file references this class on line 20.
  2. Create variables that need to be tracked. 
    1. autoSaveFolder is the folder where the Sampler file will be autosaved to.
    2. timing is the amount of time in seconds between autosaves.
    3. textColor is used so that the color of text in the plugin UI can be updated in a single place.
  3. Instantiate the Python API
  4. Define the UI.
    1. This includes hooks to the python API created in autosave.py. For example:
      1. Line 47 updates the timing variable value within the QML file whenever the "Autosave every (min):" element is changed.
      2. Line 64 calls the start_auto_save function from the API and passes the timing and autoSaveFolder variables as parameters.
  5. Create a method to clean up the default filepath.
autosave.qml
/* 
Import Qt modules to design the UI 
https://doc.qt.io/qt-5/qtqml-syntax-basics.html 
*/ 
import QtQuick 2.15 
import QtQuick.Controls 2.15 
import Qt.labs.platform 1.1 
import AutoSave 1.0 // Import API defined in the Python file 
 
Rectangle { 
  id: root 
  anchors.fill: parent 
  color: "#333333" 
 
  property var autoSaveFolder: removeQmlFilePathPrefix(StandardPaths.writableLocation(StandardPaths.DocumentsLocation)) 
  property var timing: 300 
  property var textColor: "#b3b3b3" 
 
  AutoSave { 
      id: api // Instantiate the Python API 
  } 
 
  Column { 
    id: controls 
    anchors.top: parent.top + 10 
    anchors.left: parent.left + 10 
    anchors.right: parent.right 
    width: parent.width 
    spacing: 20 
    leftPadding: 10 
    topPadding: 10 
 
    Column { 
        spacing: 5 
        Text { 
            id: timingTitle 
            text: "Autosave every (min): " 
            color: root.textColor 
        } 
        SpinBox { 
            id: timingControl 
            from: 1 
            to: 10 
            stepSize: 1 
            value: 5 
 
            onValueModified: ()=>{ 
                root.timing = timingControl.value * 60 
            } 
        } 
    } 
    Row { 
        Text { 
            text: "Off" 
            color: root.textColor 
            anchors.verticalCenter: toggle.verticalCenter 
        } 
        Switch { 
            id: toggle 
            checked: false 
 
            onClicked: ()=>{ 
                if (checked === true) { 
                    api.start_auto_save(root.autoSaveFolder, root.timing) // Call a function of the API with 2 arguments 
                } 
                else if (checked === false) { 
                    api.stop_auto_save() // Call a function of the API 
                } 
            } 
        } 
        Text { 
            text: "On" 
            color: root.textColor 
            anchors.verticalCenter: toggle.verticalCenter 
        } 
 
    } 
    Column { 
        spacing: 5 
        Text { 
            text: "Default Autosave Path" 
            color: root.textColor 
            } 
        Row { 
            id: folderInput 
            TextField { 
                id: folderText 
                text: root.autoSaveFolder 
                readOnly: true 
            } 
            Button { 
                id: folderSelection 
                text: qsTr("...") 
                width: 40 
                onClicked: ()=>{ 
                    folderDialog.open() 
                    } 
            } 
        } 
    } 
 
    FolderDialog { 
        id: folderDialog 
 
        onAccepted: ()=>{ 
            root.autoSaveFolder = removeQmlFilePathPrefix(folderDialog.currentFolder) 
        } 
    } 
 
  } 
      function qmlFilePathPrefix() { 
        if (Qt.platform.os === "windows") { 
            return "file:///" 
        } 
        return "file://" 
    } 
    function removeQmlFilePathPrefix(filePath) { 
        var prefix = qmlFilePathPrefix() 
        return filePath.toString().replace(prefix, '') 
    } 
}

SVG

You may have noticed that autosave.svg is not explicitly called or mentioned in either autosave.py or autosave.qml. This is because Sampler looks for an SVG file with the same name as the PY file and automatically uses it as the plugin icon. 

Note:

If your plugin folder contains an SVG with a filename that doesn't match the plugin's PY file, your plugin will not include an icon. This can create the appearance that your plugin hasn't appeared in the Sampler UI. If this is the case, move your cursor over Sampler's right bar to highlight your plugin.

Your browser does not support the HTML5 video element

If your plugin folder doesn't contain an SVG file, a default plugin icon will be used instead.

Below is an example SVG you can use for the autosave plugin created above. 

autosave.svg

Limitations of the autosave plugin

The autosave plugin created above is functional, but it isn't perfect. For example, adjusting the autosave interval after autosave has been enabled will not actually change the time between autosaves - you would need to disable and reenable autosave for the value in the UI to be sent to the API.

If you are new to working with Python and QML together, fixing this bug is a useful way to build an understanding of how the different parts of the plugin communicate with each other.

 Adobe

Get help faster and easier

New user?

Adobe MAX 2024

Adobe MAX
The Creativity Conference

Oct 14–16 Miami Beach and online

Adobe MAX

The Creativity Conference

Oct 14–16 Miami Beach and online

Adobe MAX 2024

Adobe MAX
The Creativity Conference

Oct 14–16 Miami Beach and online

Adobe MAX

The Creativity Conference

Oct 14–16 Miami Beach and online