Export File Dialog in Blender 2.80 How can I get bring up a popup file browser in Blender 2.80 invoked by a button click in my addon, to define the name and location of a file to be exported?

In Blender 2.7x, I did this via the context window manager in the invoke function, but it is very possible that this hasn't been the preferred method for years, as I'm not finding useful documentation when I search for how to update this.

class MYTOOL_OT_write_data(bpy.types.Operator):
    bl_idname = "mytool.write_data"
    bl_label = "Write data file"

    directory: bpy.props.StringProperty(subtype="FILE_PATH")
    filename: bpy.props.StringProperty(subtype="FILE_NAME")

    def invoke(self, context, event):  # 2.7x method
        WindowManager = context.window_manager
        context.window_manager.FILEBROWSERect_add(self)  # &lt;-- error here
        return{"RUNNING_MODAL"}

    def execute(self, context):
        directory = self.directory
        filename = self.filename
        # Write csv data file using normal python functionality
        return{'FINISHED'}


In Blender 2.80, I am getting the error "AttributeError: 'WindowManager' object has no attribute 'FILEBROWSERect_add'".

Thanks for your help.



UPDATED SOLUTION based on brockmann's solution below.  The trick is to use the ExportHelper util (which existed long before Blender 2.80):

from bpy_extras.io_utils import ExportHelper

class MYTOOL_OT_write_data(bpy.types.Operator, ExportHelper):
    bl_idname = "mytool.write_data"
    bl_label = "Write data file"

    filename_ext = ".csv"  # ExportHelper mixin class uses this
    def execute(self, context):
        filepath = self.filepath
        # f = open(filepath, 'w')
        # f.write(stuff)
        # f.close()
        return{'FINISHED'}
Have a look into the Templates: Templates > Python > Operator File Export. For convenience the script adds some predefined properties as well as a custom entry to the File &gt; Export menu:

import bpy


def write_some_data(context, filepath, use_some_setting):
    print("running write_some_data...")
    f = open(filepath, 'w', encoding='utf-8')
    f.write("Hello World %s" % use_some_setting)
    f.close()

    return {'FINISHED'}


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


class ExportSomeData(Operator, ExportHelper):
    """This appears in the tooltip of the operator and in the generated docs"""
    bl_idname = "export_test.some_data"  # important since its how bpy.ops.import_test.some_data is constructed
    bl_label = "Export Some Data"

    # ExportHelper mixin class uses this
    filename_ext = ".txt"

    filter_glob: StringProperty(
        default="*.txt",
        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,
    )

    type: EnumProperty(
        name="Example Enum",
        description="Choose between two items",
        items=(
            ('OPT_A', "First Option", "Description one"),
            ('OPT_B', "Second Option", "Description two"),
        ),
        default='OPT_A',
    )

    def execute(self, context):
        return write_some_data(context, self.filepath, self.use_setting)


# Only needed if you want to add into a dynamic menu
def menu_func_export(self, context):
    self.layout.operator(ExportSomeData.bl_idname, text="Text Export Operator")


def register():
    bpy.utils.register_class(ExportSomeData)
    bpy.types.TOPBAR_MT_file_export.append(menu_func_export)


def unregister():
    bpy.utils.unregister_class(ExportSomeData)
    bpy.types.TOPBAR_MT_file_export.remove(menu_func_export)


if __name__ == "__main__":
    register()

    # test call
    bpy.ops.export_test.some_data('INVOKE_DEFAULT')