B3.2: per-image annotations

Hi all :slight_smile:

I think i’ve been dreaming of this since 2.49b ( or at least since the grease pencil exists in blender… )

This might seem dumb to lots of you but annotating things like this:

In image UV editor is pretty usefull ( to my old deficient brain ) :stuck_out_tongue:

The bad part is that those annotations ( called GPencil as a proof Grease pencil is one more unthought and unplanned hack in blender code ) are not “Per-image” setup.

They are “Per-frame” setup ( and i can understand this as GP became an animated ( and really great ) feature of blender ) but there’s no way to change GP layer on a ‘Per-image’ way in UV-editor.
Why i’d want this ?
Simply because my images are all different and each need a different GP layer.
Of course this can be done by hand… enable layer, disable layer etc… ( Mostly the same way as we can make holes in cards for ‘writing’ floating-point coordinates of our 3D models :rofl: wich would be kinda non-serious… as it is prehistory )

Now that i have a good canvas ( oh don’t think am happy with my 17,3 miles-long-script ! :rage: and this will soon be the topic of my near future questions in python… ) for playing with blender scripting, i decided to give a try to an automatic GP layer switching according to texture name in UV editor…

I see you laughing at me :joy: as this might be damn simple to you PYcoders ! But to me it is a trial !

so…

I’ll start with

bpy.context.screen.areas

caught from a loop the 4th one is the ‘IMAGE_EDITOR’ area…
I’m on the way to grab the space[0] from it ( wich seems to be the ‘client’ zone; am i right ? ) and get the ‘grease_pencil’ member…

I don’t know more now but i’ll post as soon as i get further…

If anyone have ideas or advices, i’d be really pleased to read you :stuck_out_tongue:

Happy blending !

2 Likes

Back there :smiley:

Seems quite simple ! ( though somewhat dirty as things appear to be global/static as they sometimes are in C++ or C#… ) but here’s the thing:

all GP layers are stored in a global list :open_mouth:
You just have to get them by name and apply them to the UV editor, with something like this:


def SetGPLayerInUVEditor(GPLayerName:str):
    for a in bpy.context.screen.areas:
        if a.type == 'IMAGE_EDITOR':
            space = a.spaces[0] # retrieve the 2D view zone
            if(GPLayerName != None):
                space.grease_pencil = bpy.data.grease_pencils[GPLayerName]
            else:
                space.grease_pencil = None
                

#==========================================================================================



prevImgName:str="."
def SetAnnotationsForUVEditorImage(imgName:str):
    
    global prevImgName
    
    if(imgName != prevImgName):
        prevImgName = imgName
        print("changing Annotations for "+imgName)
        
        if(imgName=="main_DIFF1850.tga"):
            SetGPLayerInUVEditor('Annotations.002')
        else:
            SetGPLayerInUVEditor(None)
    
    return

#==========================================================================================

Works like a charm !

Now i have to make a small menu for correspondances of image-name <—> layer name and voila :smiley:

Happy blending !

3 Likes

Hi all !

Finally i did it :smiley:
And it works like a charm !
annotations

Now i should make it more ‘user-friendly’ by creating an interface in the Properties panel, allowing to add entries for correspondance of texture-name <—> annotation-name but i’m somewhat lazy for making blender UI and anyway i don’t know how to save this data at file save…
So i hardcoded the correspondances in script ( the bad & dirty way )…

Happy blending !

2 Likes

This is awesome, can you share your final script?

1 Like

oh :smiley: glad you like it @joseph :smiley:

Of course i can share ! But not as is as my script does a lot of other things :sweat_smile:
Just lemme separate the annotations part and post it here…

Also, as an answer to my previous post, saving scripted ( and edited ) data can be done through scene custom properties :slight_smile:
This might be a cleaner workaround for hardcoding texture <—> annotations, without the pain of a full UI edition script…

I be back soon :slight_smile:

Happy blending !

hmm i think all is here…
I stripped most things of the script but had to leave the Panel UI part as the feature is called in the draw panel function: the dirty but simple way…
I think this could be done through a handler but i don’t know yet what event is called at UVEditor image change…

so here’s the script; as-is :stuck_out_tongue:

import bpy
import numpy as np
from mathutils import Vector
import math

from bpy.props import (StringProperty,
                       BoolProperty,
                       IntProperty,
                       FloatProperty,
                       EnumProperty,
                       PointerProperty,
                       )
from bpy.types import (Panel,
                       Operator,
                       PropertyGroup,
                       )
#==========================================================================================    



def SetImageInUVEditor(img):
    for a in bpy.context.screen.areas:
        if a.type == 'IMAGE_EDITOR':
            space = a.spaces[0] # retrieve the 2D view zone
            space.image = img

#==========================================================================================

def GetImageInUVEditor()->str:
    for a in bpy.context.screen.areas:
        if a.type == 'IMAGE_EDITOR':
            space = a.spaces[0] # retrieve the 2D view zone
            return(space.image.name)

#==========================================================================================


def SetGPLayerInUVEditor(GPLayerName:str):
    for a in bpy.context.screen.areas:
        if a.type == 'IMAGE_EDITOR':
            space = a.spaces[0] # retrieve the 2D view zone
            if(GPLayerName != None):
                space.grease_pencil = bpy.data.grease_pencils[GPLayerName]
            else:
                space.grease_pencil = None
                

#==========================================================================================

# table of correspondances texture <---> annotations
# here's what you should setup the proper names of textures and annotation layers....
textureAnnotationCorresponding =[
["main_DIFF1850.tga","Annot-0"],
["fortif_dirt.tga","Annot-1"],
["cccc",None],
]

prevImgName:str="."
def SetAnnotationsForUVEditorImage(imgName:str):
    
    found:bool = False
    
    global prevImgName
    
    if(imgName != prevImgName):
        prevImgName = imgName
        print("changing Annotations for "+imgName)
        
        for entry in textureAnnotationCorresponding:
            if(entry[0]==imgName):
                found=True
                SetGPLayerInUVEditor(entry[1])
        
        if(found==False):
            SetGPLayerInUVEditor(None)
    
    return

#==========================================================================================

    

    
#**************************************************
#
# The class for parameters ( LAUGH MY ASS OFF !! )
#
# python: the art of obfuscating simple things....
#
#**************************************************
class MyBakesParameters(PropertyGroup):
    
                            
    foldCarcassMats : BoolProperty(
                      name="useless stupid thing",
                      description="Open texture/UV helper",
                      default = False) 


#==========================================================================================    

  


def foldout(SPN,row,boolVar:str,text,icon)->bool:

    state = getattr(SPN,boolVar)

    row.prop(SPN,
             boolVar,
             icon='TRIA_DOWN' if state else 'TRIA_RIGHT',
             icon_only=True)
             
    row.alert=True
    row.label(text=text, icon=icon)
    row.alert=False 
    
    return(state)

#==========================================================================================    


#**************************************************
#
# The class for drawing previous both classes
# ( LOOOOOOOOOOL )
#
# python: the art of obfuscating simple things....
#
#**************************************************
class CarcassBakesPanel(bpy.types.Panel):
    """Creates a Panel in the Object properties window"""
    bl_label = "Joseph script"
    bl_idname = "OBJECT_PT_hello"
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'
    bl_context = "data"


    

    def draw(self, context):



        obj = context.active_object
        layout = self.layout
        
        
        SPN = context.window_manager.stupidPythonNeed
        
    
        # ========================================
        # Tex/UV helper
        # ========================================
        box = layout.box()
        row = box.row()

        currentImageName = GetImageInUVEditor()
        SetAnnotationsForUVEditorImage(currentImageName)

        # foldout for Tex/UV helper ( here's what should look blender's python API like )
        if foldout(SPN,row,'foldCarcassMats', 'Carcass tex/UV helper','TEXTURE'):

            row = box.row()
            row.label(text="Here you can put the UI you want...")
            
        
        
bl_info = {
    "name": "JosephScript",
    "blender": (2, 80, 0),
    "category": "Object",
}



def register():
    bpy.utils.register_class(MyBakesParameters)
    bpy.utils.register_class(CarcassBakesPanel)

    bpy.types.WindowManager.stupidPythonNeed = PointerProperty(type=MyBakesParameters)


def unregister():
    bpy.utils.unregister_class(CarcassBakesPanel)
    bpy.utils.unregister_class(MyBakesParameters)
    
    del bpy.types.WindowManager.stupidPythonNeed



if __name__ == "__main__":
    register()

It creates a small menu in the Outliner → mesh tab.
You have nothing to do with this menu.
For correspondance image <—> annotation, you have to hardcode it in the script.
And that’s it :slight_smile:

Tell me if you got further questions @joseph

Happy blending !

1 Like