Asset Pipeline: Store Asset Catalog in String #230
@ -1,15 +1,23 @@
|
|||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import bpy
|
import bpy
|
||||||
|
from typing import List
|
||||||
|
from .config import verify_task_layer_json_data
|
||||||
|
|
||||||
asset_file_cache = None
|
asset_file_cache = None
|
||||||
cat_data_cache = None
|
cat_data_cache = None
|
||||||
|
asset_cat_dict = {}
|
||||||
|
|
||||||
|
|
||||||
# TODO add refresh operator
|
def find_asset_cat_file(directory: str) -> str:
|
||||||
|
"""Find Asset Catalog file in directory or parent directories, recursively
|
||||||
|
|
||||||
|
Args:
|
||||||
|
directory (str): Directory to search for Asset Catalog file
|
||||||
|
|
||||||
def find_asset_cat_file(directory):
|
Returns:
|
||||||
|
str: Path to Asset Catalog file or None if not found
|
||||||
|
"""
|
||||||
global asset_file_cache
|
global asset_file_cache
|
||||||
if asset_file_cache is not None:
|
if asset_file_cache is not None:
|
||||||
return asset_file_cache
|
return asset_file_cache
|
||||||
@ -24,16 +32,36 @@ def find_asset_cat_file(directory):
|
|||||||
return find_asset_cat_file(parent_dir)
|
return find_asset_cat_file(parent_dir)
|
||||||
|
|
||||||
|
|
||||||
def get_asset_cat_enum_items(reload: bool = False):
|
def get_asset_catalog_items(reload: bool = False) -> List[str]:
|
||||||
|
"""Generate List of Asset Catalog Items, and Dictionary of
|
||||||
|
Asset Catalog UUIDs with their matching names. When this function
|
||||||
|
is called the list and dict of asset catalog items will be updated.
|
||||||
|
|
||||||
|
The List is used in the UI to populate the Asset Catalog List, and the
|
||||||
|
Dictionary is used to look up the asset catalog UUID based on the name.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
reload (bool, optional): Forces reload of list/dict if True. Defaults to False.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List[str]: Returns list of strings representing Asset Catalog Names
|
||||||
|
"""
|
||||||
global cat_data_cache
|
global cat_data_cache
|
||||||
if cat_data_cache is not None and not reload:
|
global asset_cat_dict
|
||||||
return cat_data_cache
|
asset_cat_list = []
|
||||||
items = []
|
|
||||||
items.append(('NONE', 'None', ''))
|
# Return Empty List if File doesn't exist
|
||||||
asset_cat_file = find_asset_cat_file(Path(bpy.data.filepath).parent.__str__())
|
asset_cat_file = find_asset_cat_file(Path(bpy.data.filepath).parent.__str__())
|
||||||
if asset_cat_file is None:
|
if asset_cat_file is None:
|
||||||
return items
|
return asset_cat_list
|
||||||
|
|
||||||
|
# Return Cached List if exists and reload is False
|
||||||
|
if cat_data_cache is not None and not reload:
|
||||||
|
return cat_data_cache
|
||||||
|
|
||||||
|
asset_cat_dict.clear() # Reset dict so it is in sync with name list
|
||||||
|
|
||||||
|
# Loop over items in file to find asset catalog
|
||||||
with (Path(asset_cat_file)).open() as file:
|
with (Path(asset_cat_file)).open() as file:
|
||||||
for line in file.readlines():
|
for line in file.readlines():
|
||||||
if line.startswith(("#", "VERSION", "\n")):
|
if line.startswith(("#", "VERSION", "\n")):
|
||||||
@ -41,6 +69,38 @@ def get_asset_cat_enum_items(reload: bool = False):
|
|||||||
# Each line contains : 'uuid:catalog_tree:catalog_name' + eol ('\n')
|
# Each line contains : 'uuid:catalog_tree:catalog_name' + eol ('\n')
|
||||||
name = line.split(':', 1)[1].split(":")[-1].strip("\n")
|
name = line.split(':', 1)[1].split(":")[-1].strip("\n")
|
||||||
uuid = line.split(':', 1)[0]
|
uuid = line.split(':', 1)[0]
|
||||||
|
asset_cat_dict[uuid] = name # Populate dict of uuid:name
|
||||||
|
asset_cat_list.append(name) # Populate list of asset catalogue names
|
||||||
|
|
||||||
items.append((uuid, name, ''))
|
cat_data_cache = asset_cat_list # Update Cache List
|
||||||
return items
|
return asset_cat_list
|
||||||
|
|
||||||
|
|
||||||
|
def get_asset_id(name: str) -> str:
|
||||||
|
"""Get Asset Catalog UUID based on Asset Catalog Name
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name (str): Asset Catalog Name
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: Asset Catalog UUID or None if not found
|
||||||
|
"""
|
||||||
|
global asset_cat_dict
|
||||||
|
for key, value in asset_cat_dict.items():
|
||||||
|
if value == name:
|
||||||
|
return key
|
||||||
|
|
||||||
|
|
||||||
|
def get_asset_name(id: str) -> str:
|
||||||
|
"""Get Asset Catalog UUID based on Asset Catalog Name
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name (str): Asset Catalog Name
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: Asset Catalog UUID or None if not found
|
||||||
|
"""
|
||||||
|
global asset_cat_dict
|
||||||
|
for key, value in asset_cat_dict.items():
|
||||||
|
if key == id:
|
||||||
|
return value
|
||||||
|
@ -3,38 +3,53 @@ from pathlib import Path
|
|||||||
import json
|
import json
|
||||||
from . import constants
|
from . import constants
|
||||||
|
|
||||||
|
|
||||||
|
# TODO could refactor this into a class, but only one instance of that class will be needed
|
||||||
|
|
||||||
TASK_LAYER_TYPES = {}
|
TASK_LAYER_TYPES = {}
|
||||||
TRANSFER_DATA_DEFAULTS = {}
|
TRANSFER_DATA_DEFAULTS = {}
|
||||||
ATTRIBUTE_DEFAULTS = {}
|
ATTRIBUTE_DEFAULTS = {}
|
||||||
|
ASSET_CATALOG_ID = ""
|
||||||
|
|
||||||
|
|
||||||
def get_json_file():
|
def get_task_layer_json_filepath() -> Path:
|
||||||
directory = Path(bpy.data.filepath).parent
|
directory = Path(bpy.data.filepath).parent
|
||||||
json_file_path = directory.joinpath(constants.TASK_LAYER_CONFIG_NAME)
|
json_file_path = directory.joinpath(constants.TASK_LAYER_CONFIG_NAME)
|
||||||
if json_file_path.exists():
|
|
||||||
return json_file_path
|
return json_file_path
|
||||||
|
|
||||||
|
|
||||||
|
def get_task_layer_dict(file_path_str="") -> dict:
|
||||||
|
if file_path_str == "":
|
||||||
|
json_file_path = get_task_layer_json_filepath()
|
||||||
|
else:
|
||||||
|
json_file_path = Path(file_path_str)
|
||||||
|
if not json_file_path.exists():
|
||||||
return
|
return
|
||||||
|
return json.load(open(json_file_path))
|
||||||
|
|
||||||
|
|
||||||
def get_task_layer_presets_path():
|
def get_task_layer_presets_path():
|
||||||
return Path(__file__).parent.joinpath(constants.TASK_LAYER_CONFIG_DIR_NAME)
|
return Path(__file__).parent.joinpath(constants.TASK_LAYER_CONFIG_DIR_NAME)
|
||||||
|
|
||||||
|
|
||||||
def verify_json_data(json_file_path=""):
|
def verify_task_layer_json_data(json_file_path=""):
|
||||||
global TASK_LAYER_TYPES
|
global TASK_LAYER_TYPES
|
||||||
global TRANSFER_DATA_DEFAULTS
|
global TRANSFER_DATA_DEFAULTS
|
||||||
global ATTRIBUTE_DEFAULTS
|
global ATTRIBUTE_DEFAULTS
|
||||||
directory = Path(bpy.data.filepath).parent
|
global ASSET_CATALOG_ID
|
||||||
if json_file_path == "":
|
|
||||||
json_file_path = directory.joinpath(constants.TASK_LAYER_CONFIG_NAME)
|
json_content = get_task_layer_dict(json_file_path)
|
||||||
if not json_file_path.exists():
|
|
||||||
|
if not json_content:
|
||||||
return
|
return
|
||||||
json_file = open(json_file_path)
|
|
||||||
json_content = json.load(json_file)
|
|
||||||
try:
|
try:
|
||||||
TASK_LAYER_TYPES = json_content["TASK_LAYER_TYPES"]
|
TASK_LAYER_TYPES = json_content["TASK_LAYER_TYPES"]
|
||||||
TRANSFER_DATA_DEFAULTS = json_content["TRANSFER_DATA_DEFAULTS"]
|
TRANSFER_DATA_DEFAULTS = json_content["TRANSFER_DATA_DEFAULTS"]
|
||||||
ATTRIBUTE_DEFAULTS = json_content["ATTRIBUTE_DEFAULTS"]
|
ATTRIBUTE_DEFAULTS = json_content["ATTRIBUTE_DEFAULTS"]
|
||||||
|
|
||||||
|
# Asset Catalog is an optional value in task_layers.json and doesn't exist by default
|
||||||
|
if "ASSET_CATALOG_ID" in json_content:
|
||||||
|
ASSET_CATALOG_ID = json_content["ASSET_CATALOG_ID"]
|
||||||
return True
|
return True
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return
|
return
|
||||||
@ -47,3 +62,10 @@ def write_json_file(asset_path: Path, source_file_path: Path):
|
|||||||
json_dump = json.dumps(json_content, indent=4)
|
json_dump = json.dumps(json_content, indent=4)
|
||||||
with open(json_file_path, "w") as config_output:
|
with open(json_file_path, "w") as config_output:
|
||||||
config_output.write(json_dump)
|
config_output.write(json_dump)
|
||||||
|
|
||||||
|
|
||||||
|
def update_task_layer_json_data(task_layer_dict: dict):
|
||||||
|
filepath = get_task_layer_json_filepath()
|
||||||
|
with filepath.open("w") as json_file:
|
||||||
|
json.dump(task_layer_dict, json_file, indent=4)
|
||||||
|
verify_task_layer_json_data()
|
||||||
|
@ -25,7 +25,8 @@ from .sync import (
|
|||||||
sync_execute_push,
|
sync_execute_push,
|
||||||
)
|
)
|
||||||
|
|
||||||
from .asset_catalog import get_asset_cat_enum_items
|
from .asset_catalog import get_asset_catalog_items, get_asset_id
|
||||||
|
from .config import verify_task_layer_json_data
|
||||||
|
|
||||||
|
|
||||||
class ASSETPIPE_OT_create_new_asset(bpy.types.Operator):
|
class ASSETPIPE_OT_create_new_asset(bpy.types.Operator):
|
||||||
@ -65,7 +66,7 @@ class ASSETPIPE_OT_create_new_asset(bpy.types.Operator):
|
|||||||
# Dynamically Create Task Layer Bools
|
# Dynamically Create Task Layer Bools
|
||||||
self._asset_pipe = context.scene.asset_pipeline
|
self._asset_pipe = context.scene.asset_pipeline
|
||||||
|
|
||||||
config.verify_json_data(Path(self._asset_pipe.task_layer_config_type))
|
config.verify_task_layer_json_data(self._asset_pipe.task_layer_config_type)
|
||||||
|
|
||||||
all_task_layers = self._asset_pipe.all_task_layers
|
all_task_layers = self._asset_pipe.all_task_layers
|
||||||
all_task_layers.clear()
|
all_task_layers.clear()
|
||||||
@ -434,7 +435,7 @@ class ASSETPIPE_OT_publish_new_version(bpy.types.Operator):
|
|||||||
f"Only '{constants.REVIEW_PUBLISH_KEY}' Publish is supported when a version is staged",
|
f"Only '{constants.REVIEW_PUBLISH_KEY}' Publish is supported when a version is staged",
|
||||||
)
|
)
|
||||||
return {'CANCELLED'}
|
return {'CANCELLED'}
|
||||||
catalog_id = context.scene.asset_pipeline.asset_catalog_id
|
catalog_id = get_asset_id(context.scene.asset_pipeline.asset_catalog_name)
|
||||||
create_next_published_file(
|
create_next_published_file(
|
||||||
current_file=Path(bpy.data.filepath),
|
current_file=Path(bpy.data.filepath),
|
||||||
publish_type=self.publish_types,
|
publish_type=self.publish_types,
|
||||||
@ -476,7 +477,7 @@ class ASSETPIPE_OT_publish_staged_as_active(bpy.types.Operator):
|
|||||||
staged_file = find_latest_publish(current_file, publish_type=constants.STAGED_PUBLISH_KEY)
|
staged_file = find_latest_publish(current_file, publish_type=constants.STAGED_PUBLISH_KEY)
|
||||||
# Delete Staged File
|
# Delete Staged File
|
||||||
staged_file.unlink()
|
staged_file.unlink()
|
||||||
catalog_id = context.scene.asset_pipeline.asset_catalog_id
|
catalog_id = get_asset_id(context.scene.asset_pipeline.asset_catalog_name)
|
||||||
create_next_published_file(current_file=current_file, catalog_id=catalog_id)
|
create_next_published_file(current_file=current_file, catalog_id=catalog_id)
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
@ -960,7 +961,8 @@ class ASSETPIPE_OT_refresh_asset_cat(bpy.types.Operator):
|
|||||||
bl_description = """Refresh Asset Catalogs"""
|
bl_description = """Refresh Asset Catalogs"""
|
||||||
|
|
||||||
def execute(self, context: bpy.types.Context):
|
def execute(self, context: bpy.types.Context):
|
||||||
get_asset_cat_enum_items()
|
get_asset_catalog_items(reload=True)
|
||||||
|
verify_task_layer_json_data()
|
||||||
self.report({'INFO'}, "Asset Catalogs Refreshed!")
|
self.report({'INFO'}, "Asset Catalogs Refreshed!")
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
@ -2,21 +2,30 @@ import bpy
|
|||||||
import os
|
import os
|
||||||
from typing import List
|
from typing import List
|
||||||
from . import constants
|
from . import constants
|
||||||
from .config import get_task_layer_presets_path
|
from . import config
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from .prefs import get_addon_prefs
|
from .prefs import get_addon_prefs
|
||||||
from .asset_catalog import get_asset_cat_enum_items
|
from .asset_catalog import get_asset_catalog_items, get_asset_name, get_asset_id
|
||||||
|
import json
|
||||||
|
|
||||||
""" NOTE Items in these properties groups should be generated by a function that finds the
|
""" NOTE Items in these properties groups should be generated by a function that finds the
|
||||||
avaliable task layers from the task_layer.json file that needs to be created.
|
avaliable task layers from the task_layer.json file that needs to be created.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def get_safely_string_prop(self, name: str) -> str:
|
||||||
|
"""Return Value of String Property, and return "" if value isn't set"""
|
||||||
|
try:
|
||||||
|
return self[name]
|
||||||
|
except KeyError:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
def get_task_layer_presets(self, context):
|
def get_task_layer_presets(self, context):
|
||||||
prefs = get_addon_prefs()
|
prefs = get_addon_prefs()
|
||||||
user_tls = Path(prefs.custom_task_layers_dir)
|
user_tls = Path(prefs.custom_task_layers_dir)
|
||||||
|
|
||||||
presets_dir = get_task_layer_presets_path()
|
presets_dir = config.get_task_layer_presets_path()
|
||||||
items = []
|
items = []
|
||||||
|
|
||||||
for file in presets_dir.glob('*.json'):
|
for file in presets_dir.glob('*.json'):
|
||||||
@ -126,7 +135,7 @@ class AssetPipeline(bpy.types.PropertyGroup):
|
|||||||
task_layer_config_type: bpy.props.EnumProperty(
|
task_layer_config_type: bpy.props.EnumProperty(
|
||||||
name="Task Layer Preset",
|
name="Task Layer Preset",
|
||||||
items=get_task_layer_presets,
|
items=get_task_layer_presets,
|
||||||
)
|
) # type: ignore
|
||||||
|
|
||||||
temp_file: bpy.props.StringProperty(name="Pre-Sync Backup")
|
temp_file: bpy.props.StringProperty(name="Pre-Sync Backup")
|
||||||
source_file: bpy.props.StringProperty(name="File that started Sync")
|
source_file: bpy.props.StringProperty(name="File that started Sync")
|
||||||
@ -157,15 +166,31 @@ class AssetPipeline(bpy.types.PropertyGroup):
|
|||||||
attribute_ui_bool: bpy.props.BoolProperty(name="Show/Hide Attributes", default=False)
|
attribute_ui_bool: bpy.props.BoolProperty(name="Show/Hide Attributes", default=False)
|
||||||
file_parent_ui_bool: bpy.props.BoolProperty(name="Show/Hide Parent", default=False)
|
file_parent_ui_bool: bpy.props.BoolProperty(name="Show/Hide Parent", default=False)
|
||||||
|
|
||||||
def get_asset_catalogs(self, context):
|
def set_asset_catalog_name(self, input):
|
||||||
return get_asset_cat_enum_items()
|
task_layer_dict = config.get_task_layer_dict()
|
||||||
|
task_layer_dict["ASSET_CATALOG_ID"] = get_asset_id(input)
|
||||||
|
config.update_task_layer_json_data(task_layer_dict)
|
||||||
|
self['asset_catalog_name'] = input
|
||||||
|
|
||||||
asset_catalog_id: bpy.props.EnumProperty(
|
def get_asset_catalog_name(self):
|
||||||
|
if config.ASSET_CATALOG_ID != "":
|
||||||
|
asset_name = get_asset_name(config.ASSET_CATALOG_ID)
|
||||||
|
if asset_name is None:
|
||||||
|
return ""
|
||||||
|
return asset_name
|
||||||
|
return get_safely_string_prop(self, 'asset_catalog_name')
|
||||||
|
|
||||||
|
def get_asset_catalogs_search(self, context, edit_text: str):
|
||||||
|
return get_asset_catalog_items()
|
||||||
|
|
||||||
|
asset_catalog_name: bpy.props.StringProperty(
|
||||||
name="Catalog",
|
name="Catalog",
|
||||||
items=get_asset_catalogs,
|
get=get_asset_catalog_name,
|
||||||
|
set=set_asset_catalog_name,
|
||||||
|
search=get_asset_catalogs_search,
|
||||||
|
search_options={'SORT'},
|
||||||
description="Select Asset Library Catalog for the current Asset, this value will be updated each time you Push to an 'Active' Publish",
|
description="Select Asset Library Catalog for the current Asset, this value will be updated each time you Push to an 'Active' Publish",
|
||||||
)
|
) # type: ignore
|
||||||
|
|
||||||
|
|
||||||
@bpy.app.handlers.persistent
|
@bpy.app.handlers.persistent
|
||||||
def set_asset_collection_name_post_file_load(_):
|
def set_asset_collection_name_post_file_load(_):
|
||||||
@ -179,6 +204,12 @@ def set_asset_collection_name_post_file_load(_):
|
|||||||
del scene.asset_pipeline['asset_collection']
|
del scene.asset_pipeline['asset_collection']
|
||||||
|
|
||||||
|
|
||||||
|
@bpy.app.handlers.persistent
|
||||||
|
def refresh_asset_catalog(_):
|
||||||
|
get_asset_catalog_items()
|
||||||
|
config.verify_task_layer_json_data()
|
||||||
|
|
||||||
|
|
||||||
classes = (
|
classes = (
|
||||||
AssetTransferData,
|
AssetTransferData,
|
||||||
AssetTransferDataTemp,
|
AssetTransferDataTemp,
|
||||||
@ -197,6 +228,7 @@ def register():
|
|||||||
name="Surrender Ownership", default=False
|
name="Surrender Ownership", default=False
|
||||||
)
|
)
|
||||||
bpy.app.handlers.load_post.append(set_asset_collection_name_post_file_load)
|
bpy.app.handlers.load_post.append(set_asset_collection_name_post_file_load)
|
||||||
|
bpy.app.handlers.load_post.append(refresh_asset_catalog)
|
||||||
|
|
||||||
|
|
||||||
def unregister():
|
def unregister():
|
||||||
@ -206,3 +238,4 @@ def unregister():
|
|||||||
del bpy.types.Scene.asset_pipeline
|
del bpy.types.Scene.asset_pipeline
|
||||||
del bpy.types.ID.asset_id_owner
|
del bpy.types.ID.asset_id_owner
|
||||||
bpy.app.handlers.load_post.remove(set_asset_collection_name_post_file_load)
|
bpy.app.handlers.load_post.remove(set_asset_collection_name_post_file_load)
|
||||||
|
bpy.app.handlers.load_post.remove(refresh_asset_catalog)
|
||||||
|
@ -16,7 +16,7 @@ from .merge.shared_ids import get_shared_id_icon
|
|||||||
from . import constants, config
|
from . import constants, config
|
||||||
from .hooks import Hooks
|
from .hooks import Hooks
|
||||||
from .merge.task_layer import draw_task_layer_selection
|
from .merge.task_layer import draw_task_layer_selection
|
||||||
|
from .asset_catalog import get_asset_id
|
||||||
|
|
||||||
def sync_poll(cls, context):
|
def sync_poll(cls, context):
|
||||||
if any([img.is_dirty for img in bpy.data.images]):
|
if any([img.is_dirty for img in bpy.data.images]):
|
||||||
@ -155,7 +155,7 @@ def sync_execute_push(self, context):
|
|||||||
hooks_instance = Hooks()
|
hooks_instance = Hooks()
|
||||||
hooks_instance.load_hooks(context)
|
hooks_instance.load_hooks(context)
|
||||||
temp_file_path = create_temp_file_backup(self, context)
|
temp_file_path = create_temp_file_backup(self, context)
|
||||||
_catalog_id = context.scene.asset_pipeline.asset_catalog_id
|
_catalog_id = get_asset_id(context.scene.asset_pipeline.asset_catalog_name)
|
||||||
|
|
||||||
file_path = self._sync_target.__str__()
|
file_path = self._sync_target.__str__()
|
||||||
bpy.ops.wm.open_mainfile(filepath=file_path)
|
bpy.ops.wm.open_mainfile(filepath=file_path)
|
||||||
|
@ -3,7 +3,7 @@ import bpy
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from .merge.transfer_data.transfer_ui import draw_transfer_data
|
from .merge.transfer_data.transfer_ui import draw_transfer_data
|
||||||
from .merge.task_layer import draw_task_layer_selection
|
from .merge.task_layer import draw_task_layer_selection
|
||||||
from .config import verify_json_data
|
from .config import verify_task_layer_json_data
|
||||||
from .prefs import get_addon_prefs
|
from .prefs import get_addon_prefs
|
||||||
from . import constants
|
from . import constants
|
||||||
from .merge.publish import is_staged_publish
|
from .merge.publish import is_staged_publish
|
||||||
@ -45,7 +45,7 @@ class ASSETPIPE_PT_sync(bpy.types.Panel):
|
|||||||
return
|
return
|
||||||
|
|
||||||
# TODO Move this call out of the UI because we keep re-loading this file every draw
|
# TODO Move this call out of the UI because we keep re-loading this file every draw
|
||||||
if not verify_json_data():
|
if not verify_task_layer_json_data():
|
||||||
layout.label(text="Task Layer Config is invalid", icon="ERROR")
|
layout.label(text="Task Layer Config is invalid", icon="ERROR")
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -94,7 +94,7 @@ class ASSETPIPE_PT_sync_tools(bpy.types.Panel):
|
|||||||
def draw(self, context: bpy.types.Context) -> None:
|
def draw(self, context: bpy.types.Context) -> None:
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
cat_row = layout.row(align=True)
|
cat_row = layout.row(align=True)
|
||||||
cat_row.prop(context.scene.asset_pipeline, 'asset_catalog_id')
|
cat_row.prop(context.scene.asset_pipeline, 'asset_catalog_name')
|
||||||
cat_row.operator("assetpipe.refresh_asset_cat", icon='FILE_REFRESH', text="")
|
cat_row.operator("assetpipe.refresh_asset_cat", icon='FILE_REFRESH', text="")
|
||||||
layout.operator("assetpipe.batch_ownership_change")
|
layout.operator("assetpipe.batch_ownership_change")
|
||||||
layout.operator("assetpipe.revert_file", icon="FILE_TICK")
|
layout.operator("assetpipe.revert_file", icon="FILE_TICK")
|
||||||
|
Loading…
Reference in New Issue
Block a user