mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-25 13:24:54 +01:00
changed how files widget works with extensions and handle file items
This commit is contained in:
parent
0b7cdeee84
commit
ed98bbcd32
3 changed files with 141 additions and 158 deletions
|
|
@ -42,6 +42,7 @@ from .attribute_definitions import (
|
|||
EnumDef,
|
||||
BoolDef,
|
||||
FileDef,
|
||||
FileDefItem,
|
||||
)
|
||||
|
||||
from .env_tools import (
|
||||
|
|
@ -266,6 +267,7 @@ __all__ = [
|
|||
"EnumDef",
|
||||
"BoolDef",
|
||||
"FileDef",
|
||||
"FileDefItem",
|
||||
|
||||
"import_filepath",
|
||||
"modules_from_path",
|
||||
|
|
|
|||
|
|
@ -334,6 +334,61 @@ class FileDefItem(object):
|
|||
os.path.join(self.directory, filename)
|
||||
)
|
||||
|
||||
@property
|
||||
def label(self):
|
||||
if not self.is_sequence:
|
||||
return self.filenames[0]
|
||||
|
||||
frame_start = self.frames[0]
|
||||
filename_template = os.path.basename(self.template)
|
||||
if len(self.frames) == 1:
|
||||
return "{} [{}]".format(filename_template, frame_start)
|
||||
|
||||
frame_end = self.frames[-1]
|
||||
expected_len = (frame_end - frame_start) + 1
|
||||
if expected_len == len(self.frames):
|
||||
return "{} [{}-{}]".format(
|
||||
filename_template, frame_start, frame_end
|
||||
)
|
||||
|
||||
ranges = []
|
||||
_frame_start = None
|
||||
_frame_end = None
|
||||
for frame in range(frame_start, frame_end + 1):
|
||||
if frame not in self.frames:
|
||||
add_to_ranges = _frame_start is not None
|
||||
elif _frame_start is None:
|
||||
_frame_start = _frame_end = frame
|
||||
add_to_ranges = frame == frame_end
|
||||
else:
|
||||
_frame_end = frame
|
||||
add_to_ranges = frame == frame_end
|
||||
|
||||
if add_to_ranges:
|
||||
if _frame_start != _frame_end:
|
||||
_range = "{}-{}".format(_frame_start, _frame_end)
|
||||
else:
|
||||
_range = str(_frame_start)
|
||||
ranges.append(_range)
|
||||
_frame_start = _frame_end = None
|
||||
return "{} [{}]".format(
|
||||
filename_template, ",".join(ranges)
|
||||
)
|
||||
|
||||
@property
|
||||
def ext(self):
|
||||
_, ext = os.path.splitext(self.filenames[0])
|
||||
if ext:
|
||||
return ext
|
||||
return None
|
||||
|
||||
@property
|
||||
def is_dir(self):
|
||||
# QUESTION a better way how to define folder (in init argument?)
|
||||
if self.ext:
|
||||
return False
|
||||
return True
|
||||
|
||||
def set_directory(self, directory):
|
||||
self.directory = directory
|
||||
|
||||
|
|
@ -357,23 +412,30 @@ class FileDefItem(object):
|
|||
return cls("", "")
|
||||
|
||||
@classmethod
|
||||
def from_value(cls, value):
|
||||
def from_value(cls, value, sequence_extensions):
|
||||
multi = isinstance(value, (list, tuple, set))
|
||||
if not multi:
|
||||
value = [value]
|
||||
|
||||
output = []
|
||||
str_filepaths = []
|
||||
for item in value:
|
||||
if isinstance(item, dict):
|
||||
if isinstance(item, FileDefItem):
|
||||
output.append(item)
|
||||
elif isinstance(item, dict):
|
||||
output.append(cls.from_dict(item))
|
||||
elif isinstance(item, six.string_types):
|
||||
output.extend(cls.from_paths([item]))
|
||||
str_filepaths.append(item)
|
||||
else:
|
||||
raise TypeError(
|
||||
"Unknown type \"{}\". Can't convert to {}".format(
|
||||
str(type(item)), cls.__name__
|
||||
)
|
||||
)
|
||||
|
||||
if str_filepaths:
|
||||
output.extend(cls.from_paths(str_filepaths, sequence_extensions))
|
||||
|
||||
if multi:
|
||||
return output
|
||||
return output[0]
|
||||
|
|
@ -388,7 +450,7 @@ class FileDefItem(object):
|
|||
)
|
||||
|
||||
@classmethod
|
||||
def from_paths(cls, paths):
|
||||
def from_paths(cls, paths, sequence_extensions):
|
||||
filenames_by_dir = collections.defaultdict(list)
|
||||
for path in paths:
|
||||
normalized = os.path.normpath(path)
|
||||
|
|
@ -397,7 +459,18 @@ class FileDefItem(object):
|
|||
|
||||
output = []
|
||||
for directory, filenames in filenames_by_dir.items():
|
||||
cols, remainders = clique.assemble(filenames)
|
||||
filtered_filenames = []
|
||||
for filename in filenames:
|
||||
_, ext = os.path.splitext(filename)
|
||||
if ext in sequence_extensions:
|
||||
filtered_filenames.append(filename)
|
||||
else:
|
||||
output.append(cls(directory, [filename]))
|
||||
|
||||
if not filtered_filenames:
|
||||
continue
|
||||
|
||||
cols, remainders = clique.assemble(filtered_filenames)
|
||||
for remainder in remainders:
|
||||
output.append(cls(directory, [remainder]))
|
||||
|
||||
|
|
|
|||
|
|
@ -2,8 +2,10 @@ import os
|
|||
import collections
|
||||
import uuid
|
||||
import clique
|
||||
import six
|
||||
from Qt import QtWidgets, QtCore, QtGui
|
||||
|
||||
from openpype.lib import FileDefItem
|
||||
from openpype.tools.utils import paint_image_with_color
|
||||
# TODO change imports
|
||||
from openpype.tools.resources import (
|
||||
|
|
@ -75,174 +77,76 @@ class DropEmpty(QtWidgets.QWidget):
|
|||
class FilesModel(QtGui.QStandardItemModel):
|
||||
def __init__(self, allow_multiple_items, sequence_exts):
|
||||
super(FilesModel, self).__init__()
|
||||
|
||||
self._allow_multiple_items = allow_multiple_items
|
||||
self._sequence_exts = sequence_exts
|
||||
|
||||
self._items_by_id = {}
|
||||
self._file_items_by_id = {}
|
||||
self._filenames_by_dirpath = collections.defaultdict(set)
|
||||
self._items_by_dirpath = collections.defaultdict(list)
|
||||
|
||||
self._allow_multiple_items = allow_multiple_items
|
||||
self.sequence_exts = sequence_exts
|
||||
def add_filepaths(self, items):
|
||||
if not items:
|
||||
return
|
||||
|
||||
def add_filepaths(self, filepaths):
|
||||
if not filepaths:
|
||||
obj_items = FileDefItem.from_value(items, self._sequence_exts)
|
||||
if not obj_items:
|
||||
return
|
||||
|
||||
if not self._allow_multiple_items:
|
||||
filepaths = [filepaths[0]]
|
||||
item_ids = []
|
||||
for items in self._items_by_dirpath.values():
|
||||
for item in items:
|
||||
item_id = item.data(ITEM_ID_ROLE)
|
||||
if item_id:
|
||||
item_ids.append(item_id)
|
||||
obj_items = [obj_items[0]]
|
||||
current_ids = list(self._file_items_by_id.keys())
|
||||
if current_ids:
|
||||
self.remove_item_by_ids(current_ids)
|
||||
|
||||
if item_ids:
|
||||
self.remove_item_by_ids(item_ids)
|
||||
new_model_items = []
|
||||
for obj_item in obj_items:
|
||||
_, ext = os.path.splitext(obj_item.filenames[0])
|
||||
if ext:
|
||||
icon_pixmap = get_pixmap(filename="file.png")
|
||||
else:
|
||||
icon_pixmap = get_pixmap(filename="folder.png")
|
||||
|
||||
new_dirpaths = set()
|
||||
for filepath in filepaths:
|
||||
filename = os.path.basename(filepath)
|
||||
dirpath = os.path.dirname(filepath)
|
||||
filenames = self._filenames_by_dirpath[dirpath]
|
||||
if filename not in filenames:
|
||||
new_dirpaths.add(dirpath)
|
||||
filenames.add(filename)
|
||||
self._refresh_items(new_dirpaths)
|
||||
item_id, model_item = self._create_item(obj_item, icon_pixmap)
|
||||
new_model_items.append(model_item)
|
||||
self._file_items_by_id[item_id] = obj_item
|
||||
self._items_by_id[item_id] = model_item
|
||||
|
||||
if new_model_items:
|
||||
self.invisibleRootItem().appendRows(new_model_items)
|
||||
|
||||
def remove_item_by_ids(self, item_ids):
|
||||
if not item_ids:
|
||||
return
|
||||
|
||||
remaining_ids = set(item_ids)
|
||||
result = collections.defaultdict(list)
|
||||
for dirpath, items in self._items_by_dirpath.items():
|
||||
if not remaining_ids:
|
||||
break
|
||||
items = []
|
||||
for item_id in set(item_ids):
|
||||
if item_id not in self._items_by_id:
|
||||
continue
|
||||
item = self._items_by_id.pop(item_id)
|
||||
self._file_items_by_id.pop(item_id)
|
||||
items.append(item)
|
||||
|
||||
if items:
|
||||
for item in items:
|
||||
if not remaining_ids:
|
||||
break
|
||||
item_id = item.data(ITEM_ID_ROLE)
|
||||
if item_id in remaining_ids:
|
||||
remaining_ids.remove(item_id)
|
||||
result[dirpath].append(item)
|
||||
|
||||
if not result:
|
||||
return
|
||||
|
||||
dirpaths = set(result.keys())
|
||||
for dirpath, items in result.items():
|
||||
filenames_cache = self._filenames_by_dirpath[dirpath]
|
||||
for item in items:
|
||||
filenames = item.data(FILENAMES_ROLE)
|
||||
|
||||
self._items_by_dirpath[dirpath].remove(item)
|
||||
self.removeRows(item.row(), 1)
|
||||
for filename in filenames:
|
||||
if filename in filenames_cache:
|
||||
filenames_cache.remove(filename)
|
||||
|
||||
self._refresh_items(dirpaths)
|
||||
|
||||
def _refresh_items(self, dirpaths=None):
|
||||
if dirpaths is None:
|
||||
dirpaths = set(self._items_by_dirpath.keys())
|
||||
|
||||
new_items = []
|
||||
for dirpath in dirpaths:
|
||||
items_to_remove = list(self._items_by_dirpath[dirpath])
|
||||
cols, remainders = clique.assemble(
|
||||
self._filenames_by_dirpath[dirpath]
|
||||
)
|
||||
filtered_cols = []
|
||||
for collection in cols:
|
||||
filenames = set(collection)
|
||||
valid_col = True
|
||||
for filename in filenames:
|
||||
ext = os.path.splitext(filename)[-1]
|
||||
valid_col = ext in self.sequence_exts
|
||||
break
|
||||
|
||||
if valid_col:
|
||||
filtered_cols.append(collection)
|
||||
else:
|
||||
for filename in filenames:
|
||||
remainders.append(filename)
|
||||
|
||||
for filename in remainders:
|
||||
found = False
|
||||
for item in items_to_remove:
|
||||
item_filenames = item.data(FILENAMES_ROLE)
|
||||
if filename in item_filenames and len(item_filenames) == 1:
|
||||
found = True
|
||||
items_to_remove.remove(item)
|
||||
break
|
||||
|
||||
if found:
|
||||
continue
|
||||
|
||||
fullpath = os.path.join(dirpath, filename)
|
||||
if os.path.isdir(fullpath):
|
||||
icon_pixmap = get_pixmap(filename="folder.png")
|
||||
else:
|
||||
icon_pixmap = get_pixmap(filename="file.png")
|
||||
label = filename
|
||||
filenames = [filename]
|
||||
item = self._create_item(
|
||||
label, filenames, dirpath, icon_pixmap
|
||||
)
|
||||
new_items.append(item)
|
||||
self._items_by_dirpath[dirpath].append(item)
|
||||
|
||||
for collection in filtered_cols:
|
||||
filenames = set(collection)
|
||||
found = False
|
||||
for item in items_to_remove:
|
||||
item_filenames = item.data(FILENAMES_ROLE)
|
||||
if item_filenames == filenames:
|
||||
found = True
|
||||
items_to_remove.remove(item)
|
||||
break
|
||||
|
||||
if found:
|
||||
continue
|
||||
|
||||
col_range = collection.format("{ranges}")
|
||||
label = "{}<{}>{}".format(
|
||||
collection.head, col_range, collection.tail
|
||||
)
|
||||
icon_pixmap = get_pixmap(filename="files.png")
|
||||
item = self._create_item(
|
||||
label, filenames, dirpath, icon_pixmap
|
||||
)
|
||||
new_items.append(item)
|
||||
self._items_by_dirpath[dirpath].append(item)
|
||||
|
||||
for item in items_to_remove:
|
||||
self._items_by_dirpath[dirpath].remove(item)
|
||||
self.removeRows(item.row(), 1)
|
||||
|
||||
if new_items:
|
||||
self.invisibleRootItem().appendRows(new_items)
|
||||
|
||||
def _create_item(self, label, filenames, dirpath, icon_pixmap=None):
|
||||
first_filename = None
|
||||
for filename in filenames:
|
||||
first_filename = filename
|
||||
break
|
||||
ext = os.path.splitext(first_filename)[-1]
|
||||
is_dir = False
|
||||
if len(filenames) == 1:
|
||||
filepath = os.path.join(dirpath, first_filename)
|
||||
is_dir = os.path.isdir(filepath)
|
||||
def get_file_item_by_id(self, item_id):
|
||||
return self._file_items_by_id.get(item_id)
|
||||
|
||||
def _create_item(self, file_item, icon_pixmap=None):
|
||||
item = QtGui.QStandardItem()
|
||||
item.setData(str(uuid.uuid4()), ITEM_ID_ROLE)
|
||||
item.setData(label, ITEM_LABEL_ROLE)
|
||||
item.setData(filenames, FILENAMES_ROLE)
|
||||
item.setData(dirpath, DIRPATH_ROLE)
|
||||
item_id = str(uuid.uuid4())
|
||||
item.setData(item_id, ITEM_ID_ROLE)
|
||||
item.setData(file_item.label, ITEM_LABEL_ROLE)
|
||||
item.setData(file_item.filenames, FILENAMES_ROLE)
|
||||
item.setData(file_item.directory, DIRPATH_ROLE)
|
||||
item.setData(icon_pixmap, ITEM_ICON_ROLE)
|
||||
item.setData(ext, EXT_ROLE)
|
||||
item.setData(is_dir, IS_DIR_ROLE)
|
||||
item.setData(file_item.ext, EXT_ROLE)
|
||||
item.setData(file_item.is_dir, IS_DIR_ROLE)
|
||||
|
||||
return item
|
||||
return item_id, item
|
||||
|
||||
|
||||
class FilesProxyModel(QtCore.QSortFilterProxyModel):
|
||||
|
|
@ -344,6 +248,7 @@ class ItemWidget(QtWidgets.QWidget):
|
|||
|
||||
class FilesView(QtWidgets.QListView):
|
||||
"""View showing instances and their groups."""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(FilesView, self).__init__(*args, **kwargs)
|
||||
|
||||
|
|
@ -439,14 +344,17 @@ class FilesWidget(QtWidgets.QFrame):
|
|||
|
||||
def current_value(self):
|
||||
model = self._files_proxy_model
|
||||
filepaths = set()
|
||||
item_ids = set()
|
||||
for row in range(model.rowCount()):
|
||||
index = model.index(row, 0)
|
||||
dirpath = index.data(DIRPATH_ROLE)
|
||||
filenames = index.data(FILENAMES_ROLE)
|
||||
for filename in filenames:
|
||||
filepaths.add(os.path.join(dirpath, filename))
|
||||
return list(filepaths)
|
||||
item_ids.add(index.data(ITEM_ID_ROLE))
|
||||
|
||||
file_items = []
|
||||
for item_id in item_ids:
|
||||
file_item = self._files_model.get_file_item_by_id(item_id)
|
||||
if file_item is not None:
|
||||
file_items.append(file_item.to_dict())
|
||||
return file_items
|
||||
|
||||
def set_filters(self, folders_allowed, exts_filter):
|
||||
self._files_proxy_model.set_allow_folders(folders_allowed)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue