Files
test2/scripts/startup/bl_ui/properties_data_grease_pencil.py
Casey Bianco-Davis f8d2a3af38 Python: Rename bpy.types.GreasePencilv3 to bpy.types.GreasePencil
This renames python Grease Pencil type
from `bpy.types.GreasePencilv3` to `bpy.types.GreasePencil`.

Part of #125058.

Pull Request: https://projects.blender.org/blender/blender/pulls/146902
2025-09-29 12:25:23 +02:00

537 lines
18 KiB
Python

# SPDX-FileCopyrightText: 2023 Blender Authors
#
# SPDX-License-Identifier: GPL-2.0-or-later
import bpy
from bpy.types import Panel, Menu, UIList
from rna_prop_ui import PropertyPanel
from bl_ui.space_properties import PropertiesAnimationMixin
class DataButtonsPanel:
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "data"
@classmethod
def poll(cls, context):
return hasattr(context, "grease_pencil") and context.grease_pencil
class LayerDataButtonsPanel:
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "data"
@classmethod
def poll(cls, context):
grease_pencil = context.grease_pencil
return grease_pencil and grease_pencil.layers.active
class GREASE_PENCIL_UL_masks(UIList):
def draw_item(self, _context, layout, _data, item, icon, _active_data, _active_propname, _index):
mask = item
row = layout.row(align=True)
row.prop(mask, "name", text="", emboss=False, icon_value=icon)
row.prop(mask, "invert", text="", emboss=False)
row.prop(mask, "hide", text="", emboss=False)
class GreasePencil_LayerMaskPanel:
def draw_header(self, context):
grease_pencil = context.grease_pencil
layer = grease_pencil.layers.active
self.layout.prop(layer, "use_masks", text="", toggle=0)
def draw(self, context):
layout = self.layout
grease_pencil = context.grease_pencil
layer = grease_pencil.layers.active
layout = self.layout
layout.enabled = layer.use_masks
if not layer:
return
rows = 4
row = layout.row()
col = row.column()
col.template_list(
"GREASE_PENCIL_UL_masks", "", layer, "mask_layers", layer.mask_layers,
"active_mask_index", rows=rows, sort_lock=True,
)
col = row.column(align=True)
col.menu("GREASE_PENCIL_MT_layer_mask_add", icon='ADD', text="")
col.operator("grease_pencil.layer_mask_remove", icon='REMOVE', text="")
col.separator()
sub = col.column(align=True)
sub.operator("grease_pencil.layer_mask_reorder", icon='TRIA_UP', text="").direction = 'UP'
sub.operator("grease_pencil.layer_mask_reorder", icon='TRIA_DOWN', text="").direction = 'DOWN'
class GreasePencil_LayerTransformPanel:
def draw(self, context):
layout = self.layout
layout.use_property_split = True
grease_pencil = context.grease_pencil
layer = grease_pencil.layers.active
layout.active = not layer.lock
row = layout.row(align=True)
row.prop(layer, "translation")
row = layout.row(align=True)
row.prop(layer, "rotation")
row = layout.row(align=True)
row.prop(layer, "scale")
class GreasePencil_LayerAdjustmentsPanel:
def draw(self, context):
layout = self.layout
layout.use_property_split = True
grease_pencil = context.grease_pencil
layer = grease_pencil.layers.active
layout.active = not layer.lock
# Layer options
col = layout.column(align=True)
col.prop(layer, "tint_color")
col.prop(layer, "tint_factor", text="Factor", slider=True)
col = layout.row(align=True)
col.prop(layer, "radius_offset", text="Stroke Thickness")
class GreasePencil_LayerRelationsPanel:
def draw(self, context):
layout = self.layout
layout.use_property_split = True
grease_pencil = context.grease_pencil
layer = grease_pencil.layers.active
layout.active = not layer.lock
row = layout.row(align=True)
row.prop(layer, "parent", text="Parent")
if layer.parent and layer.parent.type == 'ARMATURE':
row = layout.row(align=True)
row.prop_search(layer, "parent_bone", layer.parent.data, "bones", text="Bone")
layout.separator()
col = layout.row(align=True)
col.prop(layer, "pass_index")
col = layout.row(align=True)
col.prop_search(layer, "viewlayer_render", context.scene, "view_layers", text="View Layer")
col = layout.row(align=True)
# Only enable this property when a view layer is selected.
col.enabled = bool(layer.viewlayer_render)
col.prop(layer, "use_viewlayer_masks")
class GreasePencil_LayerDisplayPanel:
bl_label = "Display"
def draw(self, context):
layout = self.layout
layout.use_property_split = True
grease_pencil = context.grease_pencil
layer = grease_pencil.layers.active
layout.prop(layer, "channel_color", text="Channel Color")
class GREASE_PENCIL_MT_layer_mask_add(Menu):
bl_label = "Add Mask"
def draw(self, context):
layout = self.layout
grease_pencil = context.grease_pencil
active_layer = grease_pencil.layers.active
found = False
for layer in grease_pencil.layers:
if layer == active_layer or layer.name in active_layer.mask_layers:
continue
found = True
layout.operator("grease_pencil.layer_mask_add", text=layer.name).name = layer.name
if not found:
layout.label(text="No layers to add")
class DATA_PT_context_grease_pencil(DataButtonsPanel, Panel):
bl_label = ""
bl_options = {'HIDE_HEADER'}
def draw(self, context):
layout = self.layout
ob = context.object
grease_pencil = context.grease_pencil
space = context.space_data
if ob:
layout.template_ID(ob, "data")
elif grease_pencil:
layout.template_ID(space, "pin_id")
class GREASE_PENCIL_MT_grease_pencil_add_layer_extra(Menu):
bl_label = "Add Extra"
def draw(self, context):
layout = self.layout
grease_pencil = context.grease_pencil
layer = grease_pencil.layers.active
layout.separator()
layout.operator("grease_pencil.layer_duplicate", text="Duplicate", icon='DUPLICATE').empty_keyframes = False
layout.operator("grease_pencil.layer_duplicate", text="Duplicate Empty Keyframes").empty_keyframes = True
layout.separator()
layout.operator("grease_pencil.layer_reveal", icon='RESTRICT_VIEW_OFF', text="Show All")
layout.operator("grease_pencil.layer_hide", icon='RESTRICT_VIEW_ON', text="Hide Others").unselected = True
layout.separator()
layout.operator("grease_pencil.layer_lock_all", icon='LOCKED', text="Lock All").lock = True
layout.operator("grease_pencil.layer_lock_all", icon='UNLOCKED', text="Unlock All").lock = False
layout.separator()
layout.prop(grease_pencil, "use_autolock_layers", text="Autolock Inactive Layers")
if layer:
layout.prop(layer, "ignore_locked_materials")
layout.separator()
layout.operator("grease_pencil.layer_merge", text="Merge Down").mode = 'ACTIVE'
layout.operator("grease_pencil.layer_merge", text="Merge Group").mode = 'GROUP'
layout.operator("grease_pencil.layer_merge", text="Merge All").mode = 'ALL'
layout.separator()
layout.operator("grease_pencil.relative_layer_mask_add", text="Mask with Layer Above").mode = 'ABOVE'
layout.operator("grease_pencil.relative_layer_mask_add", text="Mask with Layer Below").mode = 'BELOW'
layout.separator()
layout.operator("grease_pencil.layer_duplicate_object", text="Copy Layer to Selected").only_active = True
layout.operator("grease_pencil.layer_duplicate_object", text="Copy All Layers to Selected").only_active = False
class GREASE_PENCIL_MT_group_context_menu(Menu):
bl_label = "Layer Group"
def draw(self, context):
layout = self.layout
layout.operator("grease_pencil.layer_group_remove", text="Delete Group").keep_children = False
layout.operator("grease_pencil.layer_group_remove", text="Ungroup").keep_children = True
layout.operator("grease_pencil.layer_merge", text="Merge Group").mode = 'GROUP'
layout.separator()
row = layout.row(align=True)
row.operator_enum("grease_pencil.layer_group_color_tag", "color_tag", icon_only=True)
class DATA_PT_grease_pencil_layers(DataButtonsPanel, Panel):
bl_label = "Layers"
@classmethod
def draw_settings(cls, layout, grease_pencil):
layer = grease_pencil.layers.active
is_layer_active = layer is not None
is_group_active = grease_pencil.layer_groups.active is not None
row = layout.row()
row.template_grease_pencil_layer_tree()
col = row.column()
sub = col.column(align=True)
sub.operator_context = 'EXEC_DEFAULT'
sub.operator("grease_pencil.layer_add", icon='ADD', text="")
sub.operator("grease_pencil.layer_group_add", icon='NEWFOLDER', text="")
sub.separator()
if is_layer_active:
sub.operator("grease_pencil.layer_remove", icon='REMOVE', text="")
if is_group_active:
sub.operator("grease_pencil.layer_group_remove", icon='REMOVE', text="").keep_children = True
sub.separator()
sub.menu("GREASE_PENCIL_MT_grease_pencil_add_layer_extra", icon='DOWNARROW_HLT', text="")
col.separator()
sub = col.column(align=True)
sub.operator("grease_pencil.layer_move", icon='TRIA_UP', text="").direction = 'UP'
sub.operator("grease_pencil.layer_move", icon='TRIA_DOWN', text="").direction = 'DOWN'
if not is_layer_active:
return
layout.use_property_split = True
layout.use_property_decorate = True
col = layout.column(align=True)
# Layer main properties
row = layout.row(align=True)
row.prop(layer, "blend_mode", text="Blend Mode")
row = layout.row(align=True)
row.prop(layer, "opacity", text="Opacity", slider=True)
row = layout.row(align=True)
row.prop(layer, "use_lights", text="Lights")
def draw(self, context):
layout = self.layout
grease_pencil = context.grease_pencil
self.draw_settings(layout, grease_pencil)
class DATA_PT_grease_pencil_layer_masks(LayerDataButtonsPanel, GreasePencil_LayerMaskPanel, Panel):
bl_label = "Masks"
bl_parent_id = "DATA_PT_grease_pencil_layers"
bl_options = {'DEFAULT_CLOSED'}
class DATA_PT_grease_pencil_layer_transform(LayerDataButtonsPanel, GreasePencil_LayerTransformPanel, Panel):
bl_label = "Transform"
bl_parent_id = "DATA_PT_grease_pencil_layers"
bl_options = {'DEFAULT_CLOSED'}
class DATA_PT_grease_pencil_layer_adjustments(LayerDataButtonsPanel, GreasePencil_LayerAdjustmentsPanel, Panel):
bl_label = "Adjustments"
bl_parent_id = "DATA_PT_grease_pencil_layers"
bl_options = {'DEFAULT_CLOSED'}
class DATA_PT_grease_pencil_layer_relations(LayerDataButtonsPanel, GreasePencil_LayerRelationsPanel, Panel):
bl_label = "Relations"
bl_parent_id = "DATA_PT_grease_pencil_layers"
bl_options = {'DEFAULT_CLOSED'}
class DATA_PT_grease_pencil_layer_display(LayerDataButtonsPanel, GreasePencil_LayerDisplayPanel, Panel):
bl_label = "Display"
bl_parent_id = "DATA_PT_grease_pencil_layers"
bl_options = {'DEFAULT_CLOSED'}
class DATA_PT_grease_pencil_layer_group_display(Panel):
bl_label = "Display"
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "data"
bl_options = {'DEFAULT_CLOSED'}
@classmethod
def poll(cls, context):
grease_pencil = context.grease_pencil
return grease_pencil and grease_pencil.layer_groups.active
def draw(self, context):
layout = self.layout
layout.use_property_split = True
grease_pencil = context.grease_pencil
group = grease_pencil.layer_groups.active
layout.prop(group, "channel_color", text="Channel Color")
class DATA_PT_grease_pencil_onion_skinning(DataButtonsPanel, Panel):
bl_label = "Onion Skinning"
def draw(self, context):
grease_pencil = context.grease_pencil
layout = self.layout
layout.use_property_split = True
col = layout.column()
col.prop(grease_pencil, "onion_mode")
col.prop(grease_pencil, "onion_factor", text="Opacity", slider=True)
col.prop(grease_pencil, "onion_keyframe_type")
if grease_pencil.onion_mode == 'ABSOLUTE':
col = layout.column(align=True)
col.prop(grease_pencil, "ghost_before_range", text="Frames Before")
col.prop(grease_pencil, "ghost_after_range", text="Frames After")
elif grease_pencil.onion_mode == 'RELATIVE':
col = layout.column(align=True)
col.prop(grease_pencil, "ghost_before_range", text="Keyframes Before")
col.prop(grease_pencil, "ghost_after_range", text="Keyframes After")
class DATA_PT_grease_pencil_onion_skinning_custom_colors(DataButtonsPanel, Panel):
bl_parent_id = "DATA_PT_grease_pencil_onion_skinning"
bl_label = "Custom Colors"
bl_options = {'DEFAULT_CLOSED'}
def draw_header(self, context):
grease_pencil = context.grease_pencil
self.layout.prop(grease_pencil, "use_ghost_custom_colors", text="")
def draw(self, context):
grease_pencil = context.grease_pencil
layout = self.layout
layout.use_property_split = True
layout.enabled = grease_pencil.users <= 1 and grease_pencil.use_ghost_custom_colors
layout.prop(grease_pencil, "before_color", text="Before")
layout.prop(grease_pencil, "after_color", text="After")
class DATA_PT_grease_pencil_onion_skinning_display(DataButtonsPanel, Panel):
bl_parent_id = "DATA_PT_grease_pencil_onion_skinning"
bl_label = "Display"
bl_options = {'DEFAULT_CLOSED'}
def draw(self, context):
grease_pencil = context.grease_pencil
layout = self.layout
layout.use_property_split = True
# This was done in GPv2 but it's not entirely clear why. Presumably it was
# to indicate that the settings will affect the onion skinning of the
# other users.
layout.enabled = grease_pencil.users <= 1
col = layout.column(align=True)
col.prop(grease_pencil, "use_onion_fade", text="Fade")
sub = layout.column()
sub.active = grease_pencil.onion_mode in {'RELATIVE', 'SELECTED'}
sub.prop(grease_pencil, "use_onion_loop", text="Show Start Frame")
class DATA_PT_grease_pencil_settings(DataButtonsPanel, Panel):
bl_label = "Settings"
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
grease_pencil = context.grease_pencil
col = layout.column(align=True)
col.prop(grease_pencil, "stroke_depth_order", text="Stroke Depth Order")
class DATA_PT_grease_pencil_animation(DataButtonsPanel, PropertiesAnimationMixin, PropertyPanel, Panel):
_animated_id_context_property = "grease_pencil"
class DATA_PT_grease_pencil_custom_props(DataButtonsPanel, PropertyPanel, Panel):
_context_path = "object.data"
_property_type = bpy.types.GreasePencil
class GREASE_PENCIL_UL_attributes(UIList):
def filter_items(self, _context, data, property):
attributes = getattr(data, property)
flags = []
indices = [i for i in range(len(attributes))]
# Filtering by name
if self.filter_name:
flags = bpy.types.UI_UL_list.filter_items_by_name(
self.filter_name, self.bitflag_filter_item, attributes, "name", reverse=self.use_filter_invert,
)
if not flags:
flags = [self.bitflag_filter_item] * len(attributes)
# Filtering internal attributes
for idx, item in enumerate(attributes):
flags[idx] = 0 if item.is_internal else flags[idx]
# Reorder by name.
if self.use_filter_sort_alpha:
indices = bpy.types.UI_UL_list.sort_items_by_name(attributes, "name")
return flags, indices
def draw_item(self, _context, layout, _data, attribute, _icon, _active_data, _active_propname, _index):
data_type = attribute.bl_rna.properties["data_type"].enum_items[attribute.data_type]
split = layout.split(factor=0.50)
split.emboss = 'NONE'
split.prop(attribute, "name", text="")
sub = split.row()
sub.alignment = 'RIGHT'
sub.active = False
sub.label(text=data_type.name)
class DATA_PT_grease_pencil_attributes(DataButtonsPanel, Panel):
bl_label = "Attributes"
bl_options = {'DEFAULT_CLOSED'}
def draw(self, context):
grease_pencil = context.grease_pencil
layout = self.layout
row = layout.row()
col = row.column()
col.template_list(
"GREASE_PENCIL_UL_attributes",
"attributes",
grease_pencil,
"attributes",
grease_pencil.attributes,
"active_index",
rows=3,
)
col = row.column(align=True)
col.operator("geometry.attribute_add", icon='ADD', text="")
col.operator("geometry.attribute_remove", icon='REMOVE', text="")
classes = (
GREASE_PENCIL_UL_masks,
GREASE_PENCIL_MT_layer_mask_add,
DATA_PT_context_grease_pencil,
DATA_PT_grease_pencil_layers,
DATA_PT_grease_pencil_layer_masks,
DATA_PT_grease_pencil_layer_transform,
DATA_PT_grease_pencil_layer_adjustments,
DATA_PT_grease_pencil_layer_relations,
DATA_PT_grease_pencil_layer_display,
DATA_PT_grease_pencil_layer_group_display,
DATA_PT_grease_pencil_onion_skinning,
DATA_PT_grease_pencil_onion_skinning_custom_colors,
DATA_PT_grease_pencil_onion_skinning_display,
DATA_PT_grease_pencil_settings,
DATA_PT_grease_pencil_custom_props,
GREASE_PENCIL_MT_grease_pencil_add_layer_extra,
GREASE_PENCIL_MT_group_context_menu,
DATA_PT_grease_pencil_animation,
GREASE_PENCIL_UL_attributes,
DATA_PT_grease_pencil_attributes,
)
if __name__ == "__main__": # only for live edit.
from bpy.utils import register_class
for cls in classes:
register_class(cls)