3

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")

batFINGER
  • 84,216
  • 10
  • 108
  • 233
doby
  • 191
  • 7

2 Answers2

4

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')

brockmann
  • 12,613
  • 4
  • 50
  • 93
  • 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 – doby Feb 20 '21 at 02:54
  • 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. – brockmann Feb 20 '21 at 08:21
  • I got it working. I failed to see that you initialized the variable in the execute. It worked once I did that.

    Thank you!

    – doby Feb 21 '21 at 16:15
1

bpy.data.screens['Layout'].areas[3].regions[3].tag_redraw()

works in v3.3.

More generally, you can figure out the area and region index you need by typing / iterating though

bpy.data.screens[<screen/tab name>].areas[x].type

bpy.data.screens[<screen/tab name>].areas[x].regions[y].type

in the interactive console since this displays the name of that area/region as they aren't keyed like the screens are.

mugwortz
  • 41
  • 4