Skip to content

Commit

Permalink
Changed extension repo to list
Browse files Browse the repository at this point in the history
  • Loading branch information
jack-yao91 committed Nov 16, 2024
1 parent 33550b8 commit c5d00fb
Show file tree
Hide file tree
Showing 8 changed files with 207 additions and 36 deletions.
13 changes: 12 additions & 1 deletion docs/send2ue/customize/extensions.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,18 @@ Then in the Send to Unreal addon preferences set the `Extensions Repo Folder` to
Alternatively, this can be installed with python:
```python
# this is handy for reloading your changes as you develop extensions
bpy.context.preferences.addons['send2ue'].preferences.extensions_repo_path = 'C:\extension_repo'
import bpy
from pathlib import Path

my_extension_folder = r'C:\extension_repo'
preferences = bpy.context.preferences.addons['send2ue'].preferences
for extension_folder in preferences.extension_folder_list:
if Path(extension_folder.folder_path) == Path(my_extension_folder):
break
else:
extension_folder = preferences.extension_folder_list.add()
extension_folder.folder_path = my_extension_folder

bpy.ops.send2ue.reload_extensions()
```

Expand Down
Binary file modified docs/send2ue/customize/images/extensions/2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 9 additions & 8 deletions src/addons/send2ue/core/extension.py
Original file line number Diff line number Diff line change
Expand Up @@ -344,14 +344,15 @@ def _get_extension_classes(self):
# add in the additional extensions from the addons preferences
addon = bpy.context.preferences.addons.get(base_package)
if addon and addon.preferences:
if os.path.exists(addon.preferences.extensions_repo_path):
for file_name in os.listdir(addon.preferences.extensions_repo_path):
name, file_extension = os.path.splitext(file_name)
if file_extension == '.py':
extension_collector = ExtensionCollector(
os.path.join(addon.preferences.extensions_repo_path, file_name)
)
extensions.extend(extension_collector.get_extension_classes())
for extension_folder in addon.preferences.extension_folder_list: # type: ignore
if os.path.exists(extension_folder.folder_path):
for file_name in os.listdir(extension_folder.folder_path):
name, file_extension = os.path.splitext(file_name)
if file_extension == '.py':
extension_collector = ExtensionCollector(
os.path.join(extension_folder.folder_path, file_name)
)
extensions.extend(extension_collector.get_extension_classes())

# add in the extensions that shipped with the addon
for file_name in os.listdir(self.source_path):
Expand Down
55 changes: 48 additions & 7 deletions src/addons/send2ue/operators.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import bpy
import queue
import threading
from bl_ui.generic_ui_list import GenericUIListOperator
from .constants import ToolInfo, ExtensionTasks
from .core import export, utilities, settings, validations, extension
from .ui import file_browser, dialog
Expand Down Expand Up @@ -254,13 +255,13 @@ class ReloadExtensions(bpy.types.Operator):
def execute(self, context):
addon = bpy.context.preferences.addons.get(base_package)
if addon:
extensions_repo_path = addon.preferences.extensions_repo_path
if extensions_repo_path:
if not os.path.exists(extensions_repo_path) or not os.path.isdir(
extensions_repo_path
):
self.report(f'"{extensions_repo_path}" is not a folder path on disk.')
return {'FINISHED'}
for extension_folder in addon.preferences.extension_folder_list: # type: ignore
if extension_folder.folder_path:
if not os.path.exists(extension_folder.folder_path) or not os.path.isdir(
extension_folder.folder_path
):
self.report(f'"{extension_folder.folder_path}" is not a folder path on disk.')
return {'FINISHED'}

extension_factory = extension.ExtensionFactory()

Expand Down Expand Up @@ -303,6 +304,44 @@ class NullOperator(bpy.types.Operator):

def execute(self, context):
return {'FINISHED'}


class UILIST_ADDON_PREFERENCES_OT_entry_remove(GenericUIListOperator, bpy.types.Operator):
"""Remove the selected entry from the list"""

bl_idname = "uilist.addon_preferences_entry_remove"
bl_label = "Remove Selected Entry"

def execute(self, context):
addon_preferences = context.preferences.addons[ToolInfo.NAME.value]
my_list = self.get_list(addon_preferences)
active_index = self.get_active_index(addon_preferences)

my_list.remove(active_index)
to_index = min(active_index, len(my_list) - 1)
self.set_active_index(addon_preferences, to_index)

return {'FINISHED'}


class UILIST_ADDON_PREFERENCES_OT_entry_add(GenericUIListOperator, bpy.types.Operator):
"""Add an entry to the list after the current active item"""

bl_idname = "uilist.addon_preferences_entry_add"
bl_label = "Add Entry"

def execute(self, context):
addon_preferences = context.preferences.addons[ToolInfo.NAME.value]
my_list = self.get_list(addon_preferences)
active_index = self.get_active_index(addon_preferences)

to_index = min(len(my_list), active_index + 1)

my_list.add()
my_list.move(len(my_list) - 1, to_index)
self.set_active_index(addon_preferences, to_index)

return {'FINISHED'}


operator_classes = [
Expand All @@ -316,6 +355,8 @@ def execute(self, context):
ReloadExtensions,
StartRPCServers,
NullOperator,
UILIST_ADDON_PREFERENCES_OT_entry_remove,
UILIST_ADDON_PREFERENCES_OT_entry_add,
]


Expand Down
19 changes: 10 additions & 9 deletions src/addons/send2ue/properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,17 @@

import os
import sys
import uuid
import bpy
from .constants import ToolInfo, PathModes, Template
from .core import settings, formatting, extension

class ExtensionFolder(bpy.types.PropertyGroup):
folder_path: bpy.props.StringProperty(
default='',
description='The folder location of the extension repo.',
subtype='FILE_PATH'
) # type: ignore


class Send2UeAddonProperties:
"""
Expand All @@ -17,14 +23,6 @@ class Send2UeAddonProperties:
default=True,
description=f"This automatically creates the pre-defined collection (Export)"
)
extensions_repo_path: bpy.props.StringProperty(
name="Extensions Repo Path",
default="",
description=(
"Set this path to the folder that contains your Send to Unreal python extensions. All extensions "
"in this folder will be automatically loaded"
)
)
# ------------- Remote Execution settings ------------------
rpc_response_timeout: bpy.props.IntProperty(
name="RPC Response Timeout",
Expand Down Expand Up @@ -62,6 +60,9 @@ class Send2UeAddonProperties:
)
)

extension_folder_list: bpy.props.CollectionProperty(type=ExtensionFolder) # type: ignore
extension_folder_list_active_index: bpy.props.IntProperty() # type: ignore


class Send2UeWindowMangerProperties(bpy.types.PropertyGroup):
"""
Expand Down
121 changes: 116 additions & 5 deletions src/addons/send2ue/ui/addon_preferences.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,108 @@
# Copyright Epic Games, Inc. All Rights Reserved.

import bpy
from ..properties import Send2UeAddonProperties
from pathlib import Path
from ..properties import Send2UeAddonProperties, ExtensionFolder
from ..constants import ToolInfo
from .. import __package__

from bl_ui.generic_ui_list import (
_get_context_attr, # type: ignore
_draw_move_buttons # type: ignore
)

def _draw_add_remove_buttons(
*,
layout,
list_path,
active_index_path,
list_length,
):
"""Draw the +/- buttons to add and remove list entries."""
props = layout.operator("uilist.addon_preferences_entry_add", text="", icon='ADD')
props.list_path = list_path
props.active_index_path = active_index_path

row = layout.row()
row.enabled = list_length > 0
props = row.operator("uilist.addon_preferences_entry_remove", text="", icon='REMOVE')
props.list_path = list_path
props.active_index_path = active_index_path

def draw_ui_list(
layout,
context,
class_name="UI_UL_list",
*,
unique_id,
list_path,
active_index_path,
insertion_operators=True,
move_operators=True,
menu_class_name="",
**kwargs,
):
"""
This overrides the draw_ui_list function from the generic_ui_list module
so that we can draw the add and remove buttons for a list in the addon preferences.
By default, the generic_ui_list module buttons link to ops that receive the scene
context, which is not what we want in this case. So we had to create new ops that
do this job.
"""

row = layout.row()

list_owner_path, list_prop_name = list_path.rsplit('.', 1)
list_owner = _get_context_attr(context, list_owner_path)

index_owner_path, index_prop_name = active_index_path.rsplit('.', 1)
index_owner = _get_context_attr(context, index_owner_path)

list_to_draw = _get_context_attr(context, list_path)

row.template_list(
class_name,
unique_id,
list_owner, list_prop_name,
index_owner, index_prop_name,
rows=4 if list_to_draw else 1,
**kwargs,
)

col = row.column()

if insertion_operators:
_draw_add_remove_buttons(
layout=col,
list_path=list_path,
active_index_path=active_index_path,
list_length=len(list_to_draw),
)
layout.separator()

if menu_class_name:
col.menu(menu_class_name, icon='DOWNARROW_HLT', text="")
col.separator()

if move_operators and list_to_draw:
_draw_move_buttons(
layout=col,
list_path=list_path,
active_index_path=active_index_path,
list_length=len(list_to_draw),
)

# Return the right-side column.
return col


class FOLDER_UL_extension_path(bpy.types.UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_prop_name):
row = layout.row()
row.alert = False
if item.folder_path and not Path(item.folder_path).exists():
row.alert = True
row.prop(item, "folder_path", text="", emboss=False)

class SendToUnrealPreferences(Send2UeAddonProperties, bpy.types.AddonPreferences):
"""
Expand Down Expand Up @@ -35,16 +133,27 @@ def draw(self, context):
row.prop(self, 'command_endpoint', text='')
row = self.layout.row()

row.label(text='Extensions Repo Path:')
row.label(text='Extensions Repo Paths:')
row = self.layout.row()
draw_ui_list(
row,
context=bpy.context.preferences.addons[ToolInfo.NAME.value],
class_name="FOLDER_UL_extension_path",
list_path="preferences.extension_folder_list",
active_index_path="preferences.extension_folder_list_active_index",
unique_id="extension_folder_list_id",
insertion_operators=True,
move_operators=False,
) # type: ignore
row = self.layout.row()
row = row.split(factor=0.95, align=True)
row.prop(self, 'extensions_repo_path', text='')
row.operator('send2ue.reload_extensions', text='', icon='UV_SYNC_SELECT')
row.operator('send2ue.reload_extensions', text='Reload All Extensions', icon='FILE_REFRESH')

def register():
"""
Registers the addon preferences when the addon is enabled.
"""
bpy.utils.register_class(ExtensionFolder)
bpy.utils.register_class(FOLDER_UL_extension_path)
bpy.utils.register_class(SendToUnrealPreferences)


Expand All @@ -53,3 +162,5 @@ def unregister():
Unregisters the addon preferences when the addon is disabled.
"""
bpy.utils.unregister_class(SendToUnrealPreferences)
bpy.utils.unregister_class(FOLDER_UL_extension_path)
bpy.utils.unregister_class(ExtensionFolder)
7 changes: 1 addition & 6 deletions tests/utils/base_test_case.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,12 +144,7 @@ def set_extension_repo(self, path):
if self.test_environment:
path = os.path.normpath(path).replace(os.path.sep, '/')

self.blender.set_addon_property(
'preferences',
self.addon_name,
'extensions_repo_path',
path
)
self.blender.add_extension_repo(path)
self.blender.run_addon_operator(self.addon_name, 'reload_extensions')

def assert_extension_operators(self, extension_name, extension_operators, exists=True):
Expand Down
11 changes: 11 additions & 0 deletions tests/utils/blender.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import logging
import importlib
import tempfile
from pathlib import Path

try:
import bpy
Expand Down Expand Up @@ -111,6 +112,16 @@ def set_addon_property(context_name, addon_name, property_name, value, data_type
break
properties = getattr(properties, sub_property_name)

@staticmethod
def add_extension_repo(file_path):
preferences = bpy.context.preferences.addons['send2ue'].preferences
for extension_folder in preferences.extension_folder_list:
if Path(extension_folder.folder_path) == Path(file_path):
break
else:
extension_folder = preferences.extension_folder_list.add()
extension_folder.folder_path = file_path

@staticmethod
def check_particles(mesh_name, particle_names):
"""
Expand Down

0 comments on commit c5d00fb

Please sign in to comment.