I'm new with Blender 3.4 and (bpy.)Python script.

I have build a script from template and found some hints in the web. It shows a list of Material/Texture-Websites (url-links) and it can successful open the url in the browser on button click. The list is build with bpy.props.EnumProperty and an execute function opens the url. My code works flawlessly but it's a bit overloaded, see what I mean:

Enumerator class:

class UrlOperator(bpy.types.Operator):
    bl_label = "Choose URL"
    bl_idname = "wm.template_operator"
    preset_enum : bpy.props.EnumProperty(
        name= "",
        description="Select an option",
            ('1',  "AmbientCG", 'https://ambientcg.com/' ),
            ('2',  "Texturify", 'https://texturify.com/'),
            ('3',  "BelderKit", 'https://www.blenderkit.com/'),
            ('4',  "CGBookCase", 'https://www.cgbookcase.com/textures'),
            ('5',  "PolyHaven", 'https://polyhaven.com/'),
            ('6',  "3DTextures", 'https://3dtextures.me/'),
            ('7',  "Textures", 'https://www.textures.com/free'),
            ('8',  "Duion", 'https://duion.com/art/main'),
            ('9',  "OpenGameArt", 'https://opengameart.org/'),
            ('10', "TextureNinja",'https://textureninja.com/' ),
            ('11', "ShareTextures", 'https://www.sharetextures.com/assets'),
            ('12', "Substance3D Adobe (Account)", 'https://substance3d.adobe.com/assets/allassets?free=true'),
            ('13', "Empty Free",'' ),
            ('14', "Empty Free", '')        

In the execute function I evaluate the 1st value item:

    def execute(self, context):
        self.report({'INFO'}, "Selected:" + self.preset_enum)       
# overload follows here:        
        if self.preset_enum == '1' : webbrowser.open('https://ambientcg.com/')
        if self.preset_enum == '2' : webbrowser.open('https://texturify.com/')
        if self.preset_enum == '3' : webbrowser.open('https://www.blenderkit.com/')
        if self.preset_enum == '4' : webbrowser.open('https://www.cgbookcase.com/textures')
        if self.preset_enum == '5' : webbrowser.open('https://polyhaven.com/')
        if self.preset_enum == '6' : webbrowser.open('https://3dtextures.me/')
        if self.preset_enum == '7' : webbrowser.open('https://www.textures.com/free')
        if self.preset_enum == '8' : webbrowser.open('https://duion.com/art/main')
        if self.preset_enum == '9' : webbrowser.open('https://opengameart.org/')
        if self.preset_enum == '10' : webbrowser.open('https://textureninja.com/')
        if self.preset_enum == '11' : webbrowser.open('https://www.sharetextures.com/assets')
        if self.preset_enum == '12' : webbrowser.open('https://substance3d.adobe.com/assets/allassets?free=true')   
        return {'FINISHED'}  

But I dont know how to get the value with the url in it : "('1', "AmbientCG", 'https://ambientcg.com/' ),". to avoid the many if statements. As you can see I find the '1' - value, the question is how to get the url - value?

Has anyone a line of correct code for my issue?


2 Answers 2


I've found this inspecting objects with pdb. Maybe there is a better way to do this.

class UrlOperator(bpy.types.Operator):

    def execute(self, context):
        enum_items = self.properties.bl_rna.properties['preset_enum'].enum_items
        url = enum_items.get(self.preset_enum).description
  • 2
    $\begingroup$ Answers are often more pleasant to read with a body paragraph, even for simple code fixes. You can also add details and explain why it has to be the way it is, and where the snippet should lie in the code $\endgroup$
    – sybog64
    Feb 4, 2023 at 22:44
  • 1
    $\begingroup$ Hi, thanks for the post. This site is not a regular forum, answers should be substantial, stand on their own, and thoroughly explain the solution and required steps. One liners and short tips rarely make for a good answer. If you can edit your post and provide some more details about the procedure and how it works, perhaps add a few images illustrating some steps and final result. See How to write a good answer?, otherwise it may be converted to a comment. $\endgroup$ Feb 5, 2023 at 2:04
  • 1
    $\begingroup$ Thanks, this is the solution I search for. It illustrates the way to touch the iterm properties perfect. :-) $\endgroup$
    – wiwa53
    Feb 5, 2023 at 7:17
  • $\begingroup$ I've update my answer. But I don't have a lot of experience with Blender, so I can't give a comprehensive answer for now. I do my best. $\endgroup$
    – Kriplins
    Feb 5, 2023 at 11:06
  • $\begingroup$ Thanks again, your first answer will work perfect - that's similar what I've thought about on my search for a solution. $\endgroup$
    – wiwa53
    Feb 6, 2023 at 7:16

Have completed the code above with dynamic add items from text-file to EnumProperty. But I see no solution to put the code into a callback function. If the code starts in the root it will work fine for me:

1st: the structure of the text file:


2nd: the code to build the items-tuple:

# def dyn_enum_items_add(self, context): See no chance to make this run with following code in this function
# so bring this code to root and it runs:

# dynamicly construct EnumProperty.Items from textfile
# This Scrip Path + Addon + filename
path = bpy.utils.user_resource('SCRIPTS') + '\\addons\\URLPanelitems.txt'
# make Blender abspath
f = Path(bpy.path.abspath(path))
# open file as csv and convert to list
if f.exists():
    with open(f,'r') as file:
        reader = csv.reader(file)
        lst = list(tuple(line) for line in reader)

# convert list into tuple 
url_items_dyn = tuple(lst)
# return url_items_dyn

3rd: the EnumProperty code:

class UrlOperator(bpy.types.Operator):
    bl_label = "Choose URL"
    bl_idname = "wm.template_operator"
    preset_enum : bpy.props.EnumProperty(
        name= "",  
        description = "Call selected URL",     
        items = url_items_dyn

If anybody has an idea to put the code in a callback function, let me know. If I run the code (2nd) from callback I get this error in my execute function:

url = enum_items.get(self.preset_enum).description

AttributeError: 'NoneType' object has no attribute 'description'

    def execute(self, context):        
        # The way to get a EnumProperty.Value:
        enum_items = self.properties.bl_rna.properties['preset_enum'].enum_items        
        # value .description    
        url = enum_items.get(self.preset_enum).description        
        # call it in browser
        return {'FINISHED'}   

It seems there comes an empty tuple out of the callback?


You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .