1
$\begingroup$

I have a panel in 3D_View that shows the current file and allows the selection of a new file. When the file is selected, the panel needs to redraw. How do I force a redraw?

In Operator.py file

class OT_TestOpenFilebrowser(Operator, ImportHelper): 
    #https://sinestesia.co/blog/tutorials/using-blenders-filebrowser-with-python/
    bl_idname = "file.open_filebrowser" 
    bl_label = "Open the file browser (yay)" 
    filter_glob: StringProperty( default='*.xml', options={'HIDDEN'} )
    run=0
    def execute(self, context): 
        if not 'PMTfileName' in globals():
            global PMTfileName
            PMTfileName = "None2"        
            self.report({'INFO'}, PMTfileName+ " created in op") 
        PMTfileName, extension = os.path.splitext(self.filepath)
        head_tail = os.path.split(self.filepath) 
        PMTfileName = head_tail[1]
        print(PMTfileName)
        self.report({'INFO'}, PMTfileName)
        self.report({'INFO'}, extension)
        self.report({'INFO'}, self.filepath)
        context.area.tag_redraw()
        self.__class__.run += 1
        """Do something with the selected file(s).""" 
        return {'FINISHED'}
bpy.utils.register_class(OT_TestOpenFilebrowser)

If Panel file:

class PMT_PT_Pannel(Panel):
    bl_space_type = "VIEW_3D"
    bl_region_type = "UI"
    bl_label = "PMT Builder"
    bl_category = "21Geo Builder"

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

        #2 Columns with Buttons
        box = layout.box()
        box.label(text="Open File: "+PMTfileName) 
        row = layout.row()
        row2 = layout.row()

        col3 = row.column()
        col3.operator("file.open_filebrowser", text="Select File" )


        col = row2.column()
        col.operator("object.apply_all_mods", text="Apply all")

        col2 = row2.column()
        col2.operator("export.some_data", text="Export")
$\endgroup$
2
$\begingroup$

I suggest start here: How to create a custom UI?


Blender will take care of (re-)drawing the layout properly without doing anything if you add a dedicated StringProperty for your filepath to the Scene, Object or whatever makes sense in your case eg. bpy.types.Scene.my_path = StringProperty(name="My Path") and assign self.filepath attribute from the ImportHelper class to your path property:

enter image description here

Code example based on the famous Operator File Import template that comes with Blender:

import bpy
import os

# ImportHelper is a helper class, defines filename and
# invoke() function which calls the file selector.
from bpy_extras.io_utils import ImportHelper
from bpy.props import StringProperty, BoolProperty, EnumProperty
from bpy.types import Operator, Panel


class ImportSomeData(Operator, ImportHelper):
    """This appears in the tooltip of the operator and in the generated docs"""
    bl_idname = "import_test.some_data"  # important since its how bpy.ops.import_test.some_data is constructed
    bl_label = "Import XML Data"

    # ImportHelper mixin class uses this
    filename_ext = ".xml"

    filter_glob: StringProperty(
        default="*.xml",
        options={'HIDDEN'},
        maxlen=255,  # Max internal buffer length, longer would be clamped.
    )

    # List of operator properties, the attributes will be assigned
    # to the class instance from the operator settings before calling.
    use_setting: BoolProperty(
        name="Example Boolean",
        description="Example Tooltip",
        default=True,
    )

    def execute(self, context):
        folder, file = os.path.split(self.filepath)
        context.scene.my_path = self.filepath
        print (folder, file)
        return {'FINISHED'}


class OBJECT_PT_CustomPanel(Panel):
    bl_label = "My Panel"
    bl_idname = "OBJECT_PT_custom_panel"
    bl_space_type = "VIEW_3D"   
    bl_region_type = "UI"
    bl_category = "Tools"
    bl_context = "objectmode"
    '''
    @classmethod
    def poll(self,context):
        return context.object is not None
    '''
    def draw(self, context):
        layout = self.layout
        scene = context.scene
        layout.prop(scene, "my_path", text="File")
        layout.operator(ImportSomeData.bl_idname)
        layout.separator()
        
def register():
    bpy.utils.register_class(ImportSomeData)
    bpy.utils.register_class(OBJECT_PT_CustomPanel)
    bpy.types.Scene.my_path = StringProperty(name="My Path")


def unregister():
    bpy.utils.unregister_class(OBJECT_PT_CustomPanel)
    bpy.utils.unregister_class(ImportSomeData)
    del bpy.types.Scene.my_path


if __name__ == "__main__":
    register()

    # test call
    #bpy.ops.import_test.some_data('INVOKE_DEFAULT')
$\endgroup$
3
  • $\begingroup$ When I do this I get this error: AttributeError: Writing to ID classes in this context is not allowed: Scene, Scene datablock, error setting Scene.my_path $\endgroup$
    – doby
    Feb 20 at 2:54
  • $\begingroup$ Are you trying to re-write the property (assigning something to my_path) within the panel class...? If so, please study my demo code. OR What part of my demo code do you have changed to get this error? Tested on 2.91 but I don't see any reason why it should not work for versions pre 2.91 @doby Can not help much based on your comment, I suggest edit your question and add more detail about what you're doing exactly. $\endgroup$
    – brockmann
    Feb 20 at 8:21
  • $\begingroup$ I got it working. I failed to see that you initialized the variable in the execute. It worked once I did that. Thank you! $\endgroup$
    – doby
    Feb 21 at 16:15

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.