3
$\begingroup$

I have the following code to create a file browser:

import bpy
from bpy.props import StringProperty
from bpy_extras.io_utils import ImportHelper
from bpy.types import Operator

class OpenBrowser(bpy.types.Operator):
    bl_idname = "open.file"
    bl_label = "Select Excel File"
    bli_description = "Simulation output excel file"
    filter_glob: StringProperty(default = '*.xls;*.xlsx',options = {'HIDDEN'})
    filepath: bpy.props.StringProperty(subtype="FILE_PATH")
    selected_file =  'No file selected'
    #somewhere to remember the address of the file

    def execute(self, context):
        self.selected_file = self.filepath 
        #self.selected_file = self.filepath
        #display = "filepath= "+self.filepath  
        #print(display) #Prints to console  
        #Window>>>Toggle systen console

        return {'FINISHED'}

    def invoke(self, context, event): # See comments at end  [1]
        context.window_manager.fileselect_add(self)
        #Open browser, take reference to 'self' 
        #read the path to selected file, 
        #put path in declared string type data structure self.filepath

        return {'RUNNING_MODAL'}  
        # Tells Blender to hang on for the slow user input


bpy.utils.register_class(OpenBrowser) 
#Tell Blender this exists and should be used


# [1] In this invoke(self, context, event) is being triggered by the below command
#but in your script you create a button or menu item. When it is clicked
# Blender runs   invoke()  automatically.

#execute(self,context) prints self.filepath as proof it works.. I hope.


bpy.ops.open.file('INVOKE_DEFAULT')
print(OpenBrowser.selected_file)

My issue is that I would except from this code to print the file path, but I get the initialization of the global class variable ('No file selected'). Could you please tell me how to access the filepath from this bpy operator?

EDIT AFTER BATFINGER'S SUGGESTION:

WINDOWS MANAGER LAST TIME PROPS:

As BatFinger suggested this does not work in a script, since the return value will be no filepath. In the console it works fine.

CLASS METHOD APPROACH:

I then used the following script:

import bpy
from bpy.props import StringProperty
from bpy_extras.io_utils import ImportHelper
from bpy.types import Operator
import time
sel = 'global'
class OpenBrowser(bpy.types.Operator):
    bl_idname = "open.file"
    bl_label = "Select Excel File"
    bli_description = "Simulation output excel file"
    filter_glob: StringProperty(default = '*.xls;*.xlsx',options = {'HIDDEN'})
    filepath: bpy.props.StringProperty(subtype="FILE_PATH")
    @classmethod
    def save_filepath(cls,filepath):
        cls.save_filepath = filepath
        return cls.save_filepath
    def __init__(self):
        print("Start")

    def __del__(self):
        print('End')
    def execute(self, context):
        global sel
        #self.save_filepath(self.filepath)
        sel = self.filepath
        print('2 '+sel) 
        #self.selected_file = self.filepath
        #display = "filepath= "+self.filepath  
        #print(display) #Prints to console  
        #Window>>>Toggle systen console
        return {'FINISHED'}
    def invoke(self, context, event):
        context.window_manager.fileselect_add(self)        
        #Open browser, take reference to 'self' 
        #read the path to selected file, 
        #put path in declared string type data structure self.filepath
        return {'RUNNING_MODAL'}  

bpy.utils.register_class(OpenBrowser) 


bpy.ops.open.file('INVOKE_DEFAULT')  
print(OpenBrowser.__class__.save_filepath)

The print at the end causes an error because no attribute save_filepath is detected (please correct me if i am doing something wrong).

Last approach I could check is with the check method, but I am not sure how to implement it. In the meantime, if you have a script that works and it is able to get the filepath from the Operator's class into the main script body, I would be more than grateful if you could share it.

$\endgroup$
2
$\begingroup$

Its running modal

The file browser as noted in question code when invoked is running modally.

print("execute", self.filepath)

in your execute method where you set the class property, will notice in output

No file selected
execute some/file/path

because the filebrowser has been invoked and is running modally, the last print in script is executed before the new print.

If however we change test call to

bpy.ops.open.file(filepath="wink")
print(OpenBrowser.selected_file)

we get

execute wink
No file selected

Which reminds us that it is a slippery slope to set a class property from an instance, ie it doesn't work so instead

self.__class__.selected_file = self.filepath

outputs:

execute wink
wink

or perhaps better still, make a class method to set it.

Window Manager Last Time Props

Another option would be to use the output of WindowManager.operator_properties_last

>>> props = C.window_manager.operator_properties_last("open.file")
>>> props.filepath
'/home/batfinger/Documents/wildhorses.txt'

which if not run prior (or get the name wrong)

>>> props = C.window_manager.operator_properties_last("open.pile") is None
True

SpaceFileBrowser.params

Finally as shown in this answer re a check method https://blender.stackexchange.com/a/207665/15543 can get the current filepath directly from the filebrowser space while it has context.

$\endgroup$
3
  • $\begingroup$ Hello @batFINGER, thank you very much for your in-detail answer. I am new to Blender and Python OOP so please excuse any silly questions I have: 1. Using the 'Window Manager Last Time Props' gave me the correct path in the console when printed, but when I add it as a last line in my script it prints an empty filepath string. Is this normal? 2. From your answer, I understood that it is impossible to return the filepath from the operator's class when running in modal. Would it be possible for you to show me the class method approach? $\endgroup$ – Chris Patr Feb 18 at 17:43
  • $\begingroup$ To make it clear, if you run invoke the filebrowser that last print will never print the newly set fp, it is however set as a class self.__class__.save_filepath = self.filepath variable, when the "import" is executed. The final print will always happen before the filepath is set if invoked!!!) Re classmethod stackoverflow.com/questions/54264073/… . So a classmethod save_filepath(cls, filepath): cls.save_filepath = filepath Used via self.save_filepath(...) and the instance will be treated as its class ... hence the name $\endgroup$ – batFINGER Feb 18 at 17:56
  • $\begingroup$ hello again batFINGER. Thanks again for your answer. i updated my question based on your suggestions.. $\endgroup$ – Chris Patr Feb 18 at 23:50

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.