API reference
These are the commands a plugin can use, and the ones we keep working from one update to the next. Anything not on this page is part of Pixel Painter’s insides and may change without warning.
Everything a plugin changes in the project goes on the undo list, the same as if you had clicked it yourself. Ctrl+Z takes it back.
from pixelpaint import uifrom pixelpaint.api import ProjectAPI, TextureSetAPIA quick example
Section titled “A quick example”This adds an orange “Rust tint” fill layer, sets it to Multiply at 50%, and
tags it orange. Wrapping the edits in gesture() makes them one undo step
instead of four.
from pixelpaint.api import ProjectAPI
project = ProjectAPI()texture_set = project.get_active_texture_set()
with project.gesture(): layer_id = texture_set.add_layer( "Rust tint", "fill", fills={"base_color": [150, 80, 40, 255]}) texture_set.set_layer_blend(layer_id, "multiply") texture_set.set_layer_opacity(layer_id, 0.5) texture_set.set_layer_color(layer_id, "orange")Values you will see a lot
Section titled “Values you will see a lot”Channel ids: "base_color", "metallic", "roughness", "normal",
"height", "emissive".
Colour and channel values are whole numbers from 0 to 255:
- Base Color is
[r, g, b, a] - Normal is
[r, g, b]. Flat is[128, 128, 255] - Metallic, Roughness and Height are a single number. For Height,
128is flat
Tiles are (u, v) pairs. (0, 0) is UDIM 1001, (1, 0) is 1002, and so on.
Blend modes: "normal", "multiply", "divide", "screen", "add",
"subtract", "lighten", "darken".
Colour tags: "red", "orange", "yellow", "green", "blue",
"purple", "gray", or None for no tag.
Window and menus
Section titled “Window and menus”from pixelpaint import ui
| Command | What it does |
|---|---|
ui.get_main_window() |
The main Pixel Painter window. Use it as the parent of your own windows so they follow the app’s theme and close with it. |
ui.add_menu(menu) |
Puts your QMenu on the menu bar, just before Help. Returns the menu. |
ui.delete_ui_element(widget) |
Removes a window or menu your plugin made. |
ui.add_close_guard(callback) |
Calls callback() when the artist quits. Return True to let the app close, False to stop it, for example to ask about unsaved work in your tool. |
ui.remove_close_guard(callback) |
Takes a close guard away again. |
ui.refresh_asset_libraries() |
Reloads the Asset Shelf. Call it after your plugin saves a material or brush into the asset folders. |
from PySide6.QtWidgets import QMenufrom pixelpaint import ui
menu = QMenu("Studio Tools", ui.get_main_window())menu.addAction("Say hello").triggered.connect(lambda: print("hello"))ui.add_menu(menu)The project
Section titled “The project”project = ProjectAPI(). There is only ever one project, so every
ProjectAPI() you make talks to the same one.
| Command | What it does |
|---|---|
project.is_open() |
True if a project is open. Check this first. |
project.resolution |
Texture size in pixels, for example 64. |
project.mesh_path |
Path of the model file. |
project.texture_sets |
Every texture set, by name. project.texture_sets["lambert1"] gives you that set, and .keys() lists the names. |
project.get_active_texture_set() |
The set the artist is working on, or None. |
project.set_active_texture_set(name) |
Switches to another set. |
project.set_brush_color([r, g, b]) |
Changes the brush colour. |
project.get_texture(channel, tile) |
The finished image of one channel, with every layer combined, as a NumPy array. Base Color is (size, size, 4). |
with project.gesture(): |
Everything inside the block becomes a single undo step. |
project = ProjectAPI()image = project.get_texture("base_color", (0, 0))print(image.shape) # (64, 64, 4)Layers
Section titled “Layers”A texture set holds a stack of layers. Get one with
project.get_active_texture_set() or project.texture_sets["name"].
Most commands take a layer_id. You get one back when you create a layer, or
you can read it from texture_set.layers.
Reading
Section titled “Reading”| Command | What it gives you |
|---|---|
texture_set.name |
The set’s name. |
texture_set.layers |
Every layer, bottom first. Each one is a dictionary with id, name, type ("paint", "fill", "group"…), visible, opacity, blend, color and parent_id. Read these; change them with the commands below. |
texture_set.active_layer_id |
The id of the selected layer. |
texture_set.get_active_layer() |
The selected layer’s dictionary, or None. |
texture_set.set_active_layer(layer_id) |
Selects a layer. |
Creating and deleting
Section titled “Creating and deleting”| Command | What it does |
|---|---|
add_layer(name, "paint") |
A new empty paint layer. Returns its id. |
add_layer(name, "fill", fills={...}) |
A new fill layer. fills sets a value per channel, for example {"base_color": [255, 0, 0, 255], "roughness": 200}. Channels you leave out get the defaults. |
add_group(name) |
A new empty group. Returns its id. |
wrap_layers([id, id, ...], name) |
Puts those layers into a new group. They must all be in the same group already, or all at the top level. Returns the group’s id. |
delete_layer(layer_id) |
Deletes a layer. |
New layers go on top of the stack.
Changing a layer
Section titled “Changing a layer”| Command | What it does |
|---|---|
rename_layer(layer_id, new_name) |
Renames it. |
set_layer_visibility(layer_id, True) |
Shows or hides it. |
set_layer_opacity(layer_id, 0.5) |
Opacity from 0.0 to 1.0. |
set_layer_blend(layer_id, "multiply") |
Blend mode. |
set_layer_color(layer_id, "green") |
Colour tag, or None to clear it. |
set_layer_parent(layer_id, group_id) |
Moves a layer into a group, or back to the top level with None. |
Fill layers
Section titled “Fill layers”| Command | What it does |
|---|---|
set_layer_fill(layer_id, channel, value) |
Changes one channel’s value, for example set_layer_fill(layer_id, "roughness", 220). |
set_layer_texture(layer_id, channel, path) |
Loads an image file into one channel. If the file name has a UDIM number in it (rust.1001.png), the other tiles are loaded too. |
These only work on fill layers. Paint layers keep what was painted.
Copy, paste, mirror
Section titled “Copy, paste, mirror”| Command | What it does |
|---|---|
copy_layer(layer_id) |
Copies a layer (a group copies with everything in it). Returns True if it worked. |
can_paste_layer() |
True if this set can take what was copied. |
paste_layer() |
Pastes above the selected layer. Returns the new layer’s id. Works across texture sets. |
mirror_paint_layer(layer_id, "x", onto="-") |
Copies the +X half of a paint layer onto the -X half. Use "y" or "z" for the other axes, and onto="+" to go the other way. |
mirror_paint_layer(layer_id, "x", replace=True) |
Flips the whole paint layer across X. |
mirror_paint_effect(layer_id, effect_id, "x", onto="-") |
The same for a mask’s paint effect. Takes replace=True too. |
mirror_layer is the old name of mirror_paint_layer and still works, so
plugins written for it keep running. Both mirror commands return how many
texels they wrote, or None if they could not run (not a paint layer or
effect, a bad axis, or no model loaded).
# Copy the selected layer into every texture setproject = ProjectAPI()source = project.get_active_texture_set()source.copy_layer(source.active_layer_id)
with project.gesture(): for name in project.texture_sets.keys(): target = project.texture_sets[name] if target.can_paste_layer(): target.paste_layer()Materials and masks
Section titled “Materials and masks”| Command | What it does |
|---|---|
apply_material(path) |
Adds a .pmat material on top of the stack, like dropping it from the Asset Shelf. Returns the new layer ids. |
add_layer_mask(layer_id, white=True) |
Adds a mask. white=True starts with the whole layer showing. Without it the mask starts black and the layer is hidden until you paint the mask. |
remove_layer_mask(layer_id) |
Removes the mask. |
A 3D preview for your tool
Section titled “A 3D preview for your tool”from pixelpaint.viewport.preview import PreviewScene, PreviewViewport, box_mesh
A small 3D view you can put in your own window, lit like the main viewport.
It shows only what you put in its PreviewScene and never touches the open
project. See Writing plugins.
Not in the API yet
Section titled “Not in the API yet”These are not available to plugins yet. For now, do them in the app:
- Painting pixels directly from code. You can load an image into a fill layer
with
set_layer_textureinstead. - Exporting textures.
- Saving the project.
- Baking mesh maps.