ImportHelper: Common methods for FileHandler drag and drop support

Provide common implementations for two operations that all Python
FileHandlers will typically use.

- `poll_file_object_drop` To be used inside the FileHandler `poll_drop`
   callback
- `invoke_popup` To be used inside the Operator `invoke` callback

The above code closely mirrors what is currently done inside the
existing Blender c++ FileHandlers.

Pull Request: https://projects.blender.org/blender/blender/pulls/119774
This commit is contained in:
Jesse Yurkovich 2024-03-29 01:22:51 +01:00 committed by Jesse Yurkovich
parent 6dd0f6627e
commit af7a34dee7
2 changed files with 52 additions and 14 deletions

View File

@ -16,10 +16,11 @@ This ``directory`` and ``files`` properties now will be used by the
"""
import bpy
from bpy_extras.io_utils import ImportHelper
from mathutils import Vector
class ShaderScriptImport(bpy.types.Operator):
class ShaderScriptImport(bpy.types.Operator, ImportHelper):
"""Test importer that creates scripts nodes from .txt files"""
bl_idname = "shader.script_import"
bl_label = "Import a text file as a script node"
@ -28,8 +29,11 @@ class ShaderScriptImport(bpy.types.Operator):
This Operator can import multiple .txt files, we need following directory and files
properties that the file handler will use to set files path data
"""
directory: bpy.props.StringProperty(subtype='FILE_PATH', options={'SKIP_SAVE'})
files: bpy.props.CollectionProperty(type=bpy.types.OperatorFileListElement, options={'SKIP_SAVE'})
directory: bpy.props.StringProperty(subtype='FILE_PATH', options={'SKIP_SAVE', 'HIDDEN'})
files: bpy.props.CollectionProperty(type=bpy.types.OperatorFileListElement, options={'SKIP_SAVE', 'HIDDEN'})
"""Allow the user to select if the node's label is set or not"""
set_label: bpy.props.BoolProperty(name="Set Label", default=False)
@classmethod
def poll(cls, context):
@ -57,23 +61,25 @@ class ShaderScriptImport(bpy.types.Operator):
filepath = os.path.join(self.directory, file.name)
text_node.filepath = filepath
text_node.location = Vector((x, y))
# Set the node's title to the file name
if self.set_label:
text_node.label = file.name
x += 20.0
y -= 20.0
return {'FINISHED'}
"""
By default the file handler invokes the operator with the directory and files properties set.
In this example if this properties are set the operator is executed, if not the
file select window is invoked.
This depends on setting ``options={'SKIP_SAVE'}`` to the properties options to avoid
to reuse filepath data between operator calls.
"""
Use ImportHelper's invoke_popup() to handle the invocation so that this operator's properties
are shown in a popup. This allows the user to configure additional settings on the operator like
the `set_label` property. Consider having a draw() method on the operator in order to layout the
properties in the UI appropriately.
If filepath information is not provided the file select window will be invoked instead.
"""
def invoke(self, context, event):
if self.directory:
return self.execute(context)
context.window_manager.fileselect_add(self)
return {'RUNNING_MODAL'}
return self.invoke_popup(context)
class SHADER_FH_script_import(bpy.types.FileHandler):

View File

@ -23,7 +23,10 @@ from bpy.props import (
EnumProperty,
StringProperty,
)
from bpy.app.translations import pgettext_data as data_
from bpy.app.translations import (
pgettext_iface as iface_,
pgettext_data as data_,
)
def _check_axis_conversion(op):
@ -96,12 +99,28 @@ class ImportHelper:
description="Filepath used for importing the file",
maxlen=1024,
subtype='FILE_PATH',
options={'SKIP_PRESET', 'HIDDEN'}
)
def invoke(self, context, _event):
context.window_manager.fileselect_add(self)
return {'RUNNING_MODAL'}
def invoke_popup(self, context, confirm_text=""):
if self.properties.is_property_set("filepath"):
title = self.filepath
if len(self.files) > 1:
title = iface_("Import {} files").format(len(self.files))
if not confirm_text:
confirm_text = self.bl_label
confirm_text = iface_(confirm_text)
return context.window_manager.invoke_props_dialog(self, confirm_text=confirm_text, title=title, translate=False)
context.window_manager.fileselect_add(self)
return {'RUNNING_MODAL'}
def check(self, _context):
return _check_axis_conversion(self)
@ -394,6 +413,19 @@ def unpack_face_list(list_of_tuples):
return flat_ls
def poll_file_object_drop(context):
"""
A default implementation for FileHandler poll_drop methods. Allows for both the 3D Viewport and
the Outliner (in ViewLayer display mode) to be targets for file drag and drop.
"""
area = context.area
if not area:
return False
is_v3d = area.type == 'VIEW_3D'
is_outliner_view_layer = area.type == 'OUTLINER' and area.spaces.active.display_mode == 'VIEW_LAYER'
return is_v3d or is_outliner_view_layer
path_reference_mode = EnumProperty(
name="Path Mode",
description="Method used to reference paths",