ignore object with leading dot
1.0.2 - added: GP object with name starting with `.` are ignored from "all objects" operation (renaming, numbering, sending to render scene) - temporarily, layer named `note` (case insensitive) are ignored as well. This should be removed in later version to keep only dot exculsion rule.main
parent
b6e695eef3
commit
8a5654e977
|
@ -14,6 +14,11 @@ Activate / deactivate layer opacity according to prefix
|
||||||
Activate / deactivate all masks using MA layers
|
Activate / deactivate all masks using MA layers
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
1.0.2
|
||||||
|
|
||||||
|
- added: GP object with name starting with `.` are ignored from "all objects" operation (renaming, numbering, sending to render scene)
|
||||||
|
- temporarily, layer named `note` (case insensitive) are ignored as well. This should be removed in later version to keep only dot exculsion rule.
|
||||||
|
|
||||||
1.0.1
|
1.0.1
|
||||||
|
|
||||||
- fix: `Export Camera 2D Position To AE` file format not working on windows when export from linux (add CRLF terminator to generated text file)
|
- fix: `Export Camera 2D Position To AE` file format not working on windows when export from linux (add CRLF terminator to generated text file)
|
||||||
|
|
|
@ -90,7 +90,7 @@ class GPEXP_OT_add_objects_to_render(bpy.types.Operator):
|
||||||
# if not scn:
|
# if not scn:
|
||||||
# self.report({'ERROR'}, 'Could not found default scene')
|
# self.report({'ERROR'}, 'Could not found default scene')
|
||||||
# return {"CANCELLED"}
|
# return {"CANCELLED"}
|
||||||
export_gp_objects([o for o in context.scene.objects if o.type == 'GPENCIL' and not o.hide_get()], exclude_list=excludes, scene=scn)
|
export_gp_objects([o for o in context.scene.objects if o.type == 'GPENCIL' and not o.hide_get() and fn.is_valid_name(o.name)], exclude_list=excludes, scene=scn)
|
||||||
|
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
import bpy
|
||||||
|
from . import gen_vlayer, fn
|
||||||
|
|
||||||
|
class GPEXP_OT_render_auto_build(bpy.types.Operator):
|
||||||
|
bl_idname = "gp_export.render_auto_build"
|
||||||
|
bl_label = "Auto-Build"
|
||||||
|
bl_description = "Trigger all operation to make build render scene with default settings"
|
||||||
|
bl_options = {"REGISTER"}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
return context.object and context.object.type == 'GPENCIL'
|
||||||
|
|
||||||
|
# mode : bpy.props.StringProperty(options={'SKIP_SAVE'})
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
'''
|
||||||
|
ob = context.object
|
||||||
|
layer = ob.data.layers.active
|
||||||
|
if not layer:
|
||||||
|
self.report({'ERROR'}, 'No active layer')
|
||||||
|
return {"CANCELLED"}
|
||||||
|
|
||||||
|
ct = 0
|
||||||
|
# send scene ?
|
||||||
|
hided = 0
|
||||||
|
for l in ob.data.layers:
|
||||||
|
if not l.select:
|
||||||
|
if not l.viewlayer_render:
|
||||||
|
l.viewlayer_render = fn.get_view_layer('exclude').name
|
||||||
|
continue
|
||||||
|
gen_vlayer.get_set_viewlayer_from_gp(ob, l)
|
||||||
|
|
||||||
|
if l.hide:
|
||||||
|
hided += 1
|
||||||
|
ct += 1
|
||||||
|
|
||||||
|
if hided:
|
||||||
|
self.report({'WARNING'}, f'{hided}/{ct} layers are hided !')
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.report({'INFO'}, f'{ct} layer(s) added to scene "Render"')
|
||||||
|
'''
|
||||||
|
|
||||||
|
## TODO: add colors to layers (specified in ENV or hardcoded for now...)
|
||||||
|
## Option: Maybe find a way to create a color from prefix hash ? (wlways give unique color with same prefix on other project!)
|
||||||
|
|
||||||
|
|
||||||
|
## Trigger rename lowercase
|
||||||
|
bpy.ops.gp.lower_layers_name()
|
||||||
|
|
||||||
|
## Trigger renumber by distance
|
||||||
|
bpy.ops.gp.auto_number_object()
|
||||||
|
|
||||||
|
## Export layer infos ?
|
||||||
|
bpy.ops.gp.export_infos_for_compo()
|
||||||
|
|
||||||
|
## Send all GP to render scene
|
||||||
|
bpy.ops.gp.add_object_to_render(mode="ALL")
|
||||||
|
|
||||||
|
## Group all adjacent layer type
|
||||||
|
|
||||||
|
## Renumber File outputs
|
||||||
|
|
||||||
|
## Trigger check file before finishing ?
|
||||||
|
|
||||||
|
|
||||||
|
## note: After all these operation, a ctrl+Z might crash
|
||||||
|
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
classes=(
|
||||||
|
GPEXP_OT_render_auto_build,
|
||||||
|
)
|
||||||
|
|
||||||
|
def register():
|
||||||
|
for cls in classes:
|
||||||
|
bpy.utils.register_class(cls)
|
||||||
|
|
||||||
|
def unregister():
|
||||||
|
for cls in reversed(classes):
|
||||||
|
bpy.utils.unregister_class(cls)
|
|
@ -76,7 +76,7 @@ class GPEXP_OT_export_infos_for_compo(bpy.types.Operator):
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
dic = {}
|
dic = {}
|
||||||
pool = [o for o in context.scene.objects if o.type == 'GPENCIL']
|
pool = [o for o in context.scene.objects if o.type == 'GPENCIL' and fn.is_valid_name(o.name)]
|
||||||
for o in pool:
|
for o in pool:
|
||||||
# if not o.visible_get():
|
# if not o.visible_get():
|
||||||
# continue
|
# continue
|
||||||
|
@ -186,8 +186,6 @@ class GPEXP_OT_layers_state(bpy.types.Operator):
|
||||||
return context.object and context.object.type == 'GPENCIL'
|
return context.object and context.object.type == 'GPENCIL'
|
||||||
|
|
||||||
def invoke(self, context, event):
|
def invoke(self, context, event):
|
||||||
if event.alt:
|
|
||||||
self.all_objects=True
|
|
||||||
|
|
||||||
## if no existing infos.json generated, call ops
|
## if no existing infos.json generated, call ops
|
||||||
l_infos = Path(bpy.data.filepath).parent / 'render' / 'infos.json'
|
l_infos = Path(bpy.data.filepath).parent / 'render' / 'infos.json'
|
||||||
|
@ -219,7 +217,7 @@ class GPEXP_OT_layers_state(bpy.types.Operator):
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
if self.all_objects:
|
if self.all_objects:
|
||||||
pool = [o for o in context.scene.objects if o.type == 'GPENCIL']
|
pool = [o for o in context.scene.objects if o.type == 'GPENCIL' and fn.is_valid_name(o.name)]
|
||||||
else:
|
else:
|
||||||
pool = [o for o in context.selected_objects if o.type == 'GPENCIL']
|
pool = [o for o in context.selected_objects if o.type == 'GPENCIL']
|
||||||
# pool = [context.object]
|
# pool = [context.object]
|
||||||
|
@ -326,7 +324,7 @@ class GPEXP_OT_lower_layers_name(bpy.types.Operator):
|
||||||
return context.object and context.object.type == 'GPENCIL'
|
return context.object and context.object.type == 'GPENCIL'
|
||||||
|
|
||||||
all_objects : BoolProperty(name='On All Object',
|
all_objects : BoolProperty(name='On All Object',
|
||||||
default=False, description='On All object, else use selected objects') # , options={'SKIP_SAVE'}
|
default=True, description='On All object, else use selected objects') # , options={'SKIP_SAVE'}
|
||||||
|
|
||||||
object_name : BoolProperty(name='Normalize Object Name',
|
object_name : BoolProperty(name='Normalize Object Name',
|
||||||
default=True, description='Make the object name lowercase') # , options={'SKIP_SAVE'}
|
default=True, description='Make the object name lowercase') # , options={'SKIP_SAVE'}
|
||||||
|
@ -349,7 +347,7 @@ class GPEXP_OT_lower_layers_name(bpy.types.Operator):
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
layout.prop(self, 'all_objects')
|
layout.prop(self, 'all_objects')
|
||||||
if self.all_objects:
|
if self.all_objects:
|
||||||
gp_ct = len([o for o in context.scene.objects if o.type == 'GPENCIL'])
|
gp_ct = len([o for o in context.scene.objects if o.type == 'GPENCIL' and fn.is_valid_name(o.name)])
|
||||||
else:
|
else:
|
||||||
gp_ct = len([o for o in context.selected_objects if o.type == 'GPENCIL'])
|
gp_ct = len([o for o in context.selected_objects if o.type == 'GPENCIL'])
|
||||||
|
|
||||||
|
@ -367,7 +365,7 @@ class GPEXP_OT_lower_layers_name(bpy.types.Operator):
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
if self.all_objects:
|
if self.all_objects:
|
||||||
pool = [o for o in context.scene.objects if o.type == 'GPENCIL']
|
pool = [o for o in context.scene.objects if o.type == 'GPENCIL' and fn.is_valid_name(o.name)]
|
||||||
else:
|
else:
|
||||||
pool = [o for o in context.selected_objects if o.type == 'GPENCIL']
|
pool = [o for o in context.selected_objects if o.type == 'GPENCIL']
|
||||||
|
|
||||||
|
@ -387,6 +385,7 @@ class GPEXP_OT_lower_layers_name(bpy.types.Operator):
|
||||||
|
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
class GPEXP_OT_auto_number_object(bpy.types.Operator):
|
class GPEXP_OT_auto_number_object(bpy.types.Operator):
|
||||||
bl_idname = "gp.auto_number_object"
|
bl_idname = "gp.auto_number_object"
|
||||||
bl_label = "Auto Number Object"
|
bl_label = "Auto Number Object"
|
||||||
|
@ -398,7 +397,7 @@ class GPEXP_OT_auto_number_object(bpy.types.Operator):
|
||||||
return context.object and context.object.type == 'GPENCIL'
|
return context.object and context.object.type == 'GPENCIL'
|
||||||
|
|
||||||
all_objects : BoolProperty(name='On All GP Object',
|
all_objects : BoolProperty(name='On All GP Object',
|
||||||
default=False, description='On All object, else use selected Grease Pencil objects') # , options={'SKIP_SAVE'}
|
default=True, description='On All object, else use selected Grease Pencil objects') # , options={'SKIP_SAVE'}
|
||||||
|
|
||||||
rename_data : BoolProperty(name='Rename Gpencil Data',
|
rename_data : BoolProperty(name='Rename Gpencil Data',
|
||||||
default=True, description='Rename Also the Grease Pencil data using same name as object') # , options={'SKIP_SAVE'}
|
default=True, description='Rename Also the Grease Pencil data using same name as object') # , options={'SKIP_SAVE'}
|
||||||
|
@ -411,7 +410,7 @@ class GPEXP_OT_auto_number_object(bpy.types.Operator):
|
||||||
if event.ctrl or self.delete:
|
if event.ctrl or self.delete:
|
||||||
regex_num = re.compile(r'^(\d{3})_')
|
regex_num = re.compile(r'^(\d{3})_')
|
||||||
ct = 0
|
ct = 0
|
||||||
gps = [o for o in context.selected_objects if o.type == 'GPENCIL']
|
gps = [o for o in context.selected_objects if o.type == 'GPENCIL' and fn.is_valid_name(o.name)]
|
||||||
for o in gps:
|
for o in gps:
|
||||||
if regex_num.match(o.name):
|
if regex_num.match(o.name):
|
||||||
o.name = o.name[4:]
|
o.name = o.name[4:]
|
||||||
|
@ -426,7 +425,7 @@ class GPEXP_OT_auto_number_object(bpy.types.Operator):
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
layout.prop(self, 'all_objects')
|
layout.prop(self, 'all_objects')
|
||||||
if self.all_objects:
|
if self.all_objects:
|
||||||
gp_ct = len([o for o in context.scene.objects if o.type == 'GPENCIL'])
|
gp_ct = len([o for o in context.scene.objects if o.type == 'GPENCIL' and fn.is_valid_name(o.name)])
|
||||||
else:
|
else:
|
||||||
gp_ct = len([o for o in context.selected_objects if o.type == 'GPENCIL'])
|
gp_ct = len([o for o in context.selected_objects if o.type == 'GPENCIL'])
|
||||||
|
|
||||||
|
@ -437,7 +436,7 @@ class GPEXP_OT_auto_number_object(bpy.types.Operator):
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
if self.all_objects:
|
if self.all_objects:
|
||||||
pool = [o for o in context.scene.objects if o.type == 'GPENCIL']
|
pool = [o for o in context.scene.objects if o.type == 'GPENCIL' and fn.is_valid_name(o.name)]
|
||||||
else:
|
else:
|
||||||
pool = [o for o in context.selected_objects if o.type == 'GPENCIL']
|
pool = [o for o in context.selected_objects if o.type == 'GPENCIL']
|
||||||
|
|
||||||
|
@ -497,7 +496,7 @@ class GPEXP_OT_check_masks(bpy.types.Operator):
|
||||||
# else:
|
# else:
|
||||||
# pool = [o for o in context.selected_objects if o.type == 'GPENCIL']
|
# pool = [o for o in context.selected_objects if o.type == 'GPENCIL']
|
||||||
changes = []
|
changes = []
|
||||||
pool = [o for o in context.scene.objects if o.type == 'GPENCIL']
|
pool = [o for o in context.scene.objects if o.type == 'GPENCIL' and fn.is_valid_name(o.name)]
|
||||||
for o in pool:
|
for o in pool:
|
||||||
for l in o.data.layers:
|
for l in o.data.layers:
|
||||||
if l.use_mask_layer:
|
if l.use_mask_layer:
|
||||||
|
|
60
__init__.py
60
__init__.py
|
@ -2,7 +2,7 @@ bl_info = {
|
||||||
"name": "GP Render",
|
"name": "GP Render",
|
||||||
"description": "Organise export of gp layers through compositor output",
|
"description": "Organise export of gp layers through compositor output",
|
||||||
"author": "Samuel Bernou",
|
"author": "Samuel Bernou",
|
||||||
"version": (1, 0, 1),
|
"version": (1, 0, 2),
|
||||||
"blender": (2, 93, 0),
|
"blender": (2, 93, 0),
|
||||||
"location": "View3D",
|
"location": "View3D",
|
||||||
"warning": "",
|
"warning": "",
|
||||||
|
@ -26,10 +26,31 @@ from . import OP_render_pdf
|
||||||
from . import OP_export_to_ae
|
from . import OP_export_to_ae
|
||||||
from . import prefs
|
from . import prefs
|
||||||
from . import OP_setup_layers
|
from . import OP_setup_layers
|
||||||
|
from . import OP_auto_build
|
||||||
from . import ui
|
from . import ui
|
||||||
|
|
||||||
from .fn import scene_aa
|
from .fn import scene_aa
|
||||||
|
|
||||||
|
bl_modules = (
|
||||||
|
prefs,
|
||||||
|
OP_add_layer,
|
||||||
|
OP_clear,
|
||||||
|
OP_clean,
|
||||||
|
OP_connect_toggle,
|
||||||
|
OP_merge_layers,
|
||||||
|
OP_manage_outputs,
|
||||||
|
OP_scene_switch,
|
||||||
|
OP_crop_to_object,
|
||||||
|
OP_render_scenes,
|
||||||
|
OP_check_scene,
|
||||||
|
OP_post_render,
|
||||||
|
OP_render_pdf,
|
||||||
|
OP_export_to_ae,
|
||||||
|
OP_setup_layers,
|
||||||
|
OP_auto_build,
|
||||||
|
ui,
|
||||||
|
)
|
||||||
|
|
||||||
def update_scene_aa(context, scene):
|
def update_scene_aa(context, scene):
|
||||||
scene_aa(toggle=bpy.context.scene.use_aa)
|
scene_aa(toggle=bpy.context.scene.use_aa)
|
||||||
|
|
||||||
|
@ -39,22 +60,9 @@ def register():
|
||||||
if bpy.app.background:
|
if bpy.app.background:
|
||||||
return
|
return
|
||||||
|
|
||||||
prefs.register()
|
for mod in bl_modules:
|
||||||
OP_add_layer.register()
|
mod.register()
|
||||||
OP_clear.register()
|
|
||||||
OP_clean.register()
|
|
||||||
OP_connect_toggle.register()
|
|
||||||
OP_merge_layers.register()
|
|
||||||
OP_manage_outputs.register()
|
|
||||||
OP_scene_switch.register()
|
|
||||||
OP_crop_to_object.register()
|
|
||||||
OP_render_scenes.register()
|
|
||||||
OP_check_scene.register()
|
|
||||||
OP_post_render.register()
|
|
||||||
OP_render_pdf.register()
|
|
||||||
OP_export_to_ae.register()
|
|
||||||
OP_setup_layers.register()
|
|
||||||
ui.register()
|
|
||||||
# bpy.types.Scene.pgroup_name = bpy.props.PointerProperty(type = PROJ_PGT_settings)
|
# bpy.types.Scene.pgroup_name = bpy.props.PointerProperty(type = PROJ_PGT_settings)
|
||||||
bpy.types.Scene.use_aa = bpy.props.BoolProperty(
|
bpy.types.Scene.use_aa = bpy.props.BoolProperty(
|
||||||
name='Use Native Anti Aliasing',
|
name='Use Native Anti Aliasing',
|
||||||
|
@ -69,22 +77,8 @@ def unregister():
|
||||||
if bpy.app.background:
|
if bpy.app.background:
|
||||||
return
|
return
|
||||||
|
|
||||||
ui.unregister()
|
for mod in reversed(bl_modules):
|
||||||
OP_setup_layers.unregister()
|
mod.unregister()
|
||||||
OP_check_scene.unregister()
|
|
||||||
OP_post_render.unregister()
|
|
||||||
OP_export_to_ae.unregister()
|
|
||||||
OP_render_pdf.unregister()
|
|
||||||
OP_render_scenes.unregister()
|
|
||||||
OP_crop_to_object.unregister()
|
|
||||||
OP_scene_switch.unregister()
|
|
||||||
OP_manage_outputs.unregister()
|
|
||||||
OP_merge_layers.unregister()
|
|
||||||
OP_connect_toggle.unregister()
|
|
||||||
OP_clean.unregister()
|
|
||||||
OP_clear.unregister()
|
|
||||||
OP_add_layer.unregister()
|
|
||||||
prefs.unregister()
|
|
||||||
|
|
||||||
del bpy.types.Scene.use_aa
|
del bpy.types.Scene.use_aa
|
||||||
|
|
||||||
|
|
17
fn.py
17
fn.py
|
@ -10,6 +10,23 @@ from time import time
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
|
||||||
|
### -- rules
|
||||||
|
|
||||||
|
def is_valid_name(name):
|
||||||
|
'''return True if name correspond to a valid object
|
||||||
|
Don't start with a dot '.'
|
||||||
|
Is not "note"
|
||||||
|
'''
|
||||||
|
|
||||||
|
if name.startswith('.'):
|
||||||
|
return False
|
||||||
|
|
||||||
|
## FIXME: /!\ "note" as an exclude word is not good practice, temporary fix
|
||||||
|
if name.lower() == 'note':
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
### -- node basic
|
### -- node basic
|
||||||
|
|
||||||
def create_node(type, tree=None, **kargs):
|
def create_node(type, tree=None, **kargs):
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
info = {
|
||||||
|
'icon': 'AUTO',
|
||||||
|
'description': 'Setup things to make the precomp roll and rock'
|
||||||
|
}
|
||||||
|
|
||||||
|
import bpy
|
||||||
|
import os, subprocess
|
||||||
|
import re, fnmatch, glob
|
||||||
|
from pathlib import Path
|
||||||
|
from os.path import join, dirname, basename, exists, isfile, isdir, splitext
|
||||||
|
from mathutils import Vector, Matrix
|
||||||
|
from math import radians, degrees
|
||||||
|
|
||||||
|
C = bpy.context
|
||||||
|
D = bpy.data
|
||||||
|
scn = bpy.context.scene
|
||||||
|
|
||||||
|
## v0.1
|
||||||
|
## - Setup layer colors
|
||||||
|
|
||||||
|
## TODO
|
||||||
|
# - Update libraries
|
||||||
|
# - Import Fx3D (or render from Fx3D file... maybe easier consifering the number)
|
||||||
|
|
||||||
|
|
||||||
|
## tried to make color that fit in White theme
|
||||||
|
## (difficult for readability since this text color is not the same)
|
||||||
|
|
||||||
|
|
||||||
|
prefix_color = {
|
||||||
|
# 'MA_': (0.09, 0.08, 0.46), # Vivid blue
|
||||||
|
'MA_': (0.65, 0.4, 0.6), # Pink Light
|
||||||
|
|
||||||
|
'FX_': (0.12, 0.33, 0.58), # (0.3, 0.49, 0.63) # Blue Light
|
||||||
|
# 'CO_': (0.35, 0.0085, 0.25),
|
||||||
|
'CO_': (0.5,0.1,0.5), # Clear Pink
|
||||||
|
# 'CU': (0.092070, 0.177356, 0.447959), # Blue clear
|
||||||
|
'CU_': (0.02, 0.27, 0.27), # Indigo
|
||||||
|
}
|
||||||
|
|
||||||
|
## UW -> TO (here used fo CU): (0.015996, 0.246201, 0.246201) # Indigo
|
||||||
|
|
||||||
|
## invisible (close to violet light) in UW: (0.246201, 0.132868, 0.496933)
|
||||||
|
|
||||||
|
|
||||||
|
def set_layer_colors():
|
||||||
|
for ob in scn.objects:
|
||||||
|
if ob.type != 'GPENCIL':
|
||||||
|
continue
|
||||||
|
for l in ob.data.layers:
|
||||||
|
# if l.info.startswith(prefix_color.keys()):
|
||||||
|
color = prefix_color.get(l.info[:3])
|
||||||
|
if not color:
|
||||||
|
continue
|
||||||
|
print(l.info, '->', color)
|
||||||
|
l.channel_color = color
|
||||||
|
|
||||||
|
C.preferences.edit.use_anim_channel_group_colors = True
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
set_layer_colors()
|
||||||
|
|
||||||
|
## update libraries
|
||||||
|
bpy.ops.gadget.update_libraries()
|
4
ui.py
4
ui.py
|
@ -173,6 +173,9 @@ class GPEXP_PT_gp_dopesheet_ui(Panel):
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
|
|
||||||
|
## TODO: add auto-build
|
||||||
|
# layout.operator('gp_export.render_auto_build')
|
||||||
if context.object:
|
if context.object:
|
||||||
layout.label(text=f'Object: {context.object.name}')
|
layout.label(text=f'Object: {context.object.name}')
|
||||||
if context.object.data.users > 1:
|
if context.object.data.users > 1:
|
||||||
|
@ -203,7 +206,6 @@ class GPEXP_PT_gp_dopesheet_ui(Panel):
|
||||||
row.operator('gp.merge_viewlayers_to_active', text=txt, icon='SELECT_EXTEND')
|
row.operator('gp.merge_viewlayers_to_active', text=txt, icon='SELECT_EXTEND')
|
||||||
row.enabled= ct > 1
|
row.enabled= ct > 1
|
||||||
|
|
||||||
|
|
||||||
## all and objects
|
## all and objects
|
||||||
layout.separator()
|
layout.separator()
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue