mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +01:00
Merge pull request #3139 from pypeclub/enhancement/OP-3149_Publisher-UI-modifications
Publisher: UI Modifications and fixes
This commit is contained in:
commit
95f2061714
13 changed files with 668 additions and 137 deletions
|
|
@ -2,10 +2,7 @@ from openpype.pipeline import (
|
|||
Creator,
|
||||
CreatedInstance
|
||||
)
|
||||
from openpype.lib import (
|
||||
FileDef,
|
||||
BoolDef,
|
||||
)
|
||||
from openpype.lib import FileDef
|
||||
|
||||
from .pipeline import (
|
||||
list_instances,
|
||||
|
|
@ -43,7 +40,6 @@ class TrayPublishCreator(Creator):
|
|||
class SettingsCreator(TrayPublishCreator):
|
||||
create_allow_context_change = True
|
||||
|
||||
enable_review = False
|
||||
extensions = []
|
||||
|
||||
def collect_instances(self):
|
||||
|
|
@ -67,19 +63,15 @@ class SettingsCreator(TrayPublishCreator):
|
|||
self._add_instance_to_context(new_instance)
|
||||
|
||||
def get_instance_attr_defs(self):
|
||||
output = []
|
||||
|
||||
file_def = FileDef(
|
||||
"filepath",
|
||||
folders=False,
|
||||
extensions=self.extensions,
|
||||
allow_sequences=self.allow_sequences,
|
||||
label="Filepath",
|
||||
)
|
||||
output.append(file_def)
|
||||
if self.enable_review:
|
||||
output.append(BoolDef("review", label="Review"))
|
||||
return output
|
||||
return [
|
||||
FileDef(
|
||||
"filepath",
|
||||
folders=False,
|
||||
extensions=self.extensions,
|
||||
allow_sequences=self.allow_sequences,
|
||||
label="Filepath",
|
||||
)
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def from_settings(cls, item_data):
|
||||
|
|
@ -97,7 +89,6 @@ class SettingsCreator(TrayPublishCreator):
|
|||
"icon": item_data["icon"],
|
||||
"description": item_data["description"],
|
||||
"detailed_description": item_data["detailed_description"],
|
||||
"enable_review": item_data["enable_review"],
|
||||
"extensions": item_data["extensions"],
|
||||
"allow_sequences": item_data["allow_sequences"],
|
||||
"default_variants": item_data["default_variants"]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
import pyblish.api
|
||||
from openpype.lib import BoolDef
|
||||
from openpype.pipeline import OpenPypePyblishPluginMixin
|
||||
|
||||
|
||||
class CollectReviewFamily(
|
||||
pyblish.api.InstancePlugin, OpenPypePyblishPluginMixin
|
||||
):
|
||||
"""Add review family."""
|
||||
|
||||
label = "Collect Review Family"
|
||||
order = pyblish.api.CollectorOrder - 0.49
|
||||
|
||||
hosts = ["traypublisher"]
|
||||
families = [
|
||||
"image",
|
||||
"render",
|
||||
"plate",
|
||||
"review"
|
||||
]
|
||||
|
||||
def process(self, instance):
|
||||
values = self.get_attr_values_from_data(instance.data)
|
||||
if values.get("add_review_family"):
|
||||
instance.data["families"].append("review")
|
||||
|
||||
@classmethod
|
||||
def get_attribute_defs(cls):
|
||||
return [
|
||||
BoolDef("add_review_family", label="Review", default=True)
|
||||
]
|
||||
|
|
@ -22,10 +22,6 @@ class CollectSettingsSimpleInstances(pyblish.api.InstancePlugin):
|
|||
repres = instance.data["representations"]
|
||||
|
||||
creator_attributes = instance.data["creator_attributes"]
|
||||
|
||||
if creator_attributes.get("review"):
|
||||
instance.data["families"].append("review")
|
||||
|
||||
filepath_item = creator_attributes["filepath"]
|
||||
self.log.info(filepath_item)
|
||||
filepaths = [
|
||||
|
|
@ -34,9 +30,11 @@ class CollectSettingsSimpleInstances(pyblish.api.InstancePlugin):
|
|||
]
|
||||
|
||||
instance.data["sourceFilepaths"] = filepaths
|
||||
instance.data["stagingDir"] = filepath_item["directory"]
|
||||
|
||||
filenames = filepath_item["filenames"]
|
||||
ext = os.path.splitext(filenames[0])[-1]
|
||||
_, ext = os.path.splitext(filenames[0])
|
||||
ext = ext[1:]
|
||||
if len(filenames) == 1:
|
||||
filenames = filenames[0]
|
||||
|
||||
|
|
@ -46,3 +44,7 @@ class CollectSettingsSimpleInstances(pyblish.api.InstancePlugin):
|
|||
"stagingDir": filepath_item["directory"],
|
||||
"files": filenames
|
||||
})
|
||||
|
||||
self.log.debug("Created Simple Settings instance {}".format(
|
||||
instance.data
|
||||
))
|
||||
|
|
|
|||
|
|
@ -316,6 +316,7 @@ class FileDefItem(object):
|
|||
self.is_sequence = False
|
||||
self.template = None
|
||||
self.frames = []
|
||||
self.is_empty = True
|
||||
|
||||
self.set_filenames(filenames, frames, template)
|
||||
|
||||
|
|
@ -323,7 +324,9 @@ class FileDefItem(object):
|
|||
return json.dumps(self.to_dict())
|
||||
|
||||
def __repr__(self):
|
||||
if self.is_sequence:
|
||||
if self.is_empty:
|
||||
filename = "< empty >"
|
||||
elif self.is_sequence:
|
||||
filename = self.template
|
||||
else:
|
||||
filename = self.filenames[0]
|
||||
|
|
@ -335,6 +338,9 @@ class FileDefItem(object):
|
|||
|
||||
@property
|
||||
def label(self):
|
||||
if self.is_empty:
|
||||
return None
|
||||
|
||||
if not self.is_sequence:
|
||||
return self.filenames[0]
|
||||
|
||||
|
|
@ -386,6 +392,8 @@ class FileDefItem(object):
|
|||
|
||||
@property
|
||||
def ext(self):
|
||||
if self.is_empty:
|
||||
return None
|
||||
_, ext = os.path.splitext(self.filenames[0])
|
||||
if ext:
|
||||
return ext
|
||||
|
|
@ -393,6 +401,9 @@ class FileDefItem(object):
|
|||
|
||||
@property
|
||||
def is_dir(self):
|
||||
if self.is_empty:
|
||||
return False
|
||||
|
||||
# QUESTION a better way how to define folder (in init argument?)
|
||||
if self.ext:
|
||||
return False
|
||||
|
|
@ -411,6 +422,7 @@ class FileDefItem(object):
|
|||
if is_sequence and not template:
|
||||
raise ValueError("Missing template for sequence")
|
||||
|
||||
self.is_empty = len(filenames) == 0
|
||||
self.filenames = filenames
|
||||
self.template = template
|
||||
self.frames = frames
|
||||
|
|
@ -560,11 +572,7 @@ class FileDef(AbtractAttrDef):
|
|||
# Change horizontal label
|
||||
is_label_horizontal = kwargs.get("is_label_horizontal")
|
||||
if is_label_horizontal is None:
|
||||
if single_item:
|
||||
is_label_horizontal = True
|
||||
else:
|
||||
is_label_horizontal = False
|
||||
kwargs["is_label_horizontal"] = is_label_horizontal
|
||||
kwargs["is_label_horizontal"] = False
|
||||
|
||||
self.single_item = single_item
|
||||
self.folders = folders
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@
|
|||
"default_variants": [
|
||||
"Main"
|
||||
],
|
||||
"enable_review": false,
|
||||
"description": "Publish workfile backup",
|
||||
"detailed_description": "",
|
||||
"allow_sequences": true,
|
||||
|
|
|
|||
|
|
@ -45,12 +45,6 @@
|
|||
"type": "text"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "enable_review",
|
||||
"label": "Enable review",
|
||||
"tooltip": "Allow to create review from source file/s.\nFiles must be supported to be able create review."
|
||||
},
|
||||
{
|
||||
"type": "separator"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -856,18 +856,31 @@ QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {
|
|||
}
|
||||
|
||||
/* New Create/Publish UI */
|
||||
#CreatorDetailedDescription {
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
padding-top: 5px;
|
||||
background: transparent;
|
||||
border: 1px solid {color:border};
|
||||
}
|
||||
|
||||
#CreateDialogHelpButton {
|
||||
background: rgba(255, 255, 255, 31);
|
||||
border-top-left-radius: 0.2em;
|
||||
border-bottom-left-radius: 0.2em;
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
font-size: 10pt;
|
||||
font-weight: bold;
|
||||
padding: 3px 3px 3px 3px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
#CreateDialogHelpButton:hover {
|
||||
background: rgba(255, 255, 255, 63);
|
||||
}
|
||||
#CreateDialogHelpButton QWidget {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
#PublishLogConsole {
|
||||
font-family: "Noto Sans Mono";
|
||||
|
|
@ -1014,7 +1027,44 @@ VariantInputsWidget QToolButton {
|
|||
border-left: 1px solid {color:border};
|
||||
}
|
||||
|
||||
#TasksCombobox[state="invalid"], #AssetNameInput[state="invalid"] {
|
||||
#AssetNameInputWidget {
|
||||
background: {color:bg-inputs};
|
||||
border: 1px solid {color:border};
|
||||
border-radius: 0.3em;
|
||||
}
|
||||
|
||||
#AssetNameInputWidget QWidget {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
#AssetNameInputButton {
|
||||
border-bottom-left-radius: 0px;
|
||||
border-top-left-radius: 0px;
|
||||
padding: 0px;
|
||||
qproperty-iconSize: 11px 11px;
|
||||
border-left: 1px solid {color:border};
|
||||
border-right: none;
|
||||
border-top: none;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
#AssetNameInput {
|
||||
border-bottom-right-radius: 0px;
|
||||
border-top-right-radius: 0px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
#AssetNameInputWidget:hover {
|
||||
border-color: {color:border-hover};
|
||||
}
|
||||
#AssetNameInputWidget:focus{
|
||||
border-color: {color:border-focus};
|
||||
}
|
||||
#AssetNameInputWidget:disabled {
|
||||
background: {color:bg-inputs-disabled};
|
||||
}
|
||||
|
||||
#TasksCombobox[state="invalid"], #AssetNameInputWidget[state="invalid"], #AssetNameInputButton[state="invalid"] {
|
||||
border-color: {color:publisher:error};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ from openpype.tools.utils.assets_widget import (
|
|||
|
||||
class CreateDialogAssetsWidget(SingleSelectAssetsWidget):
|
||||
current_context_required = QtCore.Signal()
|
||||
header_height_changed = QtCore.Signal(int)
|
||||
|
||||
def __init__(self, controller, parent):
|
||||
self._controller = controller
|
||||
|
|
@ -27,6 +28,27 @@ class CreateDialogAssetsWidget(SingleSelectAssetsWidget):
|
|||
self._last_selection = None
|
||||
self._enabled = None
|
||||
|
||||
self._last_filter_height = None
|
||||
|
||||
def _check_header_height(self):
|
||||
"""Catch header height changes.
|
||||
|
||||
Label on top of creaters should have same height so Creators view has
|
||||
same offset.
|
||||
"""
|
||||
height = self.header_widget.height()
|
||||
if height != self._last_filter_height:
|
||||
self._last_filter_height = height
|
||||
self.header_height_changed.emit(height)
|
||||
|
||||
def resizeEvent(self, event):
|
||||
super(CreateDialogAssetsWidget, self).resizeEvent(event)
|
||||
self._check_header_height()
|
||||
|
||||
def showEvent(self, event):
|
||||
super(CreateDialogAssetsWidget, self).showEvent(event)
|
||||
self._check_header_height()
|
||||
|
||||
def _on_current_asset_click(self):
|
||||
self.current_context_required.emit()
|
||||
|
||||
|
|
@ -71,6 +93,7 @@ class AssetsHierarchyModel(QtGui.QStandardItemModel):
|
|||
Uses controller to load asset hierarchy. All asset documents are stored by
|
||||
their parents.
|
||||
"""
|
||||
|
||||
def __init__(self, controller):
|
||||
super(AssetsHierarchyModel, self).__init__()
|
||||
self._controller = controller
|
||||
|
|
@ -143,6 +166,7 @@ class AssetsHierarchyModel(QtGui.QStandardItemModel):
|
|||
|
||||
class AssetsDialog(QtWidgets.QDialog):
|
||||
"""Dialog to select asset for a context of instance."""
|
||||
|
||||
def __init__(self, controller, parent):
|
||||
super(AssetsDialog, self).__init__(parent)
|
||||
self.setWindowTitle("Select asset")
|
||||
|
|
@ -196,9 +220,26 @@ class AssetsDialog(QtWidgets.QDialog):
|
|||
# - adds ability to call reset on multiple places without repeating
|
||||
self._soft_reset_enabled = True
|
||||
|
||||
self._first_show = True
|
||||
self._default_height = 500
|
||||
|
||||
def _on_first_show(self):
|
||||
center = self.rect().center()
|
||||
size = self.size()
|
||||
size.setHeight(self._default_height)
|
||||
|
||||
self.resize(size)
|
||||
new_pos = self.mapToGlobal(center)
|
||||
new_pos.setX(new_pos.x() - int(self.width() / 2))
|
||||
new_pos.setY(new_pos.y() - int(self.height() / 2))
|
||||
self.move(new_pos)
|
||||
|
||||
def showEvent(self, event):
|
||||
"""Refresh asset model on show."""
|
||||
super(AssetsDialog, self).showEvent(event)
|
||||
if self._first_show:
|
||||
self._first_show = False
|
||||
self._on_first_show()
|
||||
# Refresh on show
|
||||
self.reset(False)
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import re
|
|||
import traceback
|
||||
import copy
|
||||
|
||||
import qtawesome
|
||||
try:
|
||||
import commonmark
|
||||
except Exception:
|
||||
|
|
@ -15,7 +16,8 @@ from openpype.pipeline.create import (
|
|||
)
|
||||
from openpype.tools.utils import (
|
||||
ErrorMessageBox,
|
||||
MessageOverlayObject
|
||||
MessageOverlayObject,
|
||||
ClickableFrame,
|
||||
)
|
||||
|
||||
from .widgets import IconValuePixmapLabel
|
||||
|
|
@ -114,6 +116,8 @@ class CreateErrorMessageBox(ErrorMessageBox):
|
|||
|
||||
# TODO add creator identifier/label to details
|
||||
class CreatorShortDescWidget(QtWidgets.QWidget):
|
||||
height_changed = QtCore.Signal(int)
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super(CreatorShortDescWidget, self).__init__(parent=parent)
|
||||
|
||||
|
|
@ -152,6 +156,22 @@ class CreatorShortDescWidget(QtWidgets.QWidget):
|
|||
self._family_label = family_label
|
||||
self._description_label = description_label
|
||||
|
||||
self._last_height = None
|
||||
|
||||
def _check_height_change(self):
|
||||
height = self.height()
|
||||
if height != self._last_height:
|
||||
self._last_height = height
|
||||
self.height_changed.emit(height)
|
||||
|
||||
def showEvent(self, event):
|
||||
super(CreatorShortDescWidget, self).showEvent(event)
|
||||
self._check_height_change()
|
||||
|
||||
def resizeEvent(self, event):
|
||||
super(CreatorShortDescWidget, self).resizeEvent(event)
|
||||
self._check_height_change()
|
||||
|
||||
def set_plugin(self, plugin=None):
|
||||
if not plugin:
|
||||
self._icon_widget.set_icon_def(None)
|
||||
|
|
@ -168,13 +188,43 @@ class CreatorShortDescWidget(QtWidgets.QWidget):
|
|||
self._description_label.setText(description)
|
||||
|
||||
|
||||
class HelpButton(QtWidgets.QPushButton):
|
||||
resized = QtCore.Signal()
|
||||
class HelpButton(ClickableFrame):
|
||||
resized = QtCore.Signal(int)
|
||||
question_mark_icon_name = "fa.question"
|
||||
help_icon_name = "fa.question-circle"
|
||||
hide_icon_name = "fa.angle-left"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(HelpButton, self).__init__(*args, **kwargs)
|
||||
self.setObjectName("CreateDialogHelpButton")
|
||||
|
||||
question_mark_label = QtWidgets.QLabel(self)
|
||||
help_widget = QtWidgets.QWidget(self)
|
||||
|
||||
help_question = QtWidgets.QLabel(help_widget)
|
||||
help_label = QtWidgets.QLabel("Help", help_widget)
|
||||
hide_icon = QtWidgets.QLabel(help_widget)
|
||||
|
||||
help_layout = QtWidgets.QHBoxLayout(help_widget)
|
||||
help_layout.setContentsMargins(0, 0, 5, 0)
|
||||
help_layout.addWidget(help_question, 0)
|
||||
help_layout.addWidget(help_label, 0)
|
||||
help_layout.addStretch(1)
|
||||
help_layout.addWidget(hide_icon, 0)
|
||||
|
||||
layout = QtWidgets.QHBoxLayout(self)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.setSpacing(0)
|
||||
layout.addWidget(question_mark_label, 0)
|
||||
layout.addWidget(help_widget, 1)
|
||||
|
||||
help_widget.setVisible(False)
|
||||
|
||||
self._question_mark_label = question_mark_label
|
||||
self._help_widget = help_widget
|
||||
self._help_question = help_question
|
||||
self._hide_icon = hide_icon
|
||||
|
||||
self._expanded = None
|
||||
self.set_expanded()
|
||||
|
||||
|
|
@ -184,31 +234,56 @@ class HelpButton(QtWidgets.QPushButton):
|
|||
return
|
||||
expanded = False
|
||||
self._expanded = expanded
|
||||
if expanded:
|
||||
text = "<"
|
||||
self._help_widget.setVisible(expanded)
|
||||
self._update_content()
|
||||
|
||||
def _update_content(self):
|
||||
width = self.get_icon_width()
|
||||
if self._expanded:
|
||||
question_mark_pix = QtGui.QPixmap(width, width)
|
||||
question_mark_pix.fill(QtCore.Qt.transparent)
|
||||
|
||||
else:
|
||||
text = "?"
|
||||
self.setText(text)
|
||||
question_mark_icon = qtawesome.icon(
|
||||
self.question_mark_icon_name, color=QtCore.Qt.white
|
||||
)
|
||||
question_mark_pix = question_mark_icon.pixmap(width, width)
|
||||
|
||||
self._update_size()
|
||||
hide_icon = qtawesome.icon(
|
||||
self.hide_icon_name, color=QtCore.Qt.white
|
||||
)
|
||||
help_question_icon = qtawesome.icon(
|
||||
self.help_icon_name, color=QtCore.Qt.white
|
||||
)
|
||||
self._question_mark_label.setPixmap(question_mark_pix)
|
||||
self._question_mark_label.setMaximumWidth(width)
|
||||
self._hide_icon.setPixmap(hide_icon.pixmap(width, width))
|
||||
self._help_question.setPixmap(help_question_icon.pixmap(width, width))
|
||||
|
||||
def _update_size(self):
|
||||
new_size = self.minimumSizeHint()
|
||||
if self.size() != new_size:
|
||||
self.resize(new_size)
|
||||
self.resized.emit()
|
||||
def get_icon_width(self):
|
||||
metrics = self.fontMetrics()
|
||||
return metrics.height()
|
||||
|
||||
def set_pos_and_size(self, pos_x, pos_y, width, height):
|
||||
update_icon = self.height() != height
|
||||
self.move(pos_x, pos_y)
|
||||
self.resize(width, height)
|
||||
|
||||
if update_icon:
|
||||
self._update_content()
|
||||
self.updateGeometry()
|
||||
|
||||
def showEvent(self, event):
|
||||
super(HelpButton, self).showEvent(event)
|
||||
self._update_size()
|
||||
self.resized.emit(self.height())
|
||||
|
||||
def resizeEvent(self, event):
|
||||
super(HelpButton, self).resizeEvent(event)
|
||||
self._update_size()
|
||||
self.resized.emit(self.height())
|
||||
|
||||
|
||||
class CreateDialog(QtWidgets.QDialog):
|
||||
default_size = (900, 500)
|
||||
default_size = (1000, 560)
|
||||
|
||||
def __init__(
|
||||
self, controller, asset_name=None, task_name=None, parent=None
|
||||
|
|
@ -255,6 +330,14 @@ class CreateDialog(QtWidgets.QDialog):
|
|||
context_layout.addWidget(tasks_widget, 1)
|
||||
|
||||
# --- Creators view ---
|
||||
creators_header_widget = QtWidgets.QWidget(self)
|
||||
header_label_widget = QtWidgets.QLabel(
|
||||
"Choose family:", creators_header_widget
|
||||
)
|
||||
creators_header_layout = QtWidgets.QHBoxLayout(creators_header_widget)
|
||||
creators_header_layout.setContentsMargins(0, 0, 0, 0)
|
||||
creators_header_layout.addWidget(header_label_widget, 1)
|
||||
|
||||
creators_view = QtWidgets.QListView(self)
|
||||
creators_model = QtGui.QStandardItemModel()
|
||||
creators_view.setModel(creators_model)
|
||||
|
|
@ -271,7 +354,6 @@ class CreateDialog(QtWidgets.QDialog):
|
|||
|
||||
variant_hints_menu = QtWidgets.QMenu(variant_widget)
|
||||
variant_hints_group = QtWidgets.QActionGroup(variant_hints_menu)
|
||||
# variant_hints_btn.setMenu(variant_hints_menu)
|
||||
|
||||
variant_layout = QtWidgets.QHBoxLayout(variant_widget)
|
||||
variant_layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
|
@ -282,9 +364,6 @@ class CreateDialog(QtWidgets.QDialog):
|
|||
subset_name_input = QtWidgets.QLineEdit(self)
|
||||
subset_name_input.setEnabled(False)
|
||||
|
||||
create_btn = QtWidgets.QPushButton("Create", self)
|
||||
create_btn.setEnabled(False)
|
||||
|
||||
form_layout = QtWidgets.QFormLayout()
|
||||
form_layout.addRow("Variant:", variant_widget)
|
||||
form_layout.addRow("Subset:", subset_name_input)
|
||||
|
|
@ -292,10 +371,9 @@ class CreateDialog(QtWidgets.QDialog):
|
|||
mid_widget = QtWidgets.QWidget(self)
|
||||
mid_layout = QtWidgets.QVBoxLayout(mid_widget)
|
||||
mid_layout.setContentsMargins(0, 0, 0, 0)
|
||||
mid_layout.addWidget(QtWidgets.QLabel("Choose family:", self))
|
||||
mid_layout.addWidget(creators_header_widget, 0)
|
||||
mid_layout.addWidget(creators_view, 1)
|
||||
mid_layout.addLayout(form_layout, 0)
|
||||
mid_layout.addWidget(create_btn, 0)
|
||||
# ------------
|
||||
|
||||
# --- Creator short info and attr defs ---
|
||||
|
|
@ -305,31 +383,62 @@ class CreateDialog(QtWidgets.QDialog):
|
|||
creator_attrs_widget
|
||||
)
|
||||
|
||||
separator_widget = QtWidgets.QWidget(self)
|
||||
separator_widget.setObjectName("Separator")
|
||||
separator_widget.setMinimumHeight(2)
|
||||
separator_widget.setMaximumHeight(2)
|
||||
attr_separator_widget = QtWidgets.QWidget(self)
|
||||
attr_separator_widget.setObjectName("Separator")
|
||||
attr_separator_widget.setMinimumHeight(1)
|
||||
attr_separator_widget.setMaximumHeight(1)
|
||||
|
||||
# Precreate attributes widget
|
||||
pre_create_widget = PreCreateWidget(creator_attrs_widget)
|
||||
|
||||
# Create button
|
||||
create_btn_wrapper = QtWidgets.QWidget(creator_attrs_widget)
|
||||
create_btn = QtWidgets.QPushButton("Create", create_btn_wrapper)
|
||||
create_btn.setEnabled(False)
|
||||
|
||||
create_btn_wrap_layout = QtWidgets.QHBoxLayout(create_btn_wrapper)
|
||||
create_btn_wrap_layout.setContentsMargins(0, 0, 0, 0)
|
||||
create_btn_wrap_layout.addStretch(1)
|
||||
create_btn_wrap_layout.addWidget(create_btn, 0)
|
||||
|
||||
creator_attrs_layout = QtWidgets.QVBoxLayout(creator_attrs_widget)
|
||||
creator_attrs_layout.setContentsMargins(0, 0, 0, 0)
|
||||
creator_attrs_layout.addWidget(creator_short_desc_widget, 0)
|
||||
creator_attrs_layout.addWidget(separator_widget, 0)
|
||||
creator_attrs_layout.addWidget(attr_separator_widget, 0)
|
||||
creator_attrs_layout.addWidget(pre_create_widget, 1)
|
||||
creator_attrs_layout.addWidget(create_btn_wrapper, 0)
|
||||
# -------------------------------------
|
||||
|
||||
# --- Detailed information about creator ---
|
||||
# Detailed description of creator
|
||||
detail_description_widget = QtWidgets.QTextEdit(self)
|
||||
detail_description_widget.setObjectName("InfoText")
|
||||
detail_description_widget.setTextInteractionFlags(
|
||||
detail_description_widget = QtWidgets.QWidget(self)
|
||||
|
||||
detail_placoholder_widget = QtWidgets.QWidget(
|
||||
detail_description_widget
|
||||
)
|
||||
detail_placoholder_widget.setAttribute(
|
||||
QtCore.Qt.WA_TranslucentBackground
|
||||
)
|
||||
|
||||
detail_description_input = QtWidgets.QTextEdit(
|
||||
detail_description_widget
|
||||
)
|
||||
detail_description_input.setObjectName("CreatorDetailedDescription")
|
||||
detail_description_input.setTextInteractionFlags(
|
||||
QtCore.Qt.TextBrowserInteraction
|
||||
)
|
||||
detail_description_widget.setVisible(False)
|
||||
# -------------------------------------------
|
||||
|
||||
detail_description_layout = QtWidgets.QVBoxLayout(
|
||||
detail_description_widget
|
||||
)
|
||||
detail_description_layout.setContentsMargins(0, 0, 0, 0)
|
||||
detail_description_layout.setSpacing(0)
|
||||
detail_description_layout.addWidget(detail_placoholder_widget, 0)
|
||||
detail_description_layout.addWidget(detail_description_input, 1)
|
||||
|
||||
detail_description_widget.setVisible(False)
|
||||
|
||||
# -------------------------------------------
|
||||
splitter_widget = QtWidgets.QSplitter(self)
|
||||
splitter_widget.addWidget(context_widget)
|
||||
splitter_widget.addWidget(mid_widget)
|
||||
|
|
@ -344,17 +453,27 @@ class CreateDialog(QtWidgets.QDialog):
|
|||
layout.addWidget(splitter_widget, 1)
|
||||
|
||||
# Floating help button
|
||||
# - Create this button as last to be fully visible
|
||||
help_btn = HelpButton(self)
|
||||
|
||||
prereq_timer = QtCore.QTimer()
|
||||
prereq_timer.setInterval(50)
|
||||
prereq_timer.setSingleShot(True)
|
||||
|
||||
desc_width_anim_timer = QtCore.QTimer()
|
||||
desc_width_anim_timer.setInterval(10)
|
||||
|
||||
prereq_timer.timeout.connect(self._on_prereq_timer)
|
||||
|
||||
desc_width_anim_timer.timeout.connect(self._on_desc_animation)
|
||||
|
||||
help_btn.clicked.connect(self._on_help_btn)
|
||||
help_btn.resized.connect(self._on_help_btn_resize)
|
||||
|
||||
assets_widget.header_height_changed.connect(
|
||||
self._on_asset_filter_height_change
|
||||
)
|
||||
|
||||
create_btn.clicked.connect(self._on_create)
|
||||
variant_widget.resized.connect(self._on_variant_widget_resize)
|
||||
variant_input.returnPressed.connect(self._on_create)
|
||||
|
|
@ -369,6 +488,10 @@ class CreateDialog(QtWidgets.QDialog):
|
|||
self._on_current_session_context_request
|
||||
)
|
||||
tasks_widget.task_changed.connect(self._on_task_change)
|
||||
creator_short_desc_widget.height_changed.connect(
|
||||
self._on_description_height_change
|
||||
)
|
||||
splitter_widget.splitterMoved.connect(self._on_splitter_move)
|
||||
|
||||
controller.add_plugins_refresh_callback(self._on_plugins_refresh)
|
||||
|
||||
|
|
@ -387,18 +510,33 @@ class CreateDialog(QtWidgets.QDialog):
|
|||
self.variant_hints_menu = variant_hints_menu
|
||||
self.variant_hints_group = variant_hints_group
|
||||
|
||||
self._creators_header_widget = creators_header_widget
|
||||
self.creators_model = creators_model
|
||||
self.creators_view = creators_view
|
||||
self.create_btn = create_btn
|
||||
|
||||
self._creator_short_desc_widget = creator_short_desc_widget
|
||||
self._pre_create_widget = pre_create_widget
|
||||
self._attr_separator_widget = attr_separator_widget
|
||||
|
||||
self._detail_placoholder_widget = detail_placoholder_widget
|
||||
self._detail_description_widget = detail_description_widget
|
||||
self._detail_description_input = detail_description_input
|
||||
self._help_btn = help_btn
|
||||
|
||||
self._prereq_timer = prereq_timer
|
||||
self._first_show = True
|
||||
|
||||
# Description animation
|
||||
self._description_size_policy = detail_description_widget.sizePolicy()
|
||||
self._desc_width_anim_timer = desc_width_anim_timer
|
||||
self._desc_widget_step = 0
|
||||
self._last_description_width = None
|
||||
self._last_full_width = 0
|
||||
self._expected_description_width = 0
|
||||
self._last_desc_max_width = None
|
||||
self._other_widgets_widths = []
|
||||
|
||||
def _emit_message(self, message):
|
||||
self._overlay_object.add_message(message)
|
||||
|
||||
|
|
@ -465,6 +603,10 @@ class CreateDialog(QtWidgets.QDialog):
|
|||
def _invalidate_prereq(self):
|
||||
self._prereq_timer.start()
|
||||
|
||||
def _on_asset_filter_height_change(self, height):
|
||||
self._creators_header_widget.setMinimumHeight(height)
|
||||
self._creators_header_widget.setMaximumHeight(height)
|
||||
|
||||
def _on_prereq_timer(self):
|
||||
prereq_available = True
|
||||
creator_btn_tooltips = []
|
||||
|
|
@ -595,6 +737,12 @@ class CreateDialog(QtWidgets.QDialog):
|
|||
if self._task_name:
|
||||
self._tasks_widget.select_task_name(self._task_name)
|
||||
|
||||
def _on_description_height_change(self):
|
||||
# Use separator's 'y' position as height
|
||||
height = self._attr_separator_widget.y()
|
||||
self._detail_placoholder_widget.setMinimumHeight(height)
|
||||
self._detail_placoholder_widget.setMaximumHeight(height)
|
||||
|
||||
def _on_creator_item_change(self, new_index, _old_index):
|
||||
identifier = None
|
||||
if new_index.isValid():
|
||||
|
|
@ -602,54 +750,192 @@ class CreateDialog(QtWidgets.QDialog):
|
|||
self._set_creator_by_identifier(identifier)
|
||||
|
||||
def _update_help_btn(self):
|
||||
pos_x = self.width() - self._help_btn.width()
|
||||
point = self._creator_short_desc_widget.rect().topRight()
|
||||
mapped_point = self._creator_short_desc_widget.mapTo(self, point)
|
||||
pos_y = mapped_point.y()
|
||||
self._help_btn.move(max(0, pos_x), max(0, pos_y))
|
||||
short_desc_rect = self._creator_short_desc_widget.rect()
|
||||
|
||||
def _on_help_btn_resize(self):
|
||||
# point = short_desc_rect.topRight()
|
||||
point = short_desc_rect.center()
|
||||
mapped_point = self._creator_short_desc_widget.mapTo(self, point)
|
||||
# pos_y = mapped_point.y()
|
||||
center_pos_y = mapped_point.y()
|
||||
icon_width = self._help_btn.get_icon_width()
|
||||
|
||||
_height = int(icon_width * 2.5)
|
||||
height = min(_height, short_desc_rect.height())
|
||||
pos_y = center_pos_y - int(height / 2)
|
||||
|
||||
pos_x = self.width() - icon_width
|
||||
if self._detail_placoholder_widget.isVisible():
|
||||
pos_x -= (
|
||||
self._detail_placoholder_widget.width()
|
||||
+ self._splitter_widget.handle(3).width()
|
||||
)
|
||||
|
||||
width = self.width() - pos_x
|
||||
|
||||
self._help_btn.set_pos_and_size(
|
||||
max(0, pos_x), max(0, pos_y),
|
||||
width, height
|
||||
)
|
||||
|
||||
def _on_help_btn_resize(self, height):
|
||||
if self._creator_short_desc_widget.height() != height:
|
||||
self._update_help_btn()
|
||||
|
||||
def _on_splitter_move(self, *args):
|
||||
self._update_help_btn()
|
||||
|
||||
def _on_help_btn(self):
|
||||
if self._desc_width_anim_timer.isActive():
|
||||
return
|
||||
|
||||
final_size = self.size()
|
||||
cur_sizes = self._splitter_widget.sizes()
|
||||
spacing = self._splitter_widget.handleWidth()
|
||||
|
||||
if self._desc_widget_step == 0:
|
||||
now_visible = self._detail_description_widget.isVisible()
|
||||
else:
|
||||
now_visible = self._desc_widget_step > 0
|
||||
|
||||
sizes = []
|
||||
for idx, value in enumerate(cur_sizes):
|
||||
if idx < 3:
|
||||
sizes.append(value)
|
||||
|
||||
now_visible = self._detail_description_widget.isVisible()
|
||||
self._last_full_width = final_size.width()
|
||||
self._other_widgets_widths = list(sizes)
|
||||
|
||||
if now_visible:
|
||||
width = final_size.width() - (
|
||||
spacing + self._detail_description_widget.width()
|
||||
)
|
||||
cur_desc_width = self._detail_description_widget.width()
|
||||
if cur_desc_width < 1:
|
||||
cur_desc_width = 2
|
||||
step_size = int(cur_desc_width / 5)
|
||||
if step_size < 1:
|
||||
step_size = 1
|
||||
|
||||
step_size *= -1
|
||||
expected_width = 0
|
||||
desc_width = cur_desc_width - 1
|
||||
width = final_size.width() - 1
|
||||
min_max = desc_width
|
||||
self._last_description_width = cur_desc_width
|
||||
|
||||
else:
|
||||
last_size = self._detail_description_widget.sizeHint().width()
|
||||
width = final_size.width() + spacing + last_size
|
||||
sizes.append(last_size)
|
||||
self._detail_description_widget.setVisible(True)
|
||||
handle = self._splitter_widget.handle(3)
|
||||
desc_width = handle.sizeHint().width()
|
||||
if self._last_description_width:
|
||||
expected_width = self._last_description_width
|
||||
else:
|
||||
hint = self._detail_description_widget.sizeHint()
|
||||
expected_width = hint.width()
|
||||
|
||||
width = final_size.width() + desc_width
|
||||
step_size = int(expected_width / 5)
|
||||
if step_size < 1:
|
||||
step_size = 1
|
||||
min_max = 0
|
||||
|
||||
if self._last_desc_max_width is None:
|
||||
self._last_desc_max_width = (
|
||||
self._detail_description_widget.maximumWidth()
|
||||
)
|
||||
self._detail_description_widget.setMinimumWidth(min_max)
|
||||
self._detail_description_widget.setMaximumWidth(min_max)
|
||||
self._expected_description_width = expected_width
|
||||
self._desc_widget_step = step_size
|
||||
|
||||
self._desc_width_anim_timer.start()
|
||||
|
||||
sizes.append(desc_width)
|
||||
|
||||
final_size.setWidth(width)
|
||||
|
||||
self._detail_description_widget.setVisible(not now_visible)
|
||||
self._splitter_widget.setSizes(sizes)
|
||||
self.resize(final_size)
|
||||
|
||||
self._help_btn.set_expanded(not now_visible)
|
||||
|
||||
def _on_desc_animation(self):
|
||||
current_width = self._detail_description_widget.width()
|
||||
|
||||
desc_width = None
|
||||
last_step = False
|
||||
growing = self._desc_widget_step > 0
|
||||
|
||||
# Growing
|
||||
if growing:
|
||||
if current_width < self._expected_description_width:
|
||||
desc_width = current_width + self._desc_widget_step
|
||||
if desc_width >= self._expected_description_width:
|
||||
desc_width = self._expected_description_width
|
||||
last_step = True
|
||||
|
||||
# Decreasing
|
||||
elif self._desc_widget_step < 0:
|
||||
if current_width > self._expected_description_width:
|
||||
desc_width = current_width + self._desc_widget_step
|
||||
if desc_width <= self._expected_description_width:
|
||||
desc_width = self._expected_description_width
|
||||
last_step = True
|
||||
|
||||
if desc_width is None:
|
||||
self._desc_widget_step = 0
|
||||
self._desc_width_anim_timer.stop()
|
||||
return
|
||||
|
||||
if last_step and not growing:
|
||||
self._detail_description_widget.setVisible(False)
|
||||
QtWidgets.QApplication.processEvents()
|
||||
|
||||
width = self._last_full_width
|
||||
handle_width = self._splitter_widget.handle(3).width()
|
||||
if growing:
|
||||
width += (handle_width + desc_width)
|
||||
else:
|
||||
width -= self._last_description_width
|
||||
if last_step:
|
||||
width -= handle_width
|
||||
else:
|
||||
width += desc_width
|
||||
|
||||
if not last_step or growing:
|
||||
self._detail_description_widget.setMaximumWidth(desc_width)
|
||||
self._detail_description_widget.setMinimumWidth(desc_width)
|
||||
|
||||
window_size = self.size()
|
||||
window_size.setWidth(width)
|
||||
self.resize(window_size)
|
||||
if not last_step:
|
||||
return
|
||||
|
||||
self._desc_widget_step = 0
|
||||
self._desc_width_anim_timer.stop()
|
||||
|
||||
if not growing:
|
||||
return
|
||||
|
||||
self._detail_description_widget.setMinimumWidth(0)
|
||||
self._detail_description_widget.setMaximumWidth(
|
||||
self._last_desc_max_width
|
||||
)
|
||||
self._detail_description_widget.setSizePolicy(
|
||||
self._description_size_policy
|
||||
)
|
||||
|
||||
sizes = list(self._other_widgets_widths)
|
||||
sizes.append(desc_width)
|
||||
self._splitter_widget.setSizes(sizes)
|
||||
|
||||
def _set_creator_detailed_text(self, creator):
|
||||
if not creator:
|
||||
self._detail_description_widget.setPlainText("")
|
||||
self._detail_description_input.setPlainText("")
|
||||
return
|
||||
detailed_description = creator.get_detail_description() or ""
|
||||
if commonmark:
|
||||
html = commonmark.commonmark(detailed_description)
|
||||
self._detail_description_widget.setHtml(html)
|
||||
self._detail_description_input.setHtml(html)
|
||||
else:
|
||||
self._detail_description_widget.setMarkdown(detailed_description)
|
||||
self._detail_description_input.setMarkdown(detailed_description)
|
||||
|
||||
def _set_creator_by_identifier(self, identifier):
|
||||
creator = self.controller.manual_creators.get(identifier)
|
||||
|
|
@ -806,6 +1092,21 @@ class CreateDialog(QtWidgets.QDialog):
|
|||
self.variant_input.setProperty("state", state)
|
||||
self.variant_input.style().polish(self.variant_input)
|
||||
|
||||
def _on_first_show(self):
|
||||
center = self.rect().center()
|
||||
|
||||
width, height = self.default_size
|
||||
self.resize(width, height)
|
||||
part = int(width / 7)
|
||||
self._splitter_widget.setSizes(
|
||||
[part * 2, part * 2, width - (part * 4)]
|
||||
)
|
||||
|
||||
new_pos = self.mapToGlobal(center)
|
||||
new_pos.setX(new_pos.x() - int(self.width() / 2))
|
||||
new_pos.setY(new_pos.y() - int(self.height() / 2))
|
||||
self.move(new_pos)
|
||||
|
||||
def moveEvent(self, event):
|
||||
super(CreateDialog, self).moveEvent(event)
|
||||
self._last_pos = self.pos()
|
||||
|
|
@ -814,13 +1115,7 @@ class CreateDialog(QtWidgets.QDialog):
|
|||
super(CreateDialog, self).showEvent(event)
|
||||
if self._first_show:
|
||||
self._first_show = False
|
||||
width, height = self.default_size
|
||||
self.resize(width, height)
|
||||
|
||||
third_size = int(width / 3)
|
||||
self._splitter_widget.setSizes(
|
||||
[third_size, third_size, width - (2 * third_size)]
|
||||
)
|
||||
self._on_first_show()
|
||||
|
||||
if self._last_pos is not None:
|
||||
self.move(self._last_pos)
|
||||
|
|
|
|||
|
|
@ -14,7 +14,8 @@ from openpype.tools.utils import (
|
|||
PlaceholderLineEdit,
|
||||
IconButton,
|
||||
PixmapLabel,
|
||||
BaseClickableFrame
|
||||
BaseClickableFrame,
|
||||
set_style_property,
|
||||
)
|
||||
from openpype.pipeline.create import SUBSET_NAME_ALLOWED_SYMBOLS
|
||||
from .assets_widget import AssetsDialog
|
||||
|
|
@ -344,21 +345,42 @@ class AssetsField(BaseClickableFrame):
|
|||
|
||||
def __init__(self, controller, parent):
|
||||
super(AssetsField, self).__init__(parent)
|
||||
self.setObjectName("AssetNameInputWidget")
|
||||
|
||||
dialog = AssetsDialog(controller, self)
|
||||
# Don't use 'self' for parent!
|
||||
# - this widget has specific styles
|
||||
dialog = AssetsDialog(controller, parent)
|
||||
|
||||
name_input = ClickableLineEdit(self)
|
||||
name_input.setObjectName("AssetNameInput")
|
||||
|
||||
icon_name = "fa.window-maximize"
|
||||
icon = qtawesome.icon(icon_name, color="white")
|
||||
icon_btn = QtWidgets.QPushButton(self)
|
||||
icon_btn.setIcon(icon)
|
||||
icon_btn.setObjectName("AssetNameInputButton")
|
||||
|
||||
layout = QtWidgets.QHBoxLayout(self)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.setSpacing(0)
|
||||
layout.addWidget(name_input, 1)
|
||||
layout.addWidget(icon_btn, 0)
|
||||
|
||||
# Make sure all widgets are vertically extended to highest widget
|
||||
for widget in (
|
||||
name_input,
|
||||
icon_btn
|
||||
):
|
||||
size_policy = widget.sizePolicy()
|
||||
size_policy.setVerticalPolicy(size_policy.MinimumExpanding)
|
||||
widget.setSizePolicy(size_policy)
|
||||
name_input.clicked.connect(self._mouse_release_callback)
|
||||
icon_btn.clicked.connect(self._mouse_release_callback)
|
||||
dialog.finished.connect(self._on_dialog_finish)
|
||||
|
||||
self._dialog = dialog
|
||||
self._name_input = name_input
|
||||
self._icon_btn = icon_btn
|
||||
|
||||
self._origin_value = []
|
||||
self._origin_selection = []
|
||||
|
|
@ -406,10 +428,9 @@ class AssetsField(BaseClickableFrame):
|
|||
self._set_state_property(state)
|
||||
|
||||
def _set_state_property(self, state):
|
||||
current_value = self._name_input.property("state")
|
||||
if current_value != state:
|
||||
self._name_input.setProperty("state", state)
|
||||
self._name_input.style().polish(self._name_input)
|
||||
set_style_property(self, "state", state)
|
||||
set_style_property(self._name_input, "state", state)
|
||||
set_style_property(self._icon_btn, "state", state)
|
||||
|
||||
def is_valid(self):
|
||||
"""Is asset valid."""
|
||||
|
|
@ -842,6 +863,8 @@ class VariantInputWidget(PlaceholderLineEdit):
|
|||
|
||||
self._ignore_value_change = True
|
||||
|
||||
self._has_value_changed = False
|
||||
|
||||
self._origin_value = list(variants)
|
||||
self._current_value = list(variants)
|
||||
|
||||
|
|
@ -892,11 +915,23 @@ class MultipleItemWidget(QtWidgets.QWidget):
|
|||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.addWidget(view)
|
||||
|
||||
model.rowsInserted.connect(self._on_insert)
|
||||
|
||||
self._view = view
|
||||
self._model = model
|
||||
|
||||
self._value = []
|
||||
|
||||
def _on_insert(self):
|
||||
self._update_size()
|
||||
|
||||
def _update_size(self):
|
||||
model = self._view.model()
|
||||
if model.rowCount() == 0:
|
||||
return
|
||||
height = self._view.sizeHintForRow(0)
|
||||
self.setMaximumHeight(height + (2 * self._view.spacing()))
|
||||
|
||||
def showEvent(self, event):
|
||||
super(MultipleItemWidget, self).showEvent(event)
|
||||
tmp_item = None
|
||||
|
|
@ -904,13 +939,15 @@ class MultipleItemWidget(QtWidgets.QWidget):
|
|||
# Add temp item to be able calculate maximum height of widget
|
||||
tmp_item = QtGui.QStandardItem("tmp")
|
||||
self._model.appendRow(tmp_item)
|
||||
|
||||
height = self._view.sizeHintForRow(0)
|
||||
self.setMaximumHeight(height + (2 * self._view.spacing()))
|
||||
self._update_size()
|
||||
|
||||
if tmp_item is not None:
|
||||
self._model.clear()
|
||||
|
||||
def resizeEvent(self, event):
|
||||
super(MultipleItemWidget, self).resizeEvent(event)
|
||||
self._update_size()
|
||||
|
||||
def set_value(self, value=None):
|
||||
"""Set value/s of currently selected instance."""
|
||||
if value is None:
|
||||
|
|
@ -1235,7 +1272,11 @@ class CreatorAttrsWidget(QtWidgets.QWidget):
|
|||
)
|
||||
|
||||
content_widget = QtWidgets.QWidget(self._scroll_area)
|
||||
content_layout = QtWidgets.QFormLayout(content_widget)
|
||||
content_layout = QtWidgets.QGridLayout(content_widget)
|
||||
content_layout.setColumnStretch(0, 0)
|
||||
content_layout.setColumnStretch(1, 1)
|
||||
|
||||
row = 0
|
||||
for attr_def, attr_instances, values in result:
|
||||
widget = create_widget_for_attr_def(attr_def, content_widget)
|
||||
if attr_def.is_value_def:
|
||||
|
|
@ -1246,10 +1287,28 @@ class CreatorAttrsWidget(QtWidgets.QWidget):
|
|||
else:
|
||||
widget.set_value(values, True)
|
||||
|
||||
label = attr_def.label or attr_def.key
|
||||
content_layout.addRow(label, widget)
|
||||
widget.value_changed.connect(self._input_value_changed)
|
||||
expand_cols = 2
|
||||
if attr_def.is_value_def and attr_def.is_label_horizontal:
|
||||
expand_cols = 1
|
||||
|
||||
col_num = 2 - expand_cols
|
||||
|
||||
label = attr_def.label or attr_def.key
|
||||
if label:
|
||||
label_widget = QtWidgets.QLabel(label, self)
|
||||
content_layout.addWidget(
|
||||
label_widget, row, 0, 1, expand_cols
|
||||
)
|
||||
if not attr_def.is_label_horizontal:
|
||||
row += 1
|
||||
|
||||
content_layout.addWidget(
|
||||
widget, row, col_num, 1, expand_cols
|
||||
)
|
||||
|
||||
row += 1
|
||||
|
||||
widget.value_changed.connect(self._input_value_changed)
|
||||
self._attr_def_id_to_instances[attr_def.id] = attr_instances
|
||||
self._attr_def_id_to_attr_def[attr_def.id] = attr_def
|
||||
|
||||
|
|
|
|||
|
|
@ -589,10 +589,12 @@ class AssetsWidget(QtWidgets.QWidget):
|
|||
view = AssetsView(self)
|
||||
view.setModel(proxy)
|
||||
|
||||
header_widget = QtWidgets.QWidget(self)
|
||||
|
||||
current_asset_icon = qtawesome.icon(
|
||||
"fa.arrow-down", color=get_default_tools_icon_color()
|
||||
)
|
||||
current_asset_btn = QtWidgets.QPushButton(self)
|
||||
current_asset_btn = QtWidgets.QPushButton(header_widget)
|
||||
current_asset_btn.setIcon(current_asset_icon)
|
||||
current_asset_btn.setToolTip("Go to Asset from current Session")
|
||||
# Hide by default
|
||||
|
|
@ -601,25 +603,35 @@ class AssetsWidget(QtWidgets.QWidget):
|
|||
refresh_icon = qtawesome.icon(
|
||||
"fa.refresh", color=get_default_tools_icon_color()
|
||||
)
|
||||
refresh_btn = QtWidgets.QPushButton(self)
|
||||
refresh_btn = QtWidgets.QPushButton(header_widget)
|
||||
refresh_btn.setIcon(refresh_icon)
|
||||
refresh_btn.setToolTip("Refresh items")
|
||||
|
||||
filter_input = PlaceholderLineEdit(self)
|
||||
filter_input = PlaceholderLineEdit(header_widget)
|
||||
filter_input.setPlaceholderText("Filter assets..")
|
||||
|
||||
# Header
|
||||
header_layout = QtWidgets.QHBoxLayout()
|
||||
header_layout = QtWidgets.QHBoxLayout(header_widget)
|
||||
header_layout.setContentsMargins(0, 0, 0, 0)
|
||||
header_layout.addWidget(filter_input)
|
||||
header_layout.addWidget(current_asset_btn)
|
||||
header_layout.addWidget(refresh_btn)
|
||||
|
||||
# Make header widgets expand vertically if there is a place
|
||||
for widget in (
|
||||
current_asset_btn,
|
||||
refresh_btn,
|
||||
filter_input,
|
||||
):
|
||||
size_policy = widget.sizePolicy()
|
||||
size_policy.setVerticalPolicy(size_policy.MinimumExpanding)
|
||||
widget.setSizePolicy(size_policy)
|
||||
|
||||
# Layout
|
||||
layout = QtWidgets.QVBoxLayout(self)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.setSpacing(4)
|
||||
layout.addLayout(header_layout)
|
||||
layout.addWidget(view)
|
||||
layout.addWidget(header_widget, 0)
|
||||
layout.addWidget(view, 1)
|
||||
|
||||
# Signals/Slots
|
||||
filter_input.textChanged.connect(self._on_filter_text_change)
|
||||
|
|
@ -630,6 +642,8 @@ class AssetsWidget(QtWidgets.QWidget):
|
|||
current_asset_btn.clicked.connect(self._on_current_asset_click)
|
||||
view.doubleClicked.connect(self.double_clicked)
|
||||
|
||||
self._header_widget = header_widget
|
||||
self._filter_input = filter_input
|
||||
self._refresh_btn = refresh_btn
|
||||
self._current_asset_btn = current_asset_btn
|
||||
self._model = model
|
||||
|
|
@ -637,8 +651,14 @@ class AssetsWidget(QtWidgets.QWidget):
|
|||
self._view = view
|
||||
self._last_project_name = None
|
||||
|
||||
self._last_btns_height = None
|
||||
|
||||
self.model_selection = {}
|
||||
|
||||
@property
|
||||
def header_widget(self):
|
||||
return self._header_widget
|
||||
|
||||
def _create_source_model(self):
|
||||
model = AssetModel(dbcon=self.dbcon, parent=self)
|
||||
model.refreshed.connect(self._on_model_refresh)
|
||||
|
|
@ -669,6 +689,7 @@ class AssetsWidget(QtWidgets.QWidget):
|
|||
This separation gives ability to override this method and use it
|
||||
in differnt way.
|
||||
"""
|
||||
|
||||
self.set_current_session_asset()
|
||||
|
||||
def set_current_session_asset(self):
|
||||
|
|
@ -681,6 +702,7 @@ class AssetsWidget(QtWidgets.QWidget):
|
|||
Some tools may have their global refresh button or do not support
|
||||
refresh at all.
|
||||
"""
|
||||
|
||||
if visible is None:
|
||||
visible = not self._refresh_btn.isVisible()
|
||||
self._refresh_btn.setVisible(visible)
|
||||
|
|
@ -690,6 +712,7 @@ class AssetsWidget(QtWidgets.QWidget):
|
|||
|
||||
Not all tools support using of current context asset.
|
||||
"""
|
||||
|
||||
if visible is None:
|
||||
visible = not self._current_asset_btn.isVisible()
|
||||
self._current_asset_btn.setVisible(visible)
|
||||
|
|
@ -723,6 +746,7 @@ class AssetsWidget(QtWidgets.QWidget):
|
|||
so if you're modifying model keep in mind that this method should be
|
||||
called when refresh is done.
|
||||
"""
|
||||
|
||||
self._proxy.sort(0)
|
||||
self._set_loading_state(loading=False, empty=not has_item)
|
||||
self.refreshed.emit()
|
||||
|
|
@ -767,6 +791,7 @@ class SingleSelectAssetsWidget(AssetsWidget):
|
|||
|
||||
Contain single selection specific api methods.
|
||||
"""
|
||||
|
||||
def get_selected_asset_id(self):
|
||||
"""Currently selected asset id."""
|
||||
selection_model = self._view.selectionModel()
|
||||
|
|
|
|||
|
|
@ -151,7 +151,7 @@ class FilesModel(QtGui.QStandardItemModel):
|
|||
item = QtGui.QStandardItem()
|
||||
item_id = str(uuid.uuid4())
|
||||
item.setData(item_id, ITEM_ID_ROLE)
|
||||
item.setData(file_item.label, ITEM_LABEL_ROLE)
|
||||
item.setData(file_item.label or "< empty >", ITEM_LABEL_ROLE)
|
||||
item.setData(file_item.filenames, FILENAMES_ROLE)
|
||||
item.setData(file_item.directory, DIRPATH_ROLE)
|
||||
item.setData(icon_pixmap, ITEM_ICON_ROLE)
|
||||
|
|
@ -251,7 +251,7 @@ class FilesProxyModel(QtCore.QSortFilterProxyModel):
|
|||
|
||||
|
||||
class ItemWidget(QtWidgets.QWidget):
|
||||
split_requested = QtCore.Signal(str)
|
||||
context_menu_requested = QtCore.Signal(QtCore.QPoint)
|
||||
|
||||
def __init__(
|
||||
self, item_id, label, pixmap_icon, is_sequence, multivalue, parent=None
|
||||
|
|
@ -316,19 +316,9 @@ class ItemWidget(QtWidgets.QWidget):
|
|||
self._update_btn_size()
|
||||
|
||||
def _on_actions_clicked(self):
|
||||
menu = QtWidgets.QMenu(self._split_btn)
|
||||
|
||||
action = QtWidgets.QAction("Split sequence", menu)
|
||||
action.triggered.connect(self._on_split_sequence)
|
||||
|
||||
menu.addAction(action)
|
||||
|
||||
pos = self._split_btn.rect().bottomLeft()
|
||||
point = self._split_btn.mapToGlobal(pos)
|
||||
menu.popup(point)
|
||||
|
||||
def _on_split_sequence(self):
|
||||
self.split_requested.emit(self._item_id)
|
||||
self.context_menu_requested.emit(point)
|
||||
|
||||
|
||||
class InViewButton(IconButton):
|
||||
|
|
@ -339,6 +329,7 @@ class FilesView(QtWidgets.QListView):
|
|||
"""View showing instances and their groups."""
|
||||
|
||||
remove_requested = QtCore.Signal()
|
||||
context_menu_requested = QtCore.Signal(QtCore.QPoint)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(FilesView, self).__init__(*args, **kwargs)
|
||||
|
|
@ -347,6 +338,7 @@ class FilesView(QtWidgets.QListView):
|
|||
self.setSelectionMode(
|
||||
QtWidgets.QAbstractItemView.ExtendedSelection
|
||||
)
|
||||
self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
|
||||
|
||||
remove_btn = InViewButton(self)
|
||||
pix_enabled = paint_image_with_color(
|
||||
|
|
@ -361,6 +353,7 @@ class FilesView(QtWidgets.QListView):
|
|||
remove_btn.setEnabled(False)
|
||||
|
||||
remove_btn.clicked.connect(self._on_remove_clicked)
|
||||
self.customContextMenuRequested.connect(self._on_context_menu_request)
|
||||
|
||||
self._remove_btn = remove_btn
|
||||
|
||||
|
|
@ -397,6 +390,12 @@ class FilesView(QtWidgets.QListView):
|
|||
selected_item_ids.add(instance_id)
|
||||
return selected_item_ids
|
||||
|
||||
def has_selected_sequence(self):
|
||||
for index in self.selectionModel().selectedIndexes():
|
||||
if index.data(IS_SEQUENCE_ROLE):
|
||||
return True
|
||||
return False
|
||||
|
||||
def event(self, event):
|
||||
if event.type() == QtCore.QEvent.KeyPress:
|
||||
if (
|
||||
|
|
@ -408,6 +407,12 @@ class FilesView(QtWidgets.QListView):
|
|||
|
||||
return super(FilesView, self).event(event)
|
||||
|
||||
def _on_context_menu_request(self, pos):
|
||||
index = self.indexAt(pos)
|
||||
if index.isValid():
|
||||
point = self.viewport().mapToGlobal(pos)
|
||||
self.context_menu_requested.emit(point)
|
||||
|
||||
def _on_selection_change(self):
|
||||
self._remove_btn.setEnabled(self.has_selected_item_ids())
|
||||
|
||||
|
|
@ -456,6 +461,9 @@ class FilesWidget(QtWidgets.QFrame):
|
|||
files_proxy_model.rowsInserted.connect(self._on_rows_inserted)
|
||||
files_proxy_model.rowsRemoved.connect(self._on_rows_removed)
|
||||
files_view.remove_requested.connect(self._on_remove_requested)
|
||||
files_view.context_menu_requested.connect(
|
||||
self._on_context_menu_requested
|
||||
)
|
||||
self._in_set_value = False
|
||||
self._single_item = single_item
|
||||
self._multivalue = False
|
||||
|
|
@ -504,7 +512,9 @@ class FilesWidget(QtWidgets.QFrame):
|
|||
return file_items
|
||||
if file_items:
|
||||
return file_items[0]
|
||||
return FileDefItem.create_empty_item()
|
||||
|
||||
empty_item = FileDefItem.create_empty_item()
|
||||
return empty_item.to_dict()
|
||||
|
||||
def set_filters(self, folders_allowed, exts_filter):
|
||||
self._files_proxy_model.set_allow_folders(folders_allowed)
|
||||
|
|
@ -527,7 +537,9 @@ class FilesWidget(QtWidgets.QFrame):
|
|||
is_sequence,
|
||||
self._multivalue
|
||||
)
|
||||
widget.split_requested.connect(self._on_split_request)
|
||||
widget.context_menu_requested.connect(
|
||||
self._on_context_menu_requested
|
||||
)
|
||||
self._files_view.setIndexWidget(index, widget)
|
||||
self._files_proxy_model.setData(
|
||||
index, widget.sizeHint(), QtCore.Qt.SizeHintRole
|
||||
|
|
@ -559,17 +571,22 @@ class FilesWidget(QtWidgets.QFrame):
|
|||
if not self._in_set_value:
|
||||
self.value_changed.emit()
|
||||
|
||||
def _on_split_request(self, item_id):
|
||||
def _on_split_request(self):
|
||||
if self._multivalue:
|
||||
return
|
||||
|
||||
file_item = self._files_model.get_file_item_by_id(item_id)
|
||||
if not file_item:
|
||||
item_ids = self._files_view.get_selected_item_ids()
|
||||
if not item_ids:
|
||||
return
|
||||
|
||||
new_items = file_item.split_sequence()
|
||||
self._remove_item_by_ids([item_id])
|
||||
self._add_filepaths(new_items)
|
||||
for item_id in item_ids:
|
||||
file_item = self._files_model.get_file_item_by_id(item_id)
|
||||
if not file_item:
|
||||
return
|
||||
|
||||
new_items = file_item.split_sequence()
|
||||
self._add_filepaths(new_items)
|
||||
self._remove_item_by_ids(item_ids)
|
||||
|
||||
def _on_remove_requested(self):
|
||||
if self._multivalue:
|
||||
|
|
@ -579,6 +596,23 @@ class FilesWidget(QtWidgets.QFrame):
|
|||
if items_to_delete:
|
||||
self._remove_item_by_ids(items_to_delete)
|
||||
|
||||
def _on_context_menu_requested(self, pos):
|
||||
if self._multivalue:
|
||||
return
|
||||
|
||||
menu = QtWidgets.QMenu(self._files_view)
|
||||
|
||||
if self._files_view.has_selected_sequence():
|
||||
split_action = QtWidgets.QAction("Split sequence", menu)
|
||||
split_action.triggered.connect(self._on_split_request)
|
||||
menu.addAction(split_action)
|
||||
|
||||
remove_action = QtWidgets.QAction("Remove", menu)
|
||||
remove_action.triggered.connect(self._on_remove_requested)
|
||||
menu.addAction(remove_action)
|
||||
|
||||
menu.popup(pos)
|
||||
|
||||
def sizeHint(self):
|
||||
# Get size hints of widget and visible widgets
|
||||
result = super(FilesWidget, self).sizeHint()
|
||||
|
|
|
|||
|
|
@ -91,6 +91,8 @@ class AttributeDefinitionsWidget(QtWidgets.QWidget):
|
|||
layout.deleteLater()
|
||||
|
||||
new_layout = QtWidgets.QGridLayout()
|
||||
new_layout.setColumnStretch(0, 0)
|
||||
new_layout.setColumnStretch(1, 1)
|
||||
self.setLayout(new_layout)
|
||||
|
||||
def set_attr_defs(self, attr_defs):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue