mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-26 13:52:15 +01:00
updated asset creator to be able create and load in both silo and non silo projects
This commit is contained in:
parent
73de6b85d9
commit
ce1d2e1e6e
3 changed files with 238 additions and 168 deletions
|
|
@ -50,13 +50,13 @@ class Window(QtWidgets.QDialog):
|
|||
input_outlink.setStyleSheet("background-color: #333333;")
|
||||
checkbox_outlink = QtWidgets.QCheckBox("Use outlink")
|
||||
# Parent
|
||||
label_parent = QtWidgets.QLabel("Parent:")
|
||||
label_parent = QtWidgets.QLabel("*Parent:")
|
||||
input_parent = QtWidgets.QLineEdit()
|
||||
input_parent.setReadOnly(True)
|
||||
input_parent.setStyleSheet("background-color: #333333;")
|
||||
|
||||
# Name
|
||||
label_name = QtWidgets.QLabel("Name:")
|
||||
label_name = QtWidgets.QLabel("*Name:")
|
||||
input_name = QtWidgets.QLineEdit()
|
||||
input_name.setPlaceholderText("<asset name>")
|
||||
|
||||
|
|
@ -103,7 +103,7 @@ class Window(QtWidgets.QDialog):
|
|||
|
||||
task_view = QtWidgets.QTreeView()
|
||||
task_view.setIndentation(0)
|
||||
task_model = model.TasksTemplateModel()
|
||||
task_model = model.TasksModel()
|
||||
task_view.setModel(task_model)
|
||||
|
||||
info_layout.addWidget(inputs_widget)
|
||||
|
|
@ -162,6 +162,7 @@ class Window(QtWidgets.QDialog):
|
|||
# signals
|
||||
btn_create_asset.clicked.connect(self.create_asset)
|
||||
assets.selection_changed.connect(self.on_asset_changed)
|
||||
input_name.textChanged.connect(self.on_asset_name_change)
|
||||
checkbox_outlink.toggled.connect(self.on_outlink_checkbox_change)
|
||||
combo_task_template.currentTextChanged.connect(
|
||||
self.on_task_template_changed
|
||||
|
|
@ -198,6 +199,8 @@ class Window(QtWidgets.QDialog):
|
|||
schemas_items = config.get_presets().get('ftrack', {}).get(
|
||||
'project_schemas', {}
|
||||
)
|
||||
# Get info if it is silo project
|
||||
self.silos = io.distinct("silo")
|
||||
|
||||
key = "default"
|
||||
if schema_name in schemas_items:
|
||||
|
|
@ -374,9 +377,6 @@ class Window(QtWidgets.QDialog):
|
|||
session.create('Task', task_data)
|
||||
|
||||
av_project = io.find_one({'type': 'project'})
|
||||
silo = parent['silo']
|
||||
if silo is None:
|
||||
silo = parent['name']
|
||||
|
||||
hiearchy_items = []
|
||||
hiearchy_items.extend(self.get_avalon_parent(parent))
|
||||
|
|
@ -395,10 +395,14 @@ class Window(QtWidgets.QDialog):
|
|||
'parent': av_project['_id'],
|
||||
'name': name,
|
||||
'schema': "avalon-core:asset-3.0",
|
||||
'silo': silo,
|
||||
'type': 'asset',
|
||||
'data': new_asset_data
|
||||
}
|
||||
|
||||
# Backwards compatibility (add silo from parent if is silo project)
|
||||
if self.silos:
|
||||
new_asset_info["silo"] = parent["silo"]
|
||||
|
||||
try:
|
||||
schema.validate(new_asset_info)
|
||||
except Exception:
|
||||
|
|
@ -576,17 +580,35 @@ class Window(QtWidgets.QDialog):
|
|||
assets_model = self.data["model"]["assets"]
|
||||
parent_input = self.data['inputs']['parent']
|
||||
selected = assets_model.get_selected_assets()
|
||||
|
||||
self.valid_parent = False
|
||||
if len(selected) > 1:
|
||||
self.valid_parent = False
|
||||
parent_input.setText('< Please select only one asset! >')
|
||||
elif len(selected) == 1:
|
||||
self.valid_parent = True
|
||||
asset = io.find_one({"_id": selected[0], "type": "asset"})
|
||||
parent_input.setText(asset['name'])
|
||||
if isinstance(selected[0], io.ObjectId):
|
||||
self.valid_parent = True
|
||||
asset = io.find_one({"_id": selected[0], "type": "asset"})
|
||||
parent_input.setText(asset['name'])
|
||||
else:
|
||||
parent_input.setText('< Selected invalid parent(silo) >')
|
||||
else:
|
||||
self.valid_parent = False
|
||||
parent_input.setText('< Nothing is selected >')
|
||||
|
||||
self.creatability_check()
|
||||
|
||||
def on_asset_name_change(self):
|
||||
self.creatability_check()
|
||||
|
||||
def creatability_check(self):
|
||||
name_input = self.data['inputs']['name']
|
||||
name = str(name_input.text()).strip()
|
||||
creatable = False
|
||||
if name and self.valid_parent:
|
||||
creatable = True
|
||||
|
||||
self.data["buttons"]["create_asset"].setEnabled(creatable)
|
||||
|
||||
|
||||
|
||||
def show(parent=None, debug=False, context=None):
|
||||
"""Display Loader GUI
|
||||
|
|
|
|||
|
|
@ -3,26 +3,26 @@ import logging
|
|||
import collections
|
||||
|
||||
from avalon.vendor.Qt import QtCore, QtWidgets
|
||||
from avalon.vendor import qtawesome as awesome
|
||||
from avalon.vendor import qtawesome
|
||||
from avalon import io
|
||||
from avalon import style
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Node(dict):
|
||||
"""A node that can be represented in a tree view.
|
||||
class Item(dict):
|
||||
"""An item that can be represented in a tree view using `TreeModel`.
|
||||
|
||||
The node can store data just like a dictionary.
|
||||
The item can store data just like a regular dictionary.
|
||||
|
||||
>>> data = {"name": "John", "score": 10}
|
||||
>>> node = Node(data)
|
||||
>>> assert node["name"] == "John"
|
||||
>>> item = Item(data)
|
||||
>>> assert item["name"] == "John"
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, data=None):
|
||||
super(Node, self).__init__()
|
||||
super(Item, self).__init__()
|
||||
|
||||
self._children = list()
|
||||
self._parent = None
|
||||
|
|
@ -51,36 +51,36 @@ class Node(dict):
|
|||
def row(self):
|
||||
"""
|
||||
Returns:
|
||||
int: Index of this node under parent"""
|
||||
int: Index of this item under parent"""
|
||||
if self._parent is not None:
|
||||
siblings = self.parent().children()
|
||||
return siblings.index(self)
|
||||
|
||||
def add_child(self, child):
|
||||
"""Add a child to this node"""
|
||||
"""Add a child to this item"""
|
||||
child._parent = self
|
||||
self._children.append(child)
|
||||
|
||||
|
||||
class TreeModel(QtCore.QAbstractItemModel):
|
||||
|
||||
COLUMNS = list()
|
||||
NodeRole = QtCore.Qt.UserRole + 1
|
||||
Columns = list()
|
||||
ItemRole = QtCore.Qt.UserRole + 1
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super(TreeModel, self).__init__(parent)
|
||||
self._root_node = Node()
|
||||
self._root_item = Item()
|
||||
|
||||
def rowCount(self, parent):
|
||||
if parent.isValid():
|
||||
node = parent.internalPointer()
|
||||
item = parent.internalPointer()
|
||||
else:
|
||||
node = self._root_node
|
||||
item = self._root_item
|
||||
|
||||
return node.childCount()
|
||||
return item.childCount()
|
||||
|
||||
def columnCount(self, parent):
|
||||
return len(self.COLUMNS)
|
||||
return len(self.Columns)
|
||||
|
||||
def data(self, index, role):
|
||||
|
||||
|
|
@ -89,17 +89,17 @@ class TreeModel(QtCore.QAbstractItemModel):
|
|||
|
||||
if role == QtCore.Qt.DisplayRole or role == QtCore.Qt.EditRole:
|
||||
|
||||
node = index.internalPointer()
|
||||
item = index.internalPointer()
|
||||
column = index.column()
|
||||
|
||||
key = self.COLUMNS[column]
|
||||
return node.get(key, None)
|
||||
key = self.Columns[column]
|
||||
return item.get(key, None)
|
||||
|
||||
if role == self.NodeRole:
|
||||
if role == self.ItemRole:
|
||||
return index.internalPointer()
|
||||
|
||||
def setData(self, index, value, role=QtCore.Qt.EditRole):
|
||||
"""Change the data on the nodes.
|
||||
"""Change the data on the items.
|
||||
|
||||
Returns:
|
||||
bool: Whether the edit was successful
|
||||
|
|
@ -108,10 +108,10 @@ class TreeModel(QtCore.QAbstractItemModel):
|
|||
if index.isValid():
|
||||
if role == QtCore.Qt.EditRole:
|
||||
|
||||
node = index.internalPointer()
|
||||
item = index.internalPointer()
|
||||
column = index.column()
|
||||
key = self.COLUMNS[column]
|
||||
node[key] = value
|
||||
key = self.Columns[column]
|
||||
item[key] = value
|
||||
|
||||
# passing `list()` for PyQt5 (see PYSIDE-462)
|
||||
self.dataChanged.emit(index, index, list())
|
||||
|
|
@ -123,78 +123,96 @@ class TreeModel(QtCore.QAbstractItemModel):
|
|||
|
||||
def setColumns(self, keys):
|
||||
assert isinstance(keys, (list, tuple))
|
||||
self.COLUMNS = keys
|
||||
self.Columns = keys
|
||||
|
||||
def headerData(self, section, orientation, role):
|
||||
|
||||
if role == QtCore.Qt.DisplayRole:
|
||||
if section < len(self.COLUMNS):
|
||||
return self.COLUMNS[section]
|
||||
if section < len(self.Columns):
|
||||
return self.Columns[section]
|
||||
|
||||
super(TreeModel, self).headerData(section, orientation, role)
|
||||
|
||||
def flags(self, index):
|
||||
return (
|
||||
QtCore.Qt.ItemIsEnabled |
|
||||
QtCore.Qt.ItemIsSelectable
|
||||
)
|
||||
flags = QtCore.Qt.ItemIsEnabled
|
||||
|
||||
item = index.internalPointer()
|
||||
if item.get("enabled", True):
|
||||
flags |= QtCore.Qt.ItemIsSelectable
|
||||
|
||||
return flags
|
||||
|
||||
def parent(self, index):
|
||||
|
||||
node = index.internalPointer()
|
||||
parent_node = node.parent()
|
||||
item = index.internalPointer()
|
||||
parent_item = item.parent()
|
||||
|
||||
# If it has no parents we return invalid
|
||||
if parent_node == self._root_node or not parent_node:
|
||||
if parent_item == self._root_item or not parent_item:
|
||||
return QtCore.QModelIndex()
|
||||
|
||||
return self.createIndex(parent_node.row(), 0, parent_node)
|
||||
return self.createIndex(parent_item.row(), 0, parent_item)
|
||||
|
||||
def index(self, row, column, parent):
|
||||
"""Return index for row/column under parent"""
|
||||
|
||||
if not parent.isValid():
|
||||
parentNode = self._root_node
|
||||
parent_item = self._root_item
|
||||
else:
|
||||
parentNode = parent.internalPointer()
|
||||
parent_item = parent.internalPointer()
|
||||
|
||||
childItem = parentNode.child(row)
|
||||
if childItem:
|
||||
return self.createIndex(row, column, childItem)
|
||||
child_item = parent_item.child(row)
|
||||
if child_item:
|
||||
return self.createIndex(row, column, child_item)
|
||||
else:
|
||||
return QtCore.QModelIndex()
|
||||
|
||||
def add_child(self, node, parent=None):
|
||||
def add_child(self, item, parent=None):
|
||||
if parent is None:
|
||||
parent = self._root_node
|
||||
parent = self._root_item
|
||||
|
||||
parent.add_child(node)
|
||||
parent.add_child(item)
|
||||
|
||||
def column_name(self, column):
|
||||
"""Return column key by index"""
|
||||
|
||||
if column < len(self.COLUMNS):
|
||||
return self.COLUMNS[column]
|
||||
if column < len(self.Columns):
|
||||
return self.Columns[column]
|
||||
|
||||
def clear(self):
|
||||
self.beginResetModel()
|
||||
self._root_node = Node()
|
||||
self._root_item = Item()
|
||||
self.endResetModel()
|
||||
|
||||
|
||||
class TasksTemplateModel(TreeModel):
|
||||
class TasksModel(TreeModel):
|
||||
"""A model listing the tasks combined for a list of assets"""
|
||||
|
||||
COLUMNS = ["Tasks"]
|
||||
Columns = ["Tasks"]
|
||||
|
||||
def __init__(self):
|
||||
super(TasksTemplateModel, self).__init__()
|
||||
self.selectable = False
|
||||
super(TasksModel, self).__init__()
|
||||
self._num_assets = 0
|
||||
self._icons = {
|
||||
"__default__": awesome.icon("fa.folder-o",
|
||||
color=style.colors.default)
|
||||
"__default__": qtawesome.icon("fa.male",
|
||||
color=style.colors.default),
|
||||
"__no_task__": qtawesome.icon("fa.exclamation-circle",
|
||||
color=style.colors.mid)
|
||||
}
|
||||
|
||||
self._get_task_icons()
|
||||
|
||||
def _get_task_icons(self):
|
||||
# Get the project configured icons from database
|
||||
project = io.find_one({"type": "project"})
|
||||
tasks = project["config"].get("tasks", [])
|
||||
for task in tasks:
|
||||
icon_name = task.get("icon", None)
|
||||
if icon_name:
|
||||
icon = qtawesome.icon("fa.{}".format(icon_name),
|
||||
color=style.colors.default)
|
||||
self._icons[task["name"]] = icon
|
||||
|
||||
def set_tasks(self, tasks):
|
||||
"""Set assets to track by their database id
|
||||
|
||||
|
|
@ -213,23 +231,28 @@ class TasksTemplateModel(TreeModel):
|
|||
|
||||
icon = self._icons["__default__"]
|
||||
for task in tasks:
|
||||
node = Node({
|
||||
item = Item({
|
||||
"Tasks": task,
|
||||
"icon": icon
|
||||
})
|
||||
|
||||
self.add_child(node)
|
||||
self.add_child(item)
|
||||
|
||||
self.endResetModel()
|
||||
|
||||
def flags(self, index):
|
||||
if self.selectable is False:
|
||||
return QtCore.Qt.ItemIsEnabled
|
||||
else:
|
||||
return (
|
||||
QtCore.Qt.ItemIsEnabled |
|
||||
QtCore.Qt.ItemIsSelectable
|
||||
)
|
||||
return QtCore.Qt.ItemIsEnabled
|
||||
|
||||
def headerData(self, section, orientation, role):
|
||||
|
||||
# Override header for count column to show amount of assets
|
||||
# it is listing the tasks for
|
||||
if role == QtCore.Qt.DisplayRole:
|
||||
if orientation == QtCore.Qt.Horizontal:
|
||||
if section == 1: # count column
|
||||
return "count ({0})".format(self._num_assets)
|
||||
|
||||
return super(TasksModel, self).headerData(section, orientation, role)
|
||||
|
||||
def data(self, index, role):
|
||||
|
||||
|
|
@ -239,9 +262,9 @@ class TasksTemplateModel(TreeModel):
|
|||
# Add icon to the first column
|
||||
if role == QtCore.Qt.DecorationRole:
|
||||
if index.column() == 0:
|
||||
return index.internalPointer()['icon']
|
||||
return index.internalPointer()["icon"]
|
||||
|
||||
return super(TasksTemplateModel, self).data(index, role)
|
||||
return super(TasksModel, self).data(index, role)
|
||||
|
||||
|
||||
class DeselectableTreeView(QtWidgets.QTreeView):
|
||||
|
|
@ -259,33 +282,6 @@ class DeselectableTreeView(QtWidgets.QTreeView):
|
|||
QtWidgets.QTreeView.mousePressEvent(self, event)
|
||||
|
||||
|
||||
class ExactMatchesFilterProxyModel(QtCore.QSortFilterProxyModel):
|
||||
"""Filter model to where key column's value is in the filtered tags"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(ExactMatchesFilterProxyModel, self).__init__(*args, **kwargs)
|
||||
self._filters = set()
|
||||
|
||||
def setFilters(self, filters):
|
||||
self._filters = set(filters)
|
||||
|
||||
def filterAcceptsRow(self, source_row, source_parent):
|
||||
|
||||
# No filter
|
||||
if not self._filters:
|
||||
return True
|
||||
|
||||
else:
|
||||
model = self.sourceModel()
|
||||
column = self.filterKeyColumn()
|
||||
idx = model.index(source_row, column, source_parent)
|
||||
data = model.data(idx, self.filterRole())
|
||||
if data in self._filters:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
class RecursiveSortFilterProxyModel(QtCore.QSortFilterProxyModel):
|
||||
"""Filters to the regex if any of the children matches allow parent"""
|
||||
def filterAcceptsRow(self, row, parent):
|
||||
|
|
|
|||
|
|
@ -1,14 +1,15 @@
|
|||
import logging
|
||||
import contextlib
|
||||
import collections
|
||||
|
||||
from avalon.vendor import qtawesome as awesome
|
||||
from avalon.vendor import qtawesome
|
||||
from avalon.vendor.Qt import QtWidgets, QtCore, QtGui
|
||||
from avalon import io
|
||||
from avalon import style
|
||||
|
||||
from .model import (
|
||||
TreeModel,
|
||||
Node,
|
||||
Item,
|
||||
RecursiveSortFilterProxyModel,
|
||||
DeselectableTreeView
|
||||
)
|
||||
|
|
@ -150,7 +151,7 @@ class AssetModel(TreeModel):
|
|||
|
||||
"""
|
||||
|
||||
COLUMNS = ["label"]
|
||||
Columns = ["label"]
|
||||
Name = 0
|
||||
Deprecated = 2
|
||||
ObjectId = 3
|
||||
|
|
@ -162,50 +163,88 @@ class AssetModel(TreeModel):
|
|||
super(AssetModel, self).__init__(parent=parent)
|
||||
self.refresh()
|
||||
|
||||
def _add_hierarchy(self, parent=None):
|
||||
def _add_hierarchy(self, assets, parent=None, silos=None):
|
||||
"""Add the assets that are related to the parent as children items.
|
||||
|
||||
# Find the assets under the parent
|
||||
find_data = {
|
||||
"type": "asset"
|
||||
}
|
||||
if parent is None:
|
||||
find_data['$or'] = [
|
||||
{'data.visualParent': {'$exists': False}},
|
||||
{'data.visualParent': None}
|
||||
]
|
||||
else:
|
||||
find_data["data.visualParent"] = parent['_id']
|
||||
This method does *not* query the database. These instead are queried
|
||||
in a single batch upfront as an optimization to reduce database
|
||||
queries. Resulting in up to 10x speed increase.
|
||||
|
||||
assets = io.find(find_data).sort('name', 1)
|
||||
for asset in assets:
|
||||
Args:
|
||||
assets (dict): All assets in the currently active silo stored
|
||||
by key/value
|
||||
|
||||
Returns:
|
||||
None
|
||||
|
||||
"""
|
||||
if silos:
|
||||
# WARNING: Silo item "_id" is set to silo value
|
||||
# mainly because GUI issue with perserve selection and expanded row
|
||||
# and because of easier hierarchy parenting (in "assets")
|
||||
for silo in silos:
|
||||
item = Item({
|
||||
"_id": silo,
|
||||
"name": silo,
|
||||
"label": silo,
|
||||
"type": "silo"
|
||||
})
|
||||
self.add_child(item, parent=parent)
|
||||
self._add_hierarchy(assets, parent=item)
|
||||
|
||||
parent_id = parent["_id"] if parent else None
|
||||
current_assets = assets.get(parent_id, list())
|
||||
|
||||
for asset in current_assets:
|
||||
# get label from data, otherwise use name
|
||||
data = asset.get("data", {})
|
||||
label = data.get("label", asset['name'])
|
||||
label = data.get("label", asset["name"])
|
||||
tags = data.get("tags", [])
|
||||
|
||||
# store for the asset for optimization
|
||||
deprecated = "deprecated" in tags
|
||||
|
||||
node = Node({
|
||||
"_id": asset['_id'],
|
||||
item = Item({
|
||||
"_id": asset["_id"],
|
||||
"name": asset["name"],
|
||||
"label": label,
|
||||
"type": asset['type'],
|
||||
"type": asset["type"],
|
||||
"tags": ", ".join(tags),
|
||||
"deprecated": deprecated,
|
||||
"_document": asset
|
||||
})
|
||||
self.add_child(node, parent=parent)
|
||||
self.add_child(item, parent=parent)
|
||||
|
||||
# Add asset's children recursively
|
||||
self._add_hierarchy(node)
|
||||
# Add asset's children recursively if it has children
|
||||
if asset["_id"] in assets:
|
||||
self._add_hierarchy(assets, parent=item)
|
||||
|
||||
def refresh(self):
|
||||
"""Refresh the data for the model."""
|
||||
|
||||
self.clear()
|
||||
self.beginResetModel()
|
||||
self._add_hierarchy(parent=None)
|
||||
|
||||
# Get all assets in current silo sorted by name
|
||||
db_assets = io.find({"type": "asset"}).sort("name", 1)
|
||||
silos = db_assets.distinct("silo") or None
|
||||
|
||||
# Group the assets by their visual parent's id
|
||||
assets_by_parent = collections.defaultdict(list)
|
||||
for asset in db_assets:
|
||||
parent_id = (
|
||||
asset.get("data", {}).get("visualParent") or
|
||||
asset.get("silo")
|
||||
)
|
||||
assets_by_parent[parent_id].append(asset)
|
||||
|
||||
# Build the hierarchical tree items recursively
|
||||
self._add_hierarchy(
|
||||
assets_by_parent,
|
||||
parent=None,
|
||||
silos=silos
|
||||
)
|
||||
|
||||
self.endResetModel()
|
||||
|
||||
def flags(self, index):
|
||||
|
|
@ -216,15 +255,17 @@ class AssetModel(TreeModel):
|
|||
if not index.isValid():
|
||||
return
|
||||
|
||||
node = index.internalPointer()
|
||||
item = index.internalPointer()
|
||||
if role == QtCore.Qt.DecorationRole: # icon
|
||||
|
||||
column = index.column()
|
||||
if column == self.Name:
|
||||
|
||||
# Allow a custom icon and custom icon color to be defined
|
||||
data = node["_document"]["data"]
|
||||
data = item.get("_document", {}).get("data", {})
|
||||
icon = data.get("icon", None)
|
||||
if icon is None and item.get("type") == "silo":
|
||||
icon = "database"
|
||||
color = data.get("color", style.colors.default)
|
||||
|
||||
if icon is None:
|
||||
|
|
@ -235,12 +276,12 @@ class AssetModel(TreeModel):
|
|||
icon = "folder" if has_children else "folder-o"
|
||||
|
||||
# Make the color darker when the asset is deprecated
|
||||
if node.get("deprecated", False):
|
||||
if item.get("deprecated", False):
|
||||
color = QtGui.QColor(color).darker(250)
|
||||
|
||||
try:
|
||||
key = "fa.{0}".format(icon) # font-awesome key
|
||||
icon = awesome.icon(key, color=color)
|
||||
icon = qtawesome.icon(key, color=color)
|
||||
return icon
|
||||
except Exception as exception:
|
||||
# Log an error message instead of erroring out completely
|
||||
|
|
@ -250,32 +291,18 @@ class AssetModel(TreeModel):
|
|||
return
|
||||
|
||||
if role == QtCore.Qt.ForegroundRole: # font color
|
||||
if "deprecated" in node.get("tags", []):
|
||||
if "deprecated" in item.get("tags", []):
|
||||
return QtGui.QColor(style.colors.light).darker(250)
|
||||
|
||||
if role == self.ObjectIdRole:
|
||||
return node.get("_id", None)
|
||||
return item.get("_id", None)
|
||||
|
||||
if role == self.DocumentRole:
|
||||
return node.get("_document", None)
|
||||
return item.get("_document", None)
|
||||
|
||||
return super(AssetModel, self).data(index, role)
|
||||
|
||||
|
||||
class AssetView(DeselectableTreeView):
|
||||
"""Item view.
|
||||
|
||||
This implements a context menu.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super(AssetView, self).__init__()
|
||||
self.setIndentation(15)
|
||||
self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
|
||||
self.setHeaderHidden(True)
|
||||
|
||||
|
||||
class AssetWidget(QtWidgets.QWidget):
|
||||
"""A Widget to display a tree of assets with filter
|
||||
|
||||
|
|
@ -286,7 +313,6 @@ class AssetWidget(QtWidgets.QWidget):
|
|||
|
||||
"""
|
||||
|
||||
silo_changed = QtCore.Signal(str) # on silo combobox change
|
||||
assets_refreshed = QtCore.Signal() # on model refresh
|
||||
selection_changed = QtCore.Signal() # on view selection change
|
||||
current_changed = QtCore.Signal() # on view current index change
|
||||
|
|
@ -300,17 +326,21 @@ class AssetWidget(QtWidgets.QWidget):
|
|||
layout.setSpacing(4)
|
||||
|
||||
# Tree View
|
||||
model = AssetModel()
|
||||
model = AssetModel(self)
|
||||
proxy = RecursiveSortFilterProxyModel()
|
||||
proxy.setSourceModel(model)
|
||||
proxy.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive)
|
||||
view = AssetView()
|
||||
|
||||
view = DeselectableTreeView()
|
||||
view.setIndentation(15)
|
||||
view.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
|
||||
view.setHeaderHidden(True)
|
||||
view.setModel(proxy)
|
||||
|
||||
# Header
|
||||
header = QtWidgets.QHBoxLayout()
|
||||
|
||||
icon = awesome.icon("fa.refresh", color=style.colors.light)
|
||||
icon = qtawesome.icon("fa.refresh", color=style.colors.light)
|
||||
refresh = QtWidgets.QPushButton(icon, "")
|
||||
refresh.setToolTip("Refresh items")
|
||||
|
||||
|
|
@ -337,7 +367,14 @@ class AssetWidget(QtWidgets.QWidget):
|
|||
self.view = view
|
||||
|
||||
def _refresh_model(self):
|
||||
self.model.refresh()
|
||||
with preserve_expanded_rows(
|
||||
self.view, column=0, role=self.model.ObjectIdRole
|
||||
):
|
||||
with preserve_selection(
|
||||
self.view, column=0, role=self.model.ObjectIdRole
|
||||
):
|
||||
self.model.refresh()
|
||||
|
||||
self.assets_refreshed.emit()
|
||||
|
||||
def refresh(self):
|
||||
|
|
@ -346,7 +383,7 @@ class AssetWidget(QtWidgets.QWidget):
|
|||
def get_active_asset(self):
|
||||
"""Return the asset id the current asset."""
|
||||
current = self.view.currentIndex()
|
||||
return current.data(self.model.ObjectIdRole)
|
||||
return current.data(self.model.ItemRole)
|
||||
|
||||
def get_active_index(self):
|
||||
return self.view.currentIndex()
|
||||
|
|
@ -357,7 +394,7 @@ class AssetWidget(QtWidgets.QWidget):
|
|||
rows = selection.selectedRows()
|
||||
return [row.data(self.model.ObjectIdRole) for row in rows]
|
||||
|
||||
def select_assets(self, assets, expand=True):
|
||||
def select_assets(self, assets, expand=True, key="name"):
|
||||
"""Select assets by name.
|
||||
|
||||
Args:
|
||||
|
|
@ -370,8 +407,14 @@ class AssetWidget(QtWidgets.QWidget):
|
|||
"""
|
||||
# TODO: Instead of individual selection optimize for many assets
|
||||
|
||||
assert isinstance(assets,
|
||||
(tuple, list)), "Assets must be list or tuple"
|
||||
if not isinstance(assets, (tuple, list)):
|
||||
assets = [assets]
|
||||
assert isinstance(
|
||||
assets, (tuple, list)
|
||||
), "Assets must be list or tuple"
|
||||
|
||||
# convert to list - tuple cant be modified
|
||||
assets = list(assets)
|
||||
|
||||
# Clear selection
|
||||
selection_model = self.view.selectionModel()
|
||||
|
|
@ -379,16 +422,25 @@ class AssetWidget(QtWidgets.QWidget):
|
|||
|
||||
# Select
|
||||
mode = selection_model.Select | selection_model.Rows
|
||||
for index in _iter_model_rows(self.proxy,
|
||||
column=0,
|
||||
include_root=False):
|
||||
data = index.data(self.model.NodeRole)
|
||||
name = data['name']
|
||||
if name in assets:
|
||||
selection_model.select(index, mode)
|
||||
for index in iter_model_rows(
|
||||
self.proxy, column=0, include_root=False
|
||||
):
|
||||
# stop iteration if there are no assets to process
|
||||
if not assets:
|
||||
break
|
||||
|
||||
if expand:
|
||||
self.view.expand(index)
|
||||
value = index.data(self.model.ItemRole).get(key)
|
||||
if value not in assets:
|
||||
continue
|
||||
|
||||
# Set the currently active index
|
||||
self.view.setCurrentIndex(index)
|
||||
# Remove processed asset
|
||||
assets.pop(assets.index(value))
|
||||
|
||||
selection_model.select(index, mode)
|
||||
|
||||
if expand:
|
||||
# Expand parent index
|
||||
self.view.expand(self.proxy.parent(index))
|
||||
|
||||
# Set the currently active index
|
||||
self.view.setCurrentIndex(index)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue