mirror of
https://github.com/ynput/ayon-core.git
synced 2026-01-02 08:54:53 +01:00
updated standalone publisher with changes in avalon-core
This commit is contained in:
parent
bdc1ab1d0e
commit
3db98b88d6
8 changed files with 155 additions and 107 deletions
|
|
@ -2,6 +2,7 @@ import os
|
|||
import sys
|
||||
import json
|
||||
from subprocess import Popen
|
||||
from bson.objectid import ObjectId
|
||||
from pype import lib as pypelib
|
||||
from avalon.vendor.Qt import QtWidgets, QtCore
|
||||
from avalon import api, style, schema
|
||||
|
|
@ -40,13 +41,13 @@ class Window(QtWidgets.QDialog):
|
|||
self.valid_parent = False
|
||||
|
||||
# assets widget
|
||||
widget_assets = AssetWidget(self)
|
||||
widget_assets = AssetWidget(dbcon=self._db, parent=self)
|
||||
|
||||
# family widget
|
||||
widget_family = FamilyWidget(self)
|
||||
widget_family = FamilyWidget(dbcon=self._db, parent=self)
|
||||
|
||||
# components widget
|
||||
widget_components = ComponentsWidget(self)
|
||||
widget_components = ComponentsWidget(parent=self)
|
||||
|
||||
# Body
|
||||
body = QtWidgets.QSplitter()
|
||||
|
|
@ -70,6 +71,7 @@ class Window(QtWidgets.QDialog):
|
|||
|
||||
# signals
|
||||
widget_assets.selection_changed.connect(self.on_asset_changed)
|
||||
widget_family.stateChanged.connect(self.set_valid_family)
|
||||
|
||||
self.widget_assets = widget_assets
|
||||
self.widget_family = widget_family
|
||||
|
|
@ -123,7 +125,10 @@ class Window(QtWidgets.QDialog):
|
|||
Updates the task view.
|
||||
|
||||
'''
|
||||
selected = self.widget_assets.get_selected_assets()
|
||||
selected = [
|
||||
asset_id for asset_id in self.widget_assets.get_selected_assets()
|
||||
if isinstance(asset_id, ObjectId)
|
||||
]
|
||||
if len(selected) == 1:
|
||||
self.valid_parent = True
|
||||
asset = self.db.find_one({"_id": selected[0], "type": "asset"})
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
from avalon.vendor.Qt import *
|
||||
from avalon.vendor import qtawesome as awesome
|
||||
from avalon.vendor import qtawesome
|
||||
from avalon import style
|
||||
|
||||
HelpRole = QtCore.Qt.UserRole + 2
|
||||
|
|
@ -12,13 +12,12 @@ from .button_from_svgs import SvgResizable, SvgButton
|
|||
|
||||
from .model_node import Node
|
||||
from .model_tree import TreeModel
|
||||
from .model_asset import AssetModel
|
||||
from .model_asset import AssetModel, _iter_model_rows
|
||||
from .model_filter_proxy_exact_match import ExactMatchesFilterProxyModel
|
||||
from .model_filter_proxy_recursive_sort import RecursiveSortFilterProxyModel
|
||||
from .model_tasks_template import TasksTemplateModel
|
||||
from .model_tree_view_deselectable import DeselectableTreeView
|
||||
|
||||
from .widget_asset_view import AssetView
|
||||
from .widget_asset import AssetWidget
|
||||
|
||||
from .widget_family_desc import FamilyDescriptionWidget
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import logging
|
||||
import collections
|
||||
from . import QtCore, QtGui
|
||||
from . import TreeModel, Node
|
||||
from . import style, awesome
|
||||
from . import style, qtawesome
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
|
@ -44,64 +45,102 @@ class AssetModel(TreeModel):
|
|||
DocumentRole = QtCore.Qt.UserRole + 2
|
||||
ObjectIdRole = QtCore.Qt.UserRole + 3
|
||||
|
||||
def __init__(self, parent):
|
||||
def __init__(self, dbcon, parent=None):
|
||||
super(AssetModel, self).__init__(parent=parent)
|
||||
self.parent_widget = parent
|
||||
self.dbcon = dbcon
|
||||
self.refresh()
|
||||
|
||||
@property
|
||||
def db(self):
|
||||
return self.parent_widget.db
|
||||
def _add_hierarchy(self, assets, parent=None, silos=None):
|
||||
"""Add the assets that are related to the parent as children items.
|
||||
|
||||
def _add_hierarchy(self, parent=None):
|
||||
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.
|
||||
|
||||
# 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']
|
||||
Args:
|
||||
assets (dict): All assets in the currently active silo stored
|
||||
by key/value
|
||||
|
||||
assets = self.db.find(find_data).sort('name', 1)
|
||||
for asset in assets:
|
||||
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:
|
||||
node = Node({
|
||||
"_id": silo,
|
||||
"name": silo,
|
||||
"label": silo,
|
||||
"type": "silo"
|
||||
})
|
||||
self.add_child(node, parent=parent)
|
||||
self._add_hierarchy(assets, parent=node)
|
||||
|
||||
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'],
|
||||
"_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)
|
||||
|
||||
# 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=node)
|
||||
|
||||
def refresh(self):
|
||||
"""Refresh the data for the model."""
|
||||
|
||||
self.clear()
|
||||
if (
|
||||
self.db.active_project() is None or
|
||||
self.db.active_project() == ''
|
||||
self.dbcon.active_project() is None or
|
||||
self.dbcon.active_project() == ''
|
||||
):
|
||||
return
|
||||
|
||||
self.beginResetModel()
|
||||
self._add_hierarchy(parent=None)
|
||||
|
||||
# Get all assets in current silo sorted by name
|
||||
db_assets = self.dbcon.find({"type": "asset"}).sort("name", 1)
|
||||
silos = db_assets.distinct("silo") or None
|
||||
# if any silo is set to None then it's expected it should not be used
|
||||
if silos and None in silos:
|
||||
silos = 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):
|
||||
|
|
@ -119,8 +158,10 @@ class AssetModel(TreeModel):
|
|||
if column == self.Name:
|
||||
|
||||
# Allow a custom icon and custom icon color to be defined
|
||||
data = node["_document"]["data"]
|
||||
data = node.get("_document", {}).get("data", {})
|
||||
icon = data.get("icon", None)
|
||||
if icon is None and node.get("type") == "silo":
|
||||
icon = "database"
|
||||
color = data.get("color", style.colors.default)
|
||||
|
||||
if icon is None:
|
||||
|
|
@ -136,7 +177,7 @@ class AssetModel(TreeModel):
|
|||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
from . import QtCore, TreeModel
|
||||
from . import Node
|
||||
from . import awesome, style
|
||||
from . import qtawesome, style
|
||||
|
||||
|
||||
class TasksTemplateModel(TreeModel):
|
||||
|
|
@ -11,7 +11,7 @@ class TasksTemplateModel(TreeModel):
|
|||
def __init__(self, selectable=True):
|
||||
super(TasksTemplateModel, self).__init__()
|
||||
self.selectable = selectable
|
||||
self.icon = awesome.icon(
|
||||
self.icon = qtawesome.icon(
|
||||
'fa.calendar-check-o',
|
||||
color=style.colors.default
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import contextlib
|
||||
from . import QtWidgets, QtCore
|
||||
from . import RecursiveSortFilterProxyModel, AssetModel, AssetView
|
||||
from . import awesome, style
|
||||
from . import RecursiveSortFilterProxyModel, AssetModel
|
||||
from . import qtawesome, style
|
||||
from . import TasksTemplateModel, DeselectableTreeView
|
||||
|
||||
from . import _iter_model_rows
|
||||
|
||||
@contextlib.contextmanager
|
||||
def preserve_expanded_rows(tree_view,
|
||||
|
|
@ -124,11 +124,11 @@ class AssetWidget(QtWidgets.QWidget):
|
|||
selection_changed = QtCore.Signal() # on view selection change
|
||||
current_changed = QtCore.Signal() # on view current index change
|
||||
|
||||
def __init__(self, parent):
|
||||
def __init__(self, dbcon, parent=None):
|
||||
super(AssetWidget, self).__init__(parent=parent)
|
||||
self.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
self.parent_widget = parent
|
||||
self.dbcon = dbcon
|
||||
|
||||
layout = QtWidgets.QVBoxLayout()
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
|
@ -139,17 +139,21 @@ class AssetWidget(QtWidgets.QWidget):
|
|||
self._set_projects()
|
||||
self.combo_projects.currentTextChanged.connect(self.on_project_change)
|
||||
# Tree View
|
||||
model = AssetModel(self)
|
||||
model = AssetModel(dbcon=self.dbcon, parent=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")
|
||||
|
||||
|
|
@ -195,13 +199,9 @@ class AssetWidget(QtWidgets.QWidget):
|
|||
self.proxy = proxy
|
||||
self.view = view
|
||||
|
||||
@property
|
||||
def db(self):
|
||||
return self.parent_widget.db
|
||||
|
||||
def collect_data(self):
|
||||
project = self.db.find_one({'type': 'project'})
|
||||
asset = self.db.find_one({'_id': self.get_active_asset()})
|
||||
project = self.dbcon.find_one({'type': 'project'})
|
||||
asset = self.dbcon.find_one({'_id': self.get_active_asset()})
|
||||
|
||||
try:
|
||||
index = self.task_view.selectedIndexes()[0]
|
||||
|
|
@ -211,41 +211,50 @@ class AssetWidget(QtWidgets.QWidget):
|
|||
data = {
|
||||
'project': project['name'],
|
||||
'asset': asset['name'],
|
||||
'silo': asset.get("silo")
|
||||
'parents': self.get_parents(asset),
|
||||
'task': task
|
||||
}
|
||||
|
||||
return data
|
||||
|
||||
def get_parents(self, entity):
|
||||
output = []
|
||||
if entity.get('data', {}).get('visualParent', None) is None:
|
||||
return output
|
||||
parent = self.db.find_one({'_id': entity['data']['visualParent']})
|
||||
parent = self.dbcon.find_one({'_id': entity['data']['visualParent']})
|
||||
output.append(parent['name'])
|
||||
output.extend(self.get_parents(parent))
|
||||
return output
|
||||
|
||||
def _set_projects(self):
|
||||
projects = list()
|
||||
for project in self.db.projects():
|
||||
for project in self.dbcon.projects():
|
||||
projects.append(project['name'])
|
||||
|
||||
self.combo_projects.clear()
|
||||
if len(projects) > 0:
|
||||
self.combo_projects.addItems(projects)
|
||||
self.db.activate_project(projects[0])
|
||||
self.dbcon.activate_project(projects[0])
|
||||
|
||||
def on_project_change(self):
|
||||
projects = list()
|
||||
for project in self.db.projects():
|
||||
for project in self.dbcon.projects():
|
||||
projects.append(project['name'])
|
||||
project_name = self.combo_projects.currentText()
|
||||
if project_name in projects:
|
||||
self.db.activate_project(project_name)
|
||||
self.dbcon.activate_project(project_name)
|
||||
self.refresh()
|
||||
|
||||
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):
|
||||
|
|
@ -255,7 +264,7 @@ class AssetWidget(QtWidgets.QWidget):
|
|||
tasks = []
|
||||
selected = self.get_selected_assets()
|
||||
if len(selected) == 1:
|
||||
asset = self.db.find_one({
|
||||
asset = self.dbcon.find_one({
|
||||
"_id": selected[0], "type": "asset"
|
||||
})
|
||||
if asset:
|
||||
|
|
@ -266,7 +275,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()
|
||||
|
|
@ -277,7 +286,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:
|
||||
|
|
@ -290,8 +299,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()
|
||||
|
|
@ -299,16 +314,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 lib.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)
|
||||
|
|
|
|||
|
|
@ -1,16 +0,0 @@
|
|||
from . import QtCore
|
||||
from . import DeselectableTreeView
|
||||
|
||||
|
||||
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)
|
||||
|
|
@ -19,11 +19,11 @@ class FamilyWidget(QtWidgets.QWidget):
|
|||
Separator = "---separator---"
|
||||
NOT_SELECTED = '< Nothing is selected >'
|
||||
|
||||
def __init__(self, parent):
|
||||
super().__init__(parent)
|
||||
def __init__(self, dbcon, parent=None):
|
||||
super(FamilyWidget, self).__init__(parent=parent)
|
||||
# Store internal states in here
|
||||
self.state = {"valid": False}
|
||||
self.parent_widget = parent
|
||||
self.dbcon = dbcon
|
||||
self.asset_name = self.NOT_SELECTED
|
||||
|
||||
body = QtWidgets.QWidget()
|
||||
|
|
@ -67,7 +67,7 @@ class FamilyWidget(QtWidgets.QWidget):
|
|||
|
||||
layout = QtWidgets.QVBoxLayout(container)
|
||||
|
||||
header = FamilyDescriptionWidget(self)
|
||||
header = FamilyDescriptionWidget(parent=self)
|
||||
layout.addWidget(header)
|
||||
|
||||
layout.addWidget(QtWidgets.QLabel("Family"))
|
||||
|
|
@ -124,10 +124,6 @@ class FamilyWidget(QtWidgets.QWidget):
|
|||
}
|
||||
return data
|
||||
|
||||
@property
|
||||
def db(self):
|
||||
return self.parent_widget.db
|
||||
|
||||
def change_asset(self, name):
|
||||
if name is None:
|
||||
name = self.NOT_SELECTED
|
||||
|
|
@ -136,7 +132,6 @@ class FamilyWidget(QtWidgets.QWidget):
|
|||
|
||||
def _on_state_changed(self, state):
|
||||
self.state['valid'] = state
|
||||
self.parent_widget.set_valid_family(state)
|
||||
|
||||
def _build_menu(self, default_names):
|
||||
"""Create optional predefined subset names
|
||||
|
|
@ -183,7 +178,7 @@ class FamilyWidget(QtWidgets.QWidget):
|
|||
assets = None
|
||||
if asset_name != self.NOT_SELECTED:
|
||||
# Get the assets from the database which match with the name
|
||||
assets_db = self.db.find(
|
||||
assets_db = self.dbcon.find(
|
||||
filter={"type": "asset"},
|
||||
projection={"name": 1}
|
||||
)
|
||||
|
|
@ -206,7 +201,7 @@ class FamilyWidget(QtWidgets.QWidget):
|
|||
if assets:
|
||||
# Get all subsets of the current asset
|
||||
asset_ids = [asset["_id"] for asset in assets]
|
||||
subsets = self.db.find(filter={"type": "subset",
|
||||
subsets = self.dbcon.find(filter={"type": "subset",
|
||||
"name": {"$regex": "{}*".format(family),
|
||||
"$options": "i"},
|
||||
"parent": {"$in": asset_ids}}) or []
|
||||
|
|
@ -259,17 +254,17 @@ class FamilyWidget(QtWidgets.QWidget):
|
|||
asset_name != self.NOT_SELECTED and
|
||||
subset_name.strip() != ''
|
||||
):
|
||||
asset = self.db.find_one({
|
||||
asset = self.dbcon.find_one({
|
||||
'type': 'asset',
|
||||
'name': asset_name
|
||||
})
|
||||
subset = self.db.find_one({
|
||||
subset = self.dbcon.find_one({
|
||||
'type': 'subset',
|
||||
'parent': asset['_id'],
|
||||
'name': subset_name
|
||||
})
|
||||
if subset:
|
||||
versions = self.db.find({
|
||||
versions = self.dbcon.find({
|
||||
'type': 'version',
|
||||
'parent': subset['_id']
|
||||
})
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import json
|
|||
|
||||
from . import QtWidgets, QtCore, QtGui
|
||||
from . import HelpRole, FamilyRole, ExistsRole, PluginRole
|
||||
from . import awesome
|
||||
from . import qtawesome
|
||||
from pype.vendor import six
|
||||
from pype import lib as pypelib
|
||||
|
||||
|
|
@ -87,7 +87,7 @@ class FamilyDescriptionWidget(QtWidgets.QWidget):
|
|||
plugin = item.data(PluginRole)
|
||||
icon = getattr(plugin, "icon", "info-circle")
|
||||
assert isinstance(icon, six.string_types)
|
||||
icon = awesome.icon("fa.{}".format(icon), color="white")
|
||||
icon = qtawesome.icon("fa.{}".format(icon), color="white")
|
||||
pixmap = icon.pixmap(self.SIZE, self.SIZE)
|
||||
pixmap = pixmap.scaled(self.SIZE, self.SIZE)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue