ImportHelper: Common methods for FileHandler drag and drop support #119774

Merged
Jesse Yurkovich merged 2 commits from deadpin/blender:bpy_extras into main 2024-03-29 01:23:02 +01:00
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):
deadpin marked this conversation as resolved Outdated

I think it would be better to follow the c++ implementation and use bl_label as confirmation text by default rather than “Ok”:

    def invoke_popup(self, context, confirm_text=""):
        if not confirm_text:
            confirm_text = self.bl_label
I think it would be better to follow the c++ implementation and use `bl_label` as confirmation text by default rather than “Ok”: ```python def invoke_popup(self, context, confirm_text=""): if not confirm_text: confirm_text = self.bl_label ```
context.window_manager.fileselect_add(self)
return {'RUNNING_MODAL'}
def invoke_popup(self, context, confirm_text=""):
deadpin marked this conversation as resolved Outdated

This should be translated:

@@ -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):
@@ -106,9 +109,11 @@ class ImportHelper:
         if self.properties.is_property_set("filepath"):
             title = self.filepath
             if len(self.files) > 1:
-                title = "Import %d files" % len(self.files)
-            context.window_manager.invoke_props_dialog(self, confirm_text=confirm_text, title=title)
-            return {'RUNNING_MODAL'}
+                title = iface_("Import {} files").format(len(self.files))
+            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'}

I’m using str.format() simply to reuse the existing translation message from the c++ implementation.

This should be translated: ```diff @@ -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): @@ -106,9 +109,11 @@ class ImportHelper: if self.properties.is_property_set("filepath"): title = self.filepath if len(self.files) > 1: - title = "Import %d files" % len(self.files) - context.window_manager.invoke_props_dialog(self, confirm_text=confirm_text, title=title) - return {'RUNNING_MODAL'} + title = iface_("Import {} files").format(len(self.files)) + 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'} ``` I’m using `str.format()` simply to reuse the existing translation message from the c++ implementation.
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",