mirror of
https://github.com/ynput/ayon-core.git
synced 2026-01-02 00:44:52 +01:00
added widgets for family selection
This commit is contained in:
parent
d461971640
commit
09cc2e9e59
3 changed files with 393 additions and 0 deletions
|
|
@ -19,3 +19,5 @@ from .model_tree_view_deselectable import DeselectableTreeView
|
|||
|
||||
from .widget_asset_view import AssetView
|
||||
from .widget_asset import AssetWidget
|
||||
from .widget_family_desc import FamilyDescriptionWidget
|
||||
from .widget_family import FamilyWidget
|
||||
|
|
|
|||
290
pype/tools/standalonepublish/widgets/widget_family.py
Normal file
290
pype/tools/standalonepublish/widgets/widget_family.py
Normal file
|
|
@ -0,0 +1,290 @@
|
|||
import os
|
||||
import sys
|
||||
import inspect
|
||||
import json
|
||||
from collections import namedtuple
|
||||
|
||||
from . import QtWidgets, QtCore, QtGui
|
||||
from . import HelpRole, FamilyRole, ExistsRole, PluginRole
|
||||
from . import FamilyDescriptionWidget
|
||||
from pype.vendor import six
|
||||
|
||||
from avalon import api, io, style
|
||||
from pype import lib as pypelib
|
||||
|
||||
|
||||
class FamilyWidget(QtWidgets.QWidget):
|
||||
|
||||
stateChanged = QtCore.Signal(bool)
|
||||
data = dict()
|
||||
_jobs = dict()
|
||||
Separator = "---separator---"
|
||||
|
||||
def __init__(self, parent):
|
||||
super().__init__(parent)
|
||||
# Store internal states in here
|
||||
self.state = {"valid": False}
|
||||
self.parent_widget = parent
|
||||
|
||||
body = QtWidgets.QWidget()
|
||||
lists = QtWidgets.QWidget()
|
||||
|
||||
container = QtWidgets.QWidget()
|
||||
|
||||
list_families = QtWidgets.QListWidget()
|
||||
input_asset = QtWidgets.QLineEdit()
|
||||
input_asset.setEnabled(False)
|
||||
input_asset.setStyleSheet("color: #BBBBBB;")
|
||||
input_subset = QtWidgets.QLineEdit()
|
||||
input_result = QtWidgets.QLineEdit()
|
||||
input_result.setStyleSheet("color: gray;")
|
||||
input_result.setEnabled(False)
|
||||
|
||||
# region Menu for default subset names
|
||||
btn_subset = QtWidgets.QPushButton()
|
||||
btn_subset.setFixedWidth(18)
|
||||
btn_subset.setFixedHeight(20)
|
||||
menu_subset = QtWidgets.QMenu(btn_subset)
|
||||
btn_subset.setMenu(menu_subset)
|
||||
|
||||
# endregion
|
||||
name_layout = QtWidgets.QHBoxLayout()
|
||||
name_layout.addWidget(input_subset)
|
||||
name_layout.addWidget(btn_subset)
|
||||
name_layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
layout = QtWidgets.QVBoxLayout(container)
|
||||
|
||||
header = FamilyDescriptionWidget(self)
|
||||
layout.addWidget(header)
|
||||
|
||||
layout.addWidget(QtWidgets.QLabel("Family"))
|
||||
layout.addWidget(list_families)
|
||||
layout.addWidget(QtWidgets.QLabel("Asset"))
|
||||
layout.addWidget(input_asset)
|
||||
layout.addWidget(QtWidgets.QLabel("Subset"))
|
||||
layout.addLayout(name_layout)
|
||||
layout.addWidget(input_result)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
options = QtWidgets.QWidget()
|
||||
|
||||
layout = QtWidgets.QGridLayout(options)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
layout = QtWidgets.QHBoxLayout(lists)
|
||||
layout.addWidget(container)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
layout = QtWidgets.QVBoxLayout(body)
|
||||
layout.addWidget(lists)
|
||||
layout.addWidget(options, 0, QtCore.Qt.AlignLeft)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
layout = QtWidgets.QVBoxLayout(self)
|
||||
layout.addWidget(body)
|
||||
|
||||
input_subset.textChanged.connect(self.on_data_changed)
|
||||
input_asset.textChanged.connect(self.on_data_changed)
|
||||
list_families.currentItemChanged.connect(self.on_selection_changed)
|
||||
list_families.currentItemChanged.connect(header.set_item)
|
||||
|
||||
self.stateChanged.connect(self._on_state_changed)
|
||||
|
||||
self.input_subset = input_subset
|
||||
self.menu_subset = menu_subset
|
||||
self.btn_subset = btn_subset
|
||||
self.list_families = list_families
|
||||
self.input_asset = input_asset
|
||||
self.input_result = input_result
|
||||
|
||||
self.refresh()
|
||||
|
||||
@property
|
||||
def db(self):
|
||||
return self.parent_widget.db
|
||||
|
||||
def change_asset(self, name):
|
||||
self.input_asset.setText(name)
|
||||
|
||||
def _on_state_changed(self, state):
|
||||
self.state['valid'] = state
|
||||
|
||||
def _build_menu(self, default_names):
|
||||
"""Create optional predefined subset names
|
||||
|
||||
Args:
|
||||
default_names(list): all predefined names
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
|
||||
# Get and destroy the action group
|
||||
group = self.btn_subset.findChild(QtWidgets.QActionGroup)
|
||||
if group:
|
||||
group.deleteLater()
|
||||
|
||||
state = any(default_names)
|
||||
self.btn_subset.setEnabled(state)
|
||||
if state is False:
|
||||
return
|
||||
|
||||
# Build new action group
|
||||
group = QtWidgets.QActionGroup(self.btn_subset)
|
||||
for name in default_names:
|
||||
if name == self.Separator:
|
||||
self.menu_subset.addSeparator()
|
||||
continue
|
||||
action = group.addAction(name)
|
||||
self.menu_subset.addAction(action)
|
||||
|
||||
group.triggered.connect(self._on_action_clicked)
|
||||
|
||||
def _on_action_clicked(self, action):
|
||||
self.input_subset.setText(action.text())
|
||||
|
||||
def _on_data_changed(self):
|
||||
item = self.list_families.currentItem()
|
||||
subset_name = self.input_subset.text()
|
||||
asset_name = self.input_asset.text()
|
||||
|
||||
# Get the assets from the database which match with the name
|
||||
assets_db = self.db.find(filter={"type": "asset"}, projection={"name": 1})
|
||||
assets = [asset for asset in assets_db if asset_name in asset["name"]]
|
||||
if item is None:
|
||||
return
|
||||
if assets:
|
||||
# Get plugin and family
|
||||
plugin = item.data(PluginRole)
|
||||
if plugin is None:
|
||||
return
|
||||
family = plugin.family.rsplit(".", 1)[-1]
|
||||
|
||||
# Get all subsets of the current asset
|
||||
asset_ids = [asset["_id"] for asset in assets]
|
||||
subsets = self.db.find(filter={"type": "subset",
|
||||
"name": {"$regex": "{}*".format(family),
|
||||
"$options": "i"},
|
||||
"parent": {"$in": asset_ids}}) or []
|
||||
|
||||
# Get all subsets' their subset name, "Default", "High", "Low"
|
||||
existed_subsets = [sub["name"].split(family)[-1]
|
||||
for sub in subsets]
|
||||
|
||||
if plugin.defaults and isinstance(plugin.defaults, list):
|
||||
defaults = plugin.defaults[:] + [self.Separator]
|
||||
lowered = [d.lower() for d in plugin.defaults]
|
||||
for sub in [s for s in existed_subsets
|
||||
if s.lower() not in lowered]:
|
||||
defaults.append(sub)
|
||||
else:
|
||||
defaults = existed_subsets
|
||||
|
||||
self._build_menu(defaults)
|
||||
|
||||
# Update the result
|
||||
if subset_name:
|
||||
subset_name = subset_name[0].upper() + subset_name[1:]
|
||||
self.input_result.setText("{}{}".format(family, subset_name))
|
||||
|
||||
item.setData(ExistsRole, True)
|
||||
self.echo("Ready ..")
|
||||
else:
|
||||
self._build_menu([])
|
||||
item.setData(ExistsRole, False)
|
||||
if asset_name != self.parent_widget.NOT_SELECTED:
|
||||
self.echo("'%s' not found .." % asset_name)
|
||||
|
||||
# Update the valid state
|
||||
valid = (
|
||||
subset_name.strip() != "" and
|
||||
asset_name.strip() != "" and
|
||||
item.data(QtCore.Qt.ItemIsEnabled) and
|
||||
item.data(ExistsRole)
|
||||
)
|
||||
self.stateChanged.emit(valid)
|
||||
|
||||
def on_data_changed(self, *args):
|
||||
|
||||
# Set invalid state until it's reconfirmed to be valid by the
|
||||
# scheduled callback so any form of creation is held back until
|
||||
# valid again
|
||||
self.stateChanged.emit(False)
|
||||
self.schedule(self._on_data_changed, 500, channel="gui")
|
||||
|
||||
def on_selection_changed(self, *args):
|
||||
plugin = self.list_families.currentItem().data(PluginRole)
|
||||
if plugin is None:
|
||||
return
|
||||
|
||||
if plugin.defaults and isinstance(plugin.defaults, list):
|
||||
default = plugin.defaults[0]
|
||||
else:
|
||||
default = "Default"
|
||||
|
||||
self.input_subset.setText(default)
|
||||
|
||||
self.on_data_changed()
|
||||
|
||||
def keyPressEvent(self, event):
|
||||
"""Custom keyPressEvent.
|
||||
|
||||
Override keyPressEvent to do nothing so that Maya's panels won't
|
||||
take focus when pressing "SHIFT" whilst mouse is over viewport or
|
||||
outliner. This way users don't accidently perform Maya commands
|
||||
whilst trying to name an instance.
|
||||
|
||||
"""
|
||||
|
||||
def refresh(self):
|
||||
has_families = False
|
||||
|
||||
path_items = [
|
||||
pypelib.get_presets_path(), 'tools', 'standalone_publish.json'
|
||||
]
|
||||
filepath = os.path.sep.join(path_items)
|
||||
presets = dict()
|
||||
with open(filepath) as data_file:
|
||||
presets = json.load(data_file)
|
||||
|
||||
for creator in presets.get('families', {}).values():
|
||||
creator = namedtuple("Creator", creator.keys())(*creator.values())
|
||||
|
||||
label = creator.label or creator.family
|
||||
item = QtWidgets.QListWidgetItem(label)
|
||||
item.setData(QtCore.Qt.ItemIsEnabled, True)
|
||||
item.setData(HelpRole, creator.help or "")
|
||||
item.setData(FamilyRole, creator.family)
|
||||
item.setData(PluginRole, creator)
|
||||
item.setData(ExistsRole, False)
|
||||
self.list_families.addItem(item)
|
||||
|
||||
has_families = True
|
||||
|
||||
if not has_families:
|
||||
item = QtWidgets.QListWidgetItem("No registered families")
|
||||
item.setData(QtCore.Qt.ItemIsEnabled, False)
|
||||
self.list_families.addItem(item)
|
||||
|
||||
presets_path = pypelib.get_presets_path()
|
||||
config_file = os.path.sep.join([presets_path, 'tools', 'creator.json'])
|
||||
|
||||
self.list_families.setCurrentItem(self.list_families.item(0))
|
||||
|
||||
def echo(self, message):
|
||||
if hasattr(self.parent_widget, 'echo'):
|
||||
self.parent_widget.echo(message)
|
||||
|
||||
def schedule(self, func, time, channel="default"):
|
||||
try:
|
||||
self._jobs[channel].stop()
|
||||
except (AttributeError, KeyError):
|
||||
pass
|
||||
|
||||
timer = QtCore.QTimer()
|
||||
timer.setSingleShot(True)
|
||||
timer.timeout.connect(func)
|
||||
timer.start(time)
|
||||
|
||||
self._jobs[channel] = timer
|
||||
101
pype/tools/standalonepublish/widgets/widget_family_desc.py
Normal file
101
pype/tools/standalonepublish/widgets/widget_family_desc.py
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
import os
|
||||
import sys
|
||||
import inspect
|
||||
import json
|
||||
|
||||
from . import QtWidgets, QtCore, QtGui
|
||||
from . import HelpRole, FamilyRole, ExistsRole, PluginRole
|
||||
from . import awesome
|
||||
from pype.vendor import six
|
||||
from pype import lib as pypelib
|
||||
|
||||
|
||||
class FamilyDescriptionWidget(QtWidgets.QWidget):
|
||||
"""A family description widget.
|
||||
|
||||
Shows a family icon, family name and a help description.
|
||||
Used in creator header.
|
||||
|
||||
_________________
|
||||
| ____ |
|
||||
| |icon| FAMILY |
|
||||
| |____| help |
|
||||
|_________________|
|
||||
|
||||
"""
|
||||
|
||||
SIZE = 35
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super(FamilyDescriptionWidget, self).__init__(parent=parent)
|
||||
|
||||
# Header font
|
||||
font = QtGui.QFont()
|
||||
font.setBold(True)
|
||||
font.setPointSize(14)
|
||||
|
||||
layout = QtWidgets.QHBoxLayout(self)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
icon = QtWidgets.QLabel()
|
||||
icon.setSizePolicy(QtWidgets.QSizePolicy.Maximum,
|
||||
QtWidgets.QSizePolicy.Maximum)
|
||||
|
||||
# Add 4 pixel padding to avoid icon being cut off
|
||||
icon.setFixedWidth(self.SIZE + 4)
|
||||
icon.setFixedHeight(self.SIZE + 4)
|
||||
icon.setStyleSheet("""
|
||||
QLabel {
|
||||
padding-right: 5px;
|
||||
}
|
||||
""")
|
||||
|
||||
label_layout = QtWidgets.QVBoxLayout()
|
||||
label_layout.setSpacing(0)
|
||||
|
||||
family = QtWidgets.QLabel("family")
|
||||
family.setFont(font)
|
||||
family.setAlignment(QtCore.Qt.AlignBottom | QtCore.Qt.AlignLeft)
|
||||
|
||||
help = QtWidgets.QLabel("help")
|
||||
help.setAlignment(QtCore.Qt.AlignTop | QtCore.Qt.AlignLeft)
|
||||
|
||||
label_layout.addWidget(family)
|
||||
label_layout.addWidget(help)
|
||||
|
||||
layout.addWidget(icon)
|
||||
layout.addLayout(label_layout)
|
||||
|
||||
self.help = help
|
||||
self.family = family
|
||||
self.icon = icon
|
||||
|
||||
def set_item(self, item):
|
||||
"""Update elements to display information of a family item.
|
||||
|
||||
Args:
|
||||
family (dict): A family item as registered with name, help and icon
|
||||
|
||||
Returns:
|
||||
None
|
||||
|
||||
"""
|
||||
if not item:
|
||||
return
|
||||
|
||||
# Support a font-awesome icon
|
||||
plugin = item.data(PluginRole)
|
||||
icon = getattr(plugin, "icon", "info-circle")
|
||||
assert isinstance(icon, six.string_types)
|
||||
icon = awesome.icon("fa.{}".format(icon), color="white")
|
||||
pixmap = icon.pixmap(self.SIZE, self.SIZE)
|
||||
pixmap = pixmap.scaled(self.SIZE, self.SIZE)
|
||||
|
||||
# Parse a clean line from the Creator's docstring
|
||||
docstring = plugin.help or ""
|
||||
|
||||
help = docstring.splitlines()[0] if docstring else ""
|
||||
|
||||
self.icon.setPixmap(pixmap)
|
||||
self.family.setText(item.data(FamilyRole))
|
||||
self.help.setText(help)
|
||||
Loading…
Add table
Add a link
Reference in a new issue