Accessing “null” object properties -Blender 2.79b Python scripting -

animation

(Starzar) #1

So ,I was trying to create a 3dview custom menu to easily access Grease pencil data like colors ,layers etc .However these properties didn’t have any unique label but a common label called “null” . eg. bpy.data.grease_pencil[“GPencil”].(null) = 0,1,2 … (same label for both GPencil colors and GPencil layers ).

Is their any way to access and operate on such type of data?


(zaha) #2

Not an expert on the Blender Python API, but null in programming usually indicates the absence of an object/data (see null pointer). So to me it would seem that the GPencil object just is not available.

But I might be completely wrong, as I said I’m not that familiar with the Blender API yet.


#3

(null) in this case i believe is a limitation in the output from the info window, in that the prop does exist but it is deeply nested or being specially handled.

I think these are the props you are looking for;

bpy.data.grease_pencil['GPencil'].layers.active_index

bpy.data.grease_pencil['GPencil'].palettes['GP_Palette'].colors.active_index

to change the xray option for the active gp layer;

grease_pencil = bpy.data.grease_pencil['GPencil']
active_index = bpy.data.grease_pencil['GPencil'].layers.active_index

grease_pencil.layers[active_index].show_x_ray = True

or;

grease_pencil.layers.active.show_x_ray

to modify the color visibility;

grease_pencil.palettes.active.colors.active.hide = True

if you are opening the menu in the right context then you can use;

context.gpencil_data

so with this in mind lets hide the active color;

context.gpencil_data.palettes.active.colors.active.hide = True

(Starzar) #4

@proxe thanks for the reply.
I did dig into the gpencil null object prop issue and my best guess is that this exists because a gpencil color is not a float RGB values list but a collection of two set of props - strokecolor and fillcolor.
I am not sure how to access a collection of props like in case of gpencil colors.I did try using context_pointer_set function to point to the activepalette colors index.The code is given below but it gives an context attribute error.

import bpy
class GPcolorMenu(bpy.types.Menu):
bl_label = “GPcolors”
bl_idname = “gpencil.GPcolor_menu”

def draw(self, context):
    layout = self.layout

    for color in bpy.data.grease_pencil["GPencil"].palettes["GP_Palette"].colors:
        
        row = layout.row()
        
        row.context_pointer_set("active_palettecolors", color) 
        layout.menu(context.active_palettecolors, text=color.name)

def register():
bpy.utils.register_module(name)

def unregister():
bpy.utils.unregister_module(name)

if name == “main”:
register()

bpy.ops.wm.call_menu(name=GPcolorMenu.bl_idname)


(AFWS) #5

I’m still trying to figure out what you’re trying to do? If you’re trying to put properties that you can change in a menu, I don’t think that possible. Maybe a dialog operator?

import bpy


class DialogOperator(bpy.types.Operator):
	bl_idname = "object.dialog_operator"
	bl_label = "GP Colors"

	
	def draw(self, context):
		layout = self.layout

		scene = bpy.context.scene
		gp = scene.grease_pencil
		active_gp_layer = gp.layers.active
		active_gp_palette = gp.palettes.active  	  

		col = layout.column(align=True)

		col.separator()
		col.separator()

		row = col.row(align=True)
		row.label('Active Pencil:', icon_value=layout.icon(gp))
		row.label(gp.name)

		row = col.row(align=True)
		row.label('Active Layer:', icon_value=layout.icon(active_gp_palette.colors))
		row.label(active_gp_layer.info)

		row = col.row(align=True)
		row.label('Active Palette:', icon_value=layout.icon(active_gp_palette))
		row.label(active_gp_palette.name)

		col.separator()
		col.separator()
		
		row = layout.row(align=True)

		row.label('Color:')
		row.label('Stroke:')
		row.label('Fill:')

				
		for colors in active_gp_palette.colors:

			row = layout.row(align=True)
  
			row.prop(colors, "name", text='')  
			row.prop(colors, "color", text='')
			row.prop(colors, "fill_color", text='')



	def invoke(self, context, event):
		wm = context.window_manager
		return wm.invoke_props_dialog(self)


	def execute(self, context):
		return {'FINISHED'}

	
bpy.utils.register_class(DialogOperator)

# test call
bpy.ops.object.dialog_operator('INVOKE_DEFAULT')

(Starzar) #6

@AFWS great work on the script .Oh!there was some misunderstanding. I was doing 2d animation and was trying to “switch” GP colors via a custom menu in the 3dviewport ,this would accelerate coloring and not break the workflow by having to constantly move out of the drawing area to pick a new color. I was trying to do that by using the pointer function to invoke the active gp palettecolors so that they could be switched in the 3d viewport,
I was curious why you said- " If you’re trying to put properties that you can change in a menu, I don’t think that possible."

Also, I did try invoking the colors in the 3dview via the keys() assigned to them in the GPcolors datablock
“bpy.data.grease_pencil[‘GPencil’].palettes[‘GP_Palette’].colors.keys()”

However ,it gave an error - bpy_struct.keys() - this type does not support IDProperties in the Info window, eventhough the keys are clearly displayed in the Python console.
That’s confusing.
Does the Gpencil block support Python dictionary functions to be executed on them ?
Thanks.


(AFWS) #7

I saw your other thread. Still not sure why you are trying to make a menu. This is just a copy of the Grease Pencil Colors panel in a popup. You can do whatever you need related to the colors. The one thing that doesn’t seem to work is renaming colors, haven’t figured out why yet.

import bpy


class GPColorsPopup(bpy.types.Operator):
	bl_idname = "gpencil.colors_popup"
	bl_label = "GP Colors"

	@classmethod
	def poll(cls, context):
		if context.gpencil_data is None:
			return False

		gpd = context.gpencil_data
		return bool(gpd.layers.active)

	@staticmethod  
	def draw(self, context):
		layout = self.layout
		palette = context.active_gpencil_palette

		if palette:
			row = layout.row(align=True)
			row.alignment = 'CENTER'	
			row.label(GPColorsPopup.bl_label, icon='GREASEPENCIL')
	
			row = layout.row(align=True)
			row.operator_context = 'EXEC_REGION_WIN'
			row.operator_menu_enum("gpencil.palette_change", "palette", text="", icon='COLOR')
			row.prop(palette, "name", text="")
			row.operator("gpencil.palette_add", icon='ZOOMIN', text="")
			row.operator("gpencil.palette_remove", icon='X', text="")

			row = layout.row()
			col = row.column()
			if len(palette.colors) >= 2:
				color_rows = 5
			else:
				color_rows = 2
			col.template_list("GPENCIL_UL_palettecolor", "", palette, "colors", palette.colors, "active_index",
							  rows=color_rows)

			col = row.column()

			sub = col.column(align=True)
			sub.operator("gpencil.palettecolor_add", icon='ZOOMIN', text="")
			sub.operator("gpencil.palettecolor_remove", icon='ZOOMOUT', text="")

			palcol = context.active_gpencil_palettecolor
			if palcol:
				sub.menu("GPENCIL_MT_palettecolor_specials", icon='DOWNARROW_HLT', text="")

			if len(palette.colors) > 1:
				col.separator()

				sub = col.column(align=True)
				sub.operator("gpencil.palettecolor_move", icon='TRIA_UP', text="").direction = 'UP'
				sub.operator("gpencil.palettecolor_move", icon='TRIA_DOWN', text="").direction = 'DOWN'

			pcolor = palette.colors.active
			if pcolor:
				self.draw_palettecolors(layout, pcolor)


	def draw_palettecolors(self, layout, pcolor):
		split = layout.split(percentage=0.5)
		split.active = not pcolor.lock

		col = split.column(align=True)
		col.enabled = not pcolor.lock
		col.label(text="Stroke:")
		col.prop(pcolor, "color", text="")
		col.prop(pcolor, "alpha", slider=True)

		col = split.column(align=True)
		col.enabled = not pcolor.lock
		col.label(text="Fill:")
		col.prop(pcolor, "fill_color", text="")
		col.prop(pcolor, "fill_alpha", text="Opacity", slider=True)

		split = layout.split(percentage=0.5)
		split.active = not pcolor.lock

		col = split.column(align=True)
		col.active = not pcolor.lock
		col.prop(pcolor, "use_volumetric_strokes")
		col = split.column(align=True)
		col.active = not pcolor.lock
		col.prop(pcolor, "use_hq_fill")
		
		
	def invoke(self, context, event):
		wm = context.window_manager
		return wm.invoke_popup(self, width=225, height=350)


	def execute(self, context):
		return {'FINISHED'}


	def check(self, context):
		return True


addon_keymaps = []


def register():	
	bpy.utils.register_class(GPColorsPopup)

	wm = bpy.context.window_manager
	km = wm.keyconfigs.addon.keymaps.new(name='Grease Pencil', space_type='EMPTY')
	kmi = km.keymap_items.new(GPColorsPopup.bl_idname, 'C', 'PRESS', key_modifier='D')
	addon_keymaps.append((km, kmi))


def unregister():	
	bpy.utils.unregister_class(GPColorsPopup)
	
	for km, kmi in addon_keymaps:
		km.keymap_items.remove(kmi)
	addon_keymaps.clear()


if __name__ == "__main__":
	register()

(Starzar) #8

@AFWS- thanks for the script.However their is only one issue,it does not compile in Blender 2.79b.
Did try running the script in the python console, but no error /no output.


(Starzar) #9

@AFWS -Many thanks for the help , but as mentioned in my previous reply ,your script didn’t compile in Blender 2.79 and didn’t give any error on running in the python console (refer attached jpg).


(AFWS) #10

1: copy the code again
2: “Run Script” from text editor
3: you need to be in 3D viewport
4: you need a grease pencil stroke
5: “D” + “C”


(Starzar) #11

@AFWS-Thanks for showing so much interest.However,as mentioned previously your code does not work in Blender 2.79b(Note - code was test in both “Object” &“Edit_mode”).
It gives an error ,as seen below

RuntimeError: Operator bpy.ops.object.dialog_operator.poll() failed, context is
incorrect

-


(AFWS) #12

Can you test in a previous version of Blender and see if it still gives the error? I think I’m using 2.79a official release. Also, if it still gives a error with a previous version, can you post a video of the exact steps you are doing inside of Blender?