0
$\begingroup$

I am carrying out a project that requires a json file, which is automatically downloaded when Blender starts. This json file contains a list of internet sites, and I wish I could populate a series of buttons in the menu.

The fundamental problem is that I would only like to load the json file once I find it, but I don't know where to store it. The script works like this (approx.):

File my_json.json:

 {
     "site_list":{"Stackexchange":"https://blender.stackexchange.com/",
                  "Google":"https://google.com/"}    
 }

Code:

import bpy,json,os,webbrowser
from bpy.props import StringProperty


def open_json():
    json_file = "G:\\my_json.json"
    f = open(json_file) 
    data = json.load(f)  
    f.close()
    return data


class HelloWorldPanel(bpy.types.Panel):
    """Creates a Panel in the Object properties window"""
    bl_label = "Hello World Panel"
    bl_idname = "OBJECT_PT_hello"
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'
    bl_context = "object"

    def draw(self, context):
        layout = self.layout
        col=layout.column()
        
        
        data = open_json()
        for site , adress in data['site_list'].items():
            col.operator("object.simple_operator",text = site).options = adress #I pass a string to the operator (Web address)
        
        
        
class SimpleOperator(bpy.types.Operator):
    """Tooltip"""
    bl_idname = "object.simple_operator"
    bl_label = "Simple Object Operator"
    
    options: StringProperty()

    def execute(self, context):
        
        webbrowser.open(self.options) #Open web adress in the default browser

        return {'FINISHED'}


def register():
    bpy.utils.register_class(SimpleOperator)
    bpy.utils.register_class(HelloWorldPanel)

def unregister():
    bpy.utils.unregister_class(SimpleOperator)
    bpy.utils.unregister_class(HelloWorldPanel)


if __name__ == "__main__":
    register()

Result:

enter image description here

This method works, but the problem is that it iterates over and over to the Json file, opens it, reads it, closes it, and returns the data contained in it in the function return.

I was wondering where I could store this data, one time only. Read the file once, not read it again, and store the data somewhere.

$\endgroup$
1
$\begingroup$

Only layout in the draw method.

Every time the UI is redrawn a new panel class is instanced and its draw method called. Hence the excessive number of reads.

If the data is not changing then can read once when the module is imported, or when registered.

Have added an example of reading the data in operator definition ,making the data an enum property of the operator, and using a generic layout to expand the operator enum.

import bpy,json,os,webbrowser
from bpy.props import EnumProperty


def open_json():
    json_file = "/home/batfinger/Desktop/json.json"
    f = open(json_file) 
    data = json.load(f)  
    f.close()
    return data


class HelloWorldPanel(bpy.types.Panel):
    """Creates a Panel in the Object properties window"""
    bl_label = "Hello World Panel"
    bl_idname = "OBJECT_PT_hello"
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'
    bl_context = "object"

    def draw(self, context):
        layout = self.layout
        col=layout.column()
        col.operator_menu_enum("object.simple_operator", "site")
        
        
        
class SimpleOperator(bpy.types.Operator):
    """Tooltip"""
    bl_idname = "object.simple_operator"
    bl_label = "Simple Object Operator"
    data = open_json()['site_list']
    site: EnumProperty(
        items = [(site, site, address)
            for site, address in data.items()],
        name="Site",
        )
        
    def execute(self, context):
        
        webbrowser.open(self.data[self.site]) #Open web adress in the default browser

        return {'FINISHED'}


def register():
    bpy.utils.register_class(SimpleOperator)
    bpy.utils.register_class(HelloWorldPanel)

def unregister():
    bpy.utils.unregister_class(SimpleOperator)
    bpy.utils.unregister_class(HelloWorldPanel)


if __name__ == "__main__":
    register()

enter image description here Example of layout using

        col.operator_enum(
            "object.simple_operator", 
            "site")

If you are going to be using the data a lot would consider moving all the json in a separate module and expose the data on import.

from my_json_data import data
$\endgroup$
3
  • $\begingroup$ Hello batFINGER, I did not understand the example of the "from my_json_data import data" I'm not sure how to test this example. "my_json_data" what is ? $\endgroup$ – Noob Cat Sep 6 '20 at 15:01
  • 1
    $\begingroup$ If you don't know how to make a module and import it nevermind, doesn't matter that much. The idea is if What is now the code of the load_json method was instead in a separate file to import as a module, when imported for the first time it will read the json data into a property named data. $\endgroup$ – batFINGER Sep 6 '20 at 15:36
  • $\begingroup$ You hit the target, I don't know how to do it, but your idea seems like a good solution. Is there a link somewhere explaining how to do it? $\endgroup$ – Noob Cat Sep 6 '20 at 15:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.