Merge branch 'develop' into feature/OP-2045_Preview-for-multilayer-exrs
47
CHANGELOG.md
|
|
@ -1,26 +1,48 @@
|
|||
# Changelog
|
||||
|
||||
## [3.7.0-nightly.2](https://github.com/pypeclub/OpenPype/tree/HEAD)
|
||||
## [3.7.0-nightly.3](https://github.com/pypeclub/OpenPype/tree/HEAD)
|
||||
|
||||
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.6.4...HEAD)
|
||||
|
||||
### 📖 Documentation
|
||||
|
||||
- docs\[website\]: Add Ellipse Studio \(logo\) as an OpenPype contributor [\#2324](https://github.com/pypeclub/OpenPype/pull/2324)
|
||||
|
||||
**🆕 New features**
|
||||
|
||||
- Store typed version dependencies for workfiles [\#2192](https://github.com/pypeclub/OpenPype/pull/2192)
|
||||
|
||||
**🚀 Enhancements**
|
||||
|
||||
- Hiero: Add experimental tools action [\#2323](https://github.com/pypeclub/OpenPype/pull/2323)
|
||||
- Input links: Cleanup and unification of differences [\#2322](https://github.com/pypeclub/OpenPype/pull/2322)
|
||||
- General: Run process log stderr as info log level [\#2309](https://github.com/pypeclub/OpenPype/pull/2309)
|
||||
- Tools: Cleanup of unused classes [\#2304](https://github.com/pypeclub/OpenPype/pull/2304)
|
||||
- Project Manager: Added ability to delete project [\#2298](https://github.com/pypeclub/OpenPype/pull/2298)
|
||||
- Ftrack: Synchronize input links [\#2287](https://github.com/pypeclub/OpenPype/pull/2287)
|
||||
- StandalonePublisher: Remove unused plugin ExtractHarmonyZip [\#2277](https://github.com/pypeclub/OpenPype/pull/2277)
|
||||
- Ftrack: Support multiple reviews [\#2271](https://github.com/pypeclub/OpenPype/pull/2271)
|
||||
- Ftrack: Remove unused clean component plugin [\#2269](https://github.com/pypeclub/OpenPype/pull/2269)
|
||||
- Royal Render: Support for rr channels in separate dirs [\#2268](https://github.com/pypeclub/OpenPype/pull/2268)
|
||||
- Houdini: Add experimental tools action [\#2267](https://github.com/pypeclub/OpenPype/pull/2267)
|
||||
- Tools: Assets widget [\#2265](https://github.com/pypeclub/OpenPype/pull/2265)
|
||||
- Nuke: extract baked review videos presets [\#2248](https://github.com/pypeclub/OpenPype/pull/2248)
|
||||
- TVPaint: Workers rendering [\#2209](https://github.com/pypeclub/OpenPype/pull/2209)
|
||||
|
||||
**🐛 Bug fixes**
|
||||
|
||||
- Fix - provider icons are pulled from a folder [\#2326](https://github.com/pypeclub/OpenPype/pull/2326)
|
||||
- InputLinks: Typo in "inputLinks" key [\#2314](https://github.com/pypeclub/OpenPype/pull/2314)
|
||||
- Deadline timeout and logging [\#2312](https://github.com/pypeclub/OpenPype/pull/2312)
|
||||
- nuke: do not multiply representation on class method [\#2311](https://github.com/pypeclub/OpenPype/pull/2311)
|
||||
- Workfiles tool: Fix task formatting [\#2306](https://github.com/pypeclub/OpenPype/pull/2306)
|
||||
- Delivery: Fix delivery paths created on windows [\#2302](https://github.com/pypeclub/OpenPype/pull/2302)
|
||||
- Maya: Deadline - fix limit groups [\#2295](https://github.com/pypeclub/OpenPype/pull/2295)
|
||||
- New Publisher: Fix mapping of indexes [\#2285](https://github.com/pypeclub/OpenPype/pull/2285)
|
||||
- Alternate site for site sync doesnt work for sequences [\#2284](https://github.com/pypeclub/OpenPype/pull/2284)
|
||||
- FFmpeg: Execute ffprobe using list of arguments instead of string command [\#2281](https://github.com/pypeclub/OpenPype/pull/2281)
|
||||
- Nuke: Anatomy fill data use task as dictionary [\#2278](https://github.com/pypeclub/OpenPype/pull/2278)
|
||||
- Bug: fix variable name \_asset\_id in workfiles application [\#2274](https://github.com/pypeclub/OpenPype/pull/2274)
|
||||
- Version handling fixes [\#2272](https://github.com/pypeclub/OpenPype/pull/2272)
|
||||
|
||||
## [3.6.4](https://github.com/pypeclub/OpenPype/tree/3.6.4) (2021-11-23)
|
||||
|
||||
|
|
@ -44,23 +66,22 @@
|
|||
|
||||
**🚀 Enhancements**
|
||||
|
||||
- Tools: Assets widget [\#2265](https://github.com/pypeclub/OpenPype/pull/2265)
|
||||
- Royal Render: Support for rr channels in separate dirs [\#2268](https://github.com/pypeclub/OpenPype/pull/2268)
|
||||
- SceneInventory: Choose loader in asset switcher [\#2262](https://github.com/pypeclub/OpenPype/pull/2262)
|
||||
- Style: New fonts in OpenPype style [\#2256](https://github.com/pypeclub/OpenPype/pull/2256)
|
||||
- Tools: SceneInventory in OpenPype [\#2255](https://github.com/pypeclub/OpenPype/pull/2255)
|
||||
- Tools: Tasks widget [\#2251](https://github.com/pypeclub/OpenPype/pull/2251)
|
||||
- Tools: Creator in OpenPype [\#2244](https://github.com/pypeclub/OpenPype/pull/2244)
|
||||
- Added endpoint for configured extensions [\#2221](https://github.com/pypeclub/OpenPype/pull/2221)
|
||||
|
||||
**🐛 Bug fixes**
|
||||
|
||||
- Version handling fixes [\#2272](https://github.com/pypeclub/OpenPype/pull/2272)
|
||||
- Tools: Parenting of tools in Nuke and Hiero [\#2266](https://github.com/pypeclub/OpenPype/pull/2266)
|
||||
- limiting validator to specific editorial hosts [\#2264](https://github.com/pypeclub/OpenPype/pull/2264)
|
||||
- Tools: Select Context dialog attribute fix [\#2261](https://github.com/pypeclub/OpenPype/pull/2261)
|
||||
- Maya: Render publishing fails on linux [\#2260](https://github.com/pypeclub/OpenPype/pull/2260)
|
||||
- LookAssigner: Fix tool reopen [\#2259](https://github.com/pypeclub/OpenPype/pull/2259)
|
||||
- Standalone: editorial not publishing thumbnails on all subsets [\#2258](https://github.com/pypeclub/OpenPype/pull/2258)
|
||||
- Loader doesn't allow changing of version before loading [\#2254](https://github.com/pypeclub/OpenPype/pull/2254)
|
||||
- Burnins: Support mxf metadata [\#2247](https://github.com/pypeclub/OpenPype/pull/2247)
|
||||
- Maya: Support for configurable AOV separator characters [\#2197](https://github.com/pypeclub/OpenPype/pull/2197)
|
||||
- Maya: texture colorspace modes in looks [\#2195](https://github.com/pypeclub/OpenPype/pull/2195)
|
||||
|
|
@ -69,10 +90,6 @@
|
|||
|
||||
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/CI/3.6.1-nightly.1...3.6.1)
|
||||
|
||||
**🐛 Bug fixes**
|
||||
|
||||
- Loader doesn't allow changing of version before loading [\#2254](https://github.com/pypeclub/OpenPype/pull/2254)
|
||||
|
||||
## [3.6.0](https://github.com/pypeclub/OpenPype/tree/3.6.0) (2021-11-15)
|
||||
|
||||
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/CI/3.6.0-nightly.6...3.6.0)
|
||||
|
|
@ -82,14 +99,9 @@
|
|||
- Add alternative sites for Site Sync [\#2206](https://github.com/pypeclub/OpenPype/pull/2206)
|
||||
- Add command line way of running site sync server [\#2188](https://github.com/pypeclub/OpenPype/pull/2188)
|
||||
|
||||
**🆕 New features**
|
||||
|
||||
- Add validate active site button to sync queue on a project [\#2176](https://github.com/pypeclub/OpenPype/pull/2176)
|
||||
- Maya : Colorspace configuration [\#2170](https://github.com/pypeclub/OpenPype/pull/2170)
|
||||
- Blender: Added support for audio [\#2168](https://github.com/pypeclub/OpenPype/pull/2168)
|
||||
|
||||
**🚀 Enhancements**
|
||||
|
||||
- Tools: Creator in OpenPype [\#2244](https://github.com/pypeclub/OpenPype/pull/2244)
|
||||
- Tools: Subset manager in OpenPype [\#2243](https://github.com/pypeclub/OpenPype/pull/2243)
|
||||
- General: Skip module directories without init file [\#2239](https://github.com/pypeclub/OpenPype/pull/2239)
|
||||
- General: Static interfaces [\#2238](https://github.com/pypeclub/OpenPype/pull/2238)
|
||||
|
|
@ -109,8 +121,6 @@
|
|||
- Delivery: Check 'frame' key in template for sequence delivery [\#2196](https://github.com/pypeclub/OpenPype/pull/2196)
|
||||
- Settings: Site sync project settings improvement [\#2193](https://github.com/pypeclub/OpenPype/pull/2193)
|
||||
- Usage of tools code [\#2185](https://github.com/pypeclub/OpenPype/pull/2185)
|
||||
- Settings: Dictionary based on project roots [\#2184](https://github.com/pypeclub/OpenPype/pull/2184)
|
||||
- Subset name: Be able to pass asset document to get subset name [\#2179](https://github.com/pypeclub/OpenPype/pull/2179)
|
||||
|
||||
**🐛 Bug fixes**
|
||||
|
||||
|
|
@ -126,9 +136,6 @@
|
|||
- Tools: Workfiles tool don't use avalon widgets [\#2205](https://github.com/pypeclub/OpenPype/pull/2205)
|
||||
- Ftrack: Fill missing ftrack id on mongo project [\#2203](https://github.com/pypeclub/OpenPype/pull/2203)
|
||||
- Project Manager: Fix copying of tasks [\#2191](https://github.com/pypeclub/OpenPype/pull/2191)
|
||||
- Blender: Fix trying to pack an image when the shader node has no texture [\#2183](https://github.com/pypeclub/OpenPype/pull/2183)
|
||||
- Maya: review viewport settings [\#2177](https://github.com/pypeclub/OpenPype/pull/2177)
|
||||
- Maya: Aspect ratio [\#2174](https://github.com/pypeclub/OpenPype/pull/2174)
|
||||
|
||||
## [3.5.0](https://github.com/pypeclub/OpenPype/tree/3.5.0) (2021-10-17)
|
||||
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ def validate_mongo_connection(cnx: str) -> (bool, str):
|
|||
return False, "Not mongodb schema"
|
||||
|
||||
kwargs = {
|
||||
"serverSelectionTimeoutMS": 2000
|
||||
"serverSelectionTimeoutMS": os.environ.get("AVALON_TIMEOUT", 2000)
|
||||
}
|
||||
# Add certificate path if should be required
|
||||
if should_add_certificate_path_to_mongo_url(cnx):
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import logging
|
|||
|
||||
from avalon import io
|
||||
from avalon import api as avalon
|
||||
from avalon.vendor import Qt
|
||||
from Qt import QtWidgets
|
||||
from openpype import lib, api
|
||||
import pyblish.api as pyblish
|
||||
import openpype.hosts.aftereffects
|
||||
|
|
@ -41,10 +41,10 @@ def check_inventory():
|
|||
|
||||
# Warn about outdated containers.
|
||||
print("Starting new QApplication..")
|
||||
app = Qt.QtWidgets.QApplication(sys.argv)
|
||||
app = QtWidgets.QApplication(sys.argv)
|
||||
|
||||
message_box = Qt.QtWidgets.QMessageBox()
|
||||
message_box.setIcon(Qt.QtWidgets.QMessageBox.Warning)
|
||||
message_box = QtWidgets.QMessageBox()
|
||||
message_box.setIcon(QtWidgets.QMessageBox.Warning)
|
||||
msg = "There are outdated containers in the scene."
|
||||
message_box.setText(msg)
|
||||
message_box.exec_()
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import openpype.api
|
||||
from avalon.vendor import Qt
|
||||
from Qt import QtWidgets
|
||||
from avalon import aftereffects
|
||||
|
||||
import logging
|
||||
|
|
@ -56,7 +56,7 @@ class CreateRender(openpype.api.Creator):
|
|||
stub.rename_item(item.id, stub.PUBLISH_ICON + self.data["subset"])
|
||||
|
||||
def _show_msg(self, txt):
|
||||
msg = Qt.QtWidgets.QMessageBox()
|
||||
msg.setIcon(Qt.QtWidgets.QMessageBox.Warning)
|
||||
msg = QtWidgets.QMessageBox()
|
||||
msg.setIcon(QtWidgets.QMessageBox.Warning)
|
||||
msg.setText(txt)
|
||||
msg.exec_()
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import sys
|
||||
|
||||
from avalon.vendor.Qt import QtGui
|
||||
from Qt import QtGui
|
||||
import avalon.fusion
|
||||
from avalon import io
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
from avalon import api, style
|
||||
from avalon.vendor.Qt import QtGui, QtWidgets
|
||||
from Qt import QtGui, QtWidgets
|
||||
|
||||
import avalon.fusion
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
from avalon.vendor.Qt import QtWidgets
|
||||
from Qt import QtWidgets
|
||||
from avalon.vendor import qtawesome
|
||||
import avalon.fusion as avalon
|
||||
|
||||
|
|
|
|||
|
|
@ -2,12 +2,13 @@ import os
|
|||
import glob
|
||||
import logging
|
||||
|
||||
from Qt import QtWidgets, QtCore
|
||||
|
||||
import avalon.io as io
|
||||
import avalon.api as api
|
||||
import avalon.pipeline as pipeline
|
||||
import avalon.fusion
|
||||
import avalon.style as style
|
||||
from avalon.vendor.Qt import QtWidgets, QtCore
|
||||
from avalon.vendor import qtawesome as qta
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -5,13 +5,13 @@ import os
|
|||
import re
|
||||
import sys
|
||||
import ast
|
||||
import shutil
|
||||
import hiero
|
||||
from Qt import QtWidgets
|
||||
import avalon.api as avalon
|
||||
import avalon.io
|
||||
from avalon.vendor.Qt import QtWidgets
|
||||
from openpype.api import (Logger, Anatomy, get_anatomy_settings)
|
||||
from . import tags
|
||||
import shutil
|
||||
from compiler.ast import flatten
|
||||
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -105,9 +105,9 @@ def menu_install():
|
|||
sceneinventory_action.triggered.connect(
|
||||
lambda: host_tools.show_scene_inventory(parent=main_window)
|
||||
)
|
||||
menu.addSeparator()
|
||||
|
||||
if os.getenv("OPENPYPE_DEVELOP"):
|
||||
menu.addSeparator()
|
||||
reload_action = menu.addAction("Reload pipeline")
|
||||
reload_action.setIcon(QtGui.QIcon("icons:ColorAdd.png"))
|
||||
reload_action.triggered.connect(reload_config)
|
||||
|
|
@ -120,3 +120,10 @@ def menu_install():
|
|||
apply_colorspace_c_action = menu.addAction("Apply Colorspace Clips")
|
||||
apply_colorspace_c_action.setIcon(QtGui.QIcon("icons:ColorAdd.png"))
|
||||
apply_colorspace_c_action.triggered.connect(apply_colorspace_clips)
|
||||
|
||||
menu.addSeparator()
|
||||
|
||||
exeprimental_action = menu.addAction("Experimental tools...")
|
||||
exeprimental_action.triggered.connect(
|
||||
lambda: host_tools.show_experimental_tools_dialog(parent=main_window)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ from avalon.vendor import qargparse
|
|||
import avalon.api as avalon
|
||||
import openpype.api as openpype
|
||||
from . import lib
|
||||
from copy import deepcopy
|
||||
|
||||
log = openpype.Logger().get_logger(__name__)
|
||||
|
||||
|
|
@ -799,7 +800,8 @@ class PublishClip:
|
|||
# increasing steps by index of rename iteration
|
||||
self.count_steps *= self.rename_index
|
||||
|
||||
hierarchy_formating_data = dict()
|
||||
hierarchy_formating_data = {}
|
||||
hierarchy_data = deepcopy(self.hierarchy_data)
|
||||
_data = self.track_item_default_data.copy()
|
||||
if self.ui_inputs:
|
||||
# adding tag metadata from ui
|
||||
|
|
@ -824,19 +826,19 @@ class PublishClip:
|
|||
_data.update({"shot": self.shot_num})
|
||||
|
||||
# solve # in test to pythonic expression
|
||||
for _k, _v in self.hierarchy_data.items():
|
||||
for _k, _v in hierarchy_data.items():
|
||||
if "#" not in _v["value"]:
|
||||
continue
|
||||
self.hierarchy_data[
|
||||
hierarchy_data[
|
||||
_k]["value"] = self._replace_hash_to_expression(
|
||||
_k, _v["value"])
|
||||
|
||||
# fill up pythonic expresisons in hierarchy data
|
||||
for k, _v in self.hierarchy_data.items():
|
||||
for k, _v in hierarchy_data.items():
|
||||
hierarchy_formating_data[k] = _v["value"].format(**_data)
|
||||
else:
|
||||
# if no gui mode then just pass default data
|
||||
hierarchy_formating_data = self.hierarchy_data
|
||||
hierarchy_formating_data = hierarchy_data
|
||||
|
||||
tag_hierarchy_data = self._solve_tag_hierarchy_data(
|
||||
hierarchy_formating_data
|
||||
|
|
@ -886,30 +888,38 @@ class PublishClip:
|
|||
"families": [self.data["family"]]
|
||||
}
|
||||
|
||||
def _convert_to_entity(self, key):
|
||||
def _convert_to_entity(self, type, template):
|
||||
""" Converting input key to key with type. """
|
||||
# convert to entity type
|
||||
entity_type = self.types.get(key, None)
|
||||
entity_type = self.types.get(type, None)
|
||||
|
||||
assert entity_type, "Missing entity type for `{}`".format(
|
||||
key
|
||||
type
|
||||
)
|
||||
|
||||
# first collect formating data to use for formating template
|
||||
formating_data = {}
|
||||
for _k, _v in self.hierarchy_data.items():
|
||||
value = _v["value"].format(
|
||||
**self.track_item_default_data)
|
||||
formating_data[_k] = value
|
||||
|
||||
return {
|
||||
"entity_type": entity_type,
|
||||
"entity_name": self.hierarchy_data[key]["value"].format(
|
||||
**self.track_item_default_data
|
||||
"entity_name": template.format(
|
||||
**formating_data
|
||||
)
|
||||
}
|
||||
|
||||
def _create_parents(self):
|
||||
""" Create parents and return it in list. """
|
||||
self.parents = list()
|
||||
self.parents = []
|
||||
|
||||
patern = re.compile(self.parents_search_patern)
|
||||
par_split = [patern.findall(t).pop()
|
||||
|
||||
par_split = [(patern.findall(t).pop(), t)
|
||||
for t in self.hierarchy.split("/")]
|
||||
|
||||
for key in par_split:
|
||||
parent = self._convert_to_entity(key)
|
||||
for type, template in par_split:
|
||||
parent = self._convert_to_entity(type, template)
|
||||
self.parents.append(parent)
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ def on_save(_):
|
|||
def on_open(_):
|
||||
"""On scene open let's assume the containers have changed."""
|
||||
|
||||
from avalon.vendor.Qt import QtWidgets
|
||||
from Qt import QtWidgets
|
||||
from openpype.widgets import popup
|
||||
|
||||
cmds.evalDeferred(
|
||||
|
|
|
|||
|
|
@ -6,19 +6,19 @@ import platform
|
|||
import uuid
|
||||
import math
|
||||
|
||||
import bson
|
||||
import json
|
||||
import logging
|
||||
import itertools
|
||||
import contextlib
|
||||
from collections import OrderedDict, defaultdict
|
||||
from math import ceil
|
||||
from six import string_types
|
||||
import bson
|
||||
|
||||
from maya import cmds, mel
|
||||
import maya.api.OpenMaya as om
|
||||
|
||||
from avalon import api, maya, io, pipeline
|
||||
from avalon.vendor.six import string_types
|
||||
import avalon.maya.lib
|
||||
import avalon.maya.interactive
|
||||
|
||||
|
|
@ -1936,7 +1936,7 @@ def validate_fps():
|
|||
|
||||
if current_fps != fps:
|
||||
|
||||
from avalon.vendor.Qt import QtWidgets
|
||||
from Qt import QtWidgets
|
||||
from ...widgets import popup
|
||||
|
||||
# Find maya main window
|
||||
|
|
@ -2694,7 +2694,7 @@ def update_content_on_context_change():
|
|||
|
||||
|
||||
def show_message(title, msg):
|
||||
from avalon.vendor.Qt import QtWidgets
|
||||
from Qt import QtWidgets
|
||||
from openpype.widgets import message_window
|
||||
|
||||
# Find maya main window
|
||||
|
|
|
|||
|
|
@ -133,7 +133,7 @@ class ImportMayaLoader(api.Loader):
|
|||
|
||||
"""
|
||||
|
||||
from avalon.vendor.Qt import QtWidgets
|
||||
from Qt import QtWidgets
|
||||
|
||||
accept = QtWidgets.QMessageBox.Ok
|
||||
buttons = accept | QtWidgets.QMessageBox.Cancel
|
||||
|
|
|
|||
|
|
@ -275,7 +275,7 @@ class CollectYetiRig(pyblish.api.InstancePlugin):
|
|||
list: file sequence.
|
||||
|
||||
"""
|
||||
from avalon.vendor import clique
|
||||
import clique
|
||||
|
||||
escaped = re.escape(filepath)
|
||||
re_pattern = escaped.replace(pattern, "-?[0-9]+")
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
import os
|
||||
import json
|
||||
import getpass
|
||||
import appdirs
|
||||
import platform
|
||||
|
||||
import appdirs
|
||||
import requests
|
||||
|
||||
from maya import cmds
|
||||
|
||||
from avalon import api
|
||||
from avalon.vendor import requests
|
||||
|
||||
import pyblish.api
|
||||
from openpype.hosts.maya.api import lib
|
||||
|
|
|
|||
|
|
@ -89,8 +89,8 @@ class ValidateAssemblyModelTransforms(pyblish.api.InstancePlugin):
|
|||
|
||||
"""
|
||||
|
||||
from Qt import QtWidgets
|
||||
from openpype.hosts.maya.api import lib
|
||||
from avalon.vendor.Qt import QtWidgets
|
||||
|
||||
# Store namespace in variable, cosmetics thingy
|
||||
messagebox = QtWidgets.QMessageBox
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
import os
|
||||
import json
|
||||
|
||||
import appdirs
|
||||
import requests
|
||||
|
||||
import pyblish.api
|
||||
from avalon.vendor import requests
|
||||
from openpype.plugin import contextplugin_should_run
|
||||
import openpype.hosts.maya.api.action
|
||||
|
||||
|
|
|
|||
|
|
@ -70,7 +70,8 @@ def install():
|
|||
family_states = [
|
||||
"write",
|
||||
"review",
|
||||
"nukenodes"
|
||||
"nukenodes",
|
||||
"model",
|
||||
"gizmo"
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ class PypeCreator(PypeCreatorMixin, avalon.nuke.pipeline.Creator):
|
|||
self.data["subset"]):
|
||||
msg = ("The subset name `{0}` is already used on a node in"
|
||||
"this workfile.".format(self.data["subset"]))
|
||||
self.log.error(msg + '\n\nPlease use other subset name!')
|
||||
self.log.error(msg + "\n\nPlease use other subset name!")
|
||||
raise NameError("`{0}: {1}".format(__name__, msg))
|
||||
return
|
||||
|
||||
|
|
@ -53,7 +53,7 @@ class NukeLoader(api.Loader):
|
|||
container_id = None
|
||||
|
||||
def reset_container_id(self):
|
||||
self.container_id = ''.join(random.choice(
|
||||
self.container_id = "".join(random.choice(
|
||||
string.ascii_uppercase + string.digits) for _ in range(10))
|
||||
|
||||
def get_container_id(self, node):
|
||||
|
|
@ -61,7 +61,7 @@ class NukeLoader(api.Loader):
|
|||
return id_knob.value() if id_knob else None
|
||||
|
||||
def get_members(self, source):
|
||||
"""Return nodes that has same 'containerId' as `source`"""
|
||||
"""Return nodes that has same "containerId" as `source`"""
|
||||
source_id = self.get_container_id(source)
|
||||
return [node for node in nuke.allNodes(recurseGroups=True)
|
||||
if self.get_container_id(node) == source_id
|
||||
|
|
@ -116,11 +116,13 @@ class ExporterReview(object):
|
|||
|
||||
def __init__(self,
|
||||
klass,
|
||||
instance
|
||||
instance,
|
||||
multiple_presets=True
|
||||
):
|
||||
|
||||
self.log = klass.log
|
||||
self.instance = instance
|
||||
self.multiple_presets = multiple_presets
|
||||
self.path_in = self.instance.data.get("path", None)
|
||||
self.staging_dir = self.instance.data["stagingDir"]
|
||||
self.collection = self.instance.data.get("collection", None)
|
||||
|
|
@ -152,12 +154,10 @@ class ExporterReview(object):
|
|||
|
||||
def get_representation_data(self, tags=None, range=False):
|
||||
add_tags = tags or []
|
||||
|
||||
repre = {
|
||||
'outputName': self.name,
|
||||
'name': self.name,
|
||||
'ext': self.ext,
|
||||
'files': self.file,
|
||||
"name": self.name,
|
||||
"ext": self.ext,
|
||||
"files": self.file,
|
||||
"stagingDir": self.staging_dir,
|
||||
"tags": [self.name.replace("_", "-")] + add_tags
|
||||
}
|
||||
|
|
@ -168,6 +168,9 @@ class ExporterReview(object):
|
|||
"frameEnd": self.last_frame,
|
||||
})
|
||||
|
||||
if self.multiple_presets:
|
||||
repre["outputName"] = self.name
|
||||
|
||||
self.data["representations"].append(repre)
|
||||
|
||||
def get_view_input_process_node(self):
|
||||
|
|
@ -183,19 +186,19 @@ class ExporterReview(object):
|
|||
anlib.reset_selection()
|
||||
ipn_orig = None
|
||||
for v in nuke.allNodes(filter="Viewer"):
|
||||
ip = v['input_process'].getValue()
|
||||
ipn = v['input_process_node'].getValue()
|
||||
ip = v["input_process"].getValue()
|
||||
ipn = v["input_process_node"].getValue()
|
||||
if "VIEWER_INPUT" not in ipn and ip:
|
||||
ipn_orig = nuke.toNode(ipn)
|
||||
ipn_orig.setSelected(True)
|
||||
|
||||
if ipn_orig:
|
||||
# copy selected to clipboard
|
||||
nuke.nodeCopy('%clipboard%')
|
||||
nuke.nodeCopy("%clipboard%")
|
||||
# reset selection
|
||||
anlib.reset_selection()
|
||||
# paste node and selection is on it only
|
||||
nuke.nodePaste('%clipboard%')
|
||||
nuke.nodePaste("%clipboard%")
|
||||
# assign to variable
|
||||
ipn = nuke.selectedNode()
|
||||
|
||||
|
|
@ -234,9 +237,11 @@ class ExporterReviewLut(ExporterReview):
|
|||
ext=None,
|
||||
cube_size=None,
|
||||
lut_size=None,
|
||||
lut_style=None):
|
||||
lut_style=None,
|
||||
multiple_presets=True):
|
||||
# initialize parent class
|
||||
super(ExporterReviewLut, self).__init__(klass, instance)
|
||||
super(ExporterReviewLut, self).__init__(
|
||||
klass, instance, multiple_presets)
|
||||
|
||||
# deal with now lut defined in viewer lut
|
||||
if hasattr(klass, "viewer_lut_raw"):
|
||||
|
|
@ -349,9 +354,11 @@ class ExporterReviewMov(ExporterReview):
|
|||
instance,
|
||||
name=None,
|
||||
ext=None,
|
||||
multiple_presets=True
|
||||
):
|
||||
# initialize parent class
|
||||
super(ExporterReviewMov, self).__init__(klass, instance)
|
||||
super(ExporterReviewMov, self).__init__(
|
||||
klass, instance, multiple_presets)
|
||||
# passing presets for nodes to self
|
||||
self.nodes = klass.nodes if hasattr(klass, "nodes") else {}
|
||||
|
||||
|
|
|
|||
85
openpype/hosts/nuke/plugins/create/create_model.py
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
from avalon.nuke import lib as anlib
|
||||
from openpype.hosts.nuke.api import plugin
|
||||
import nuke
|
||||
|
||||
|
||||
class CreateModel(plugin.PypeCreator):
|
||||
"""Add Publishable Model Geometry"""
|
||||
|
||||
name = "model"
|
||||
label = "Create 3d Model"
|
||||
family = "model"
|
||||
icon = "cube"
|
||||
defaults = ["Main"]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(CreateModel, self).__init__(*args, **kwargs)
|
||||
self.nodes = nuke.selectedNodes()
|
||||
self.node_color = "0xff3200ff"
|
||||
return
|
||||
|
||||
def process(self):
|
||||
nodes = list()
|
||||
if (self.options or {}).get("useSelection"):
|
||||
nodes = self.nodes
|
||||
for n in nodes:
|
||||
n['selected'].setValue(0)
|
||||
end_nodes = list()
|
||||
|
||||
# get the latest nodes in tree for selecion
|
||||
for n in nodes:
|
||||
x = n
|
||||
end = 0
|
||||
while end == 0:
|
||||
try:
|
||||
x = x.dependent()[0]
|
||||
except:
|
||||
end_node = x
|
||||
end = 1
|
||||
end_nodes.append(end_node)
|
||||
|
||||
# set end_nodes
|
||||
end_nodes = list(set(end_nodes))
|
||||
|
||||
# check if nodes is 3d nodes
|
||||
for n in end_nodes:
|
||||
n['selected'].setValue(1)
|
||||
sn = nuke.createNode("Scene")
|
||||
if not sn.input(0):
|
||||
end_nodes.remove(n)
|
||||
nuke.delete(sn)
|
||||
|
||||
# loop over end nodes
|
||||
for n in end_nodes:
|
||||
n['selected'].setValue(1)
|
||||
|
||||
self.nodes = nuke.selectedNodes()
|
||||
nodes = self.nodes
|
||||
if len(nodes) >= 1:
|
||||
# loop selected nodes
|
||||
for n in nodes:
|
||||
data = self.data.copy()
|
||||
if len(nodes) > 1:
|
||||
# rename subset name only if more
|
||||
# then one node are selected
|
||||
subset = self.family + n["name"].value().capitalize()
|
||||
data["subset"] = subset
|
||||
|
||||
# change node color
|
||||
n["tile_color"].setValue(int(self.node_color, 16))
|
||||
# add avalon knobs
|
||||
anlib.set_avalon_knob_data(n, data)
|
||||
return True
|
||||
else:
|
||||
msg = str("Please select nodes you "
|
||||
"wish to add to a container")
|
||||
self.log.error(msg)
|
||||
nuke.message(msg)
|
||||
return
|
||||
else:
|
||||
# if selected is off then create one node
|
||||
model_node = nuke.createNode("WriteGeo")
|
||||
model_node["tile_color"].setValue(int(self.node_color, 16))
|
||||
# add avalon knobs
|
||||
instance = anlib.set_avalon_knob_data(model_node, self.data)
|
||||
return instance
|
||||
187
openpype/hosts/nuke/plugins/load/load_model.py
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
from avalon import api, io
|
||||
from avalon.nuke import lib as anlib
|
||||
from avalon.nuke import containerise, update_container
|
||||
import nuke
|
||||
|
||||
|
||||
class AlembicModelLoader(api.Loader):
|
||||
"""
|
||||
This will load alembic model into script.
|
||||
"""
|
||||
|
||||
families = ["model"]
|
||||
representations = ["abc"]
|
||||
|
||||
label = "Load Alembic Model"
|
||||
icon = "cube"
|
||||
color = "orange"
|
||||
node_color = "0x4ecd91ff"
|
||||
|
||||
def load(self, context, name, namespace, data):
|
||||
# get main variables
|
||||
version = context['version']
|
||||
version_data = version.get("data", {})
|
||||
vname = version.get("name", None)
|
||||
first = version_data.get("frameStart", None)
|
||||
last = version_data.get("frameEnd", None)
|
||||
fps = version_data.get("fps") or nuke.root()["fps"].getValue()
|
||||
namespace = namespace or context['asset']['name']
|
||||
object_name = "{}_{}".format(name, namespace)
|
||||
|
||||
# prepare data for imprinting
|
||||
# add additional metadata from the version to imprint to Avalon knob
|
||||
add_keys = ["source", "author", "fps"]
|
||||
|
||||
data_imprint = {"frameStart": first,
|
||||
"frameEnd": last,
|
||||
"version": vname,
|
||||
"objectName": object_name}
|
||||
|
||||
for k in add_keys:
|
||||
data_imprint.update({k: version_data[k]})
|
||||
|
||||
# getting file path
|
||||
file = self.fname.replace("\\", "/")
|
||||
|
||||
with anlib.maintained_selection():
|
||||
model_node = nuke.createNode(
|
||||
"ReadGeo2",
|
||||
"name {} file {} ".format(
|
||||
object_name, file),
|
||||
inpanel=False
|
||||
)
|
||||
model_node.forceValidate()
|
||||
model_node["frame_rate"].setValue(float(fps))
|
||||
|
||||
# workaround because nuke's bug is not adding
|
||||
# animation keys properly
|
||||
xpos = model_node.xpos()
|
||||
ypos = model_node.ypos()
|
||||
nuke.nodeCopy("%clipboard%")
|
||||
nuke.delete(model_node)
|
||||
nuke.nodePaste("%clipboard%")
|
||||
model_node = nuke.toNode(object_name)
|
||||
model_node.setXYpos(xpos, ypos)
|
||||
|
||||
# color node by correct color by actual version
|
||||
self.node_version_color(version, model_node)
|
||||
|
||||
return containerise(
|
||||
node=model_node,
|
||||
name=name,
|
||||
namespace=namespace,
|
||||
context=context,
|
||||
loader=self.__class__.__name__,
|
||||
data=data_imprint)
|
||||
|
||||
def update(self, container, representation):
|
||||
"""
|
||||
Called by Scene Inventory when look should be updated to current
|
||||
version.
|
||||
If any reference edits cannot be applied, eg. shader renamed and
|
||||
material not present, reference is unloaded and cleaned.
|
||||
All failed edits are highlighted to the user via message box.
|
||||
|
||||
Args:
|
||||
container: object that has look to be updated
|
||||
representation: (dict): relationship data to get proper
|
||||
representation from DB and persisted
|
||||
data in .json
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
# Get version from io
|
||||
version = io.find_one({
|
||||
"type": "version",
|
||||
"_id": representation["parent"]
|
||||
})
|
||||
object_name = container['objectName']
|
||||
# get corresponding node
|
||||
model_node = nuke.toNode(object_name)
|
||||
|
||||
# get main variables
|
||||
version_data = version.get("data", {})
|
||||
vname = version.get("name", None)
|
||||
first = version_data.get("frameStart", None)
|
||||
last = version_data.get("frameEnd", None)
|
||||
fps = version_data.get("fps") or nuke.root()["fps"].getValue()
|
||||
|
||||
# prepare data for imprinting
|
||||
# add additional metadata from the version to imprint to Avalon knob
|
||||
add_keys = ["source", "author", "fps"]
|
||||
|
||||
data_imprint = {"representation": str(representation["_id"]),
|
||||
"frameStart": first,
|
||||
"frameEnd": last,
|
||||
"version": vname,
|
||||
"objectName": object_name}
|
||||
|
||||
for k in add_keys:
|
||||
data_imprint.update({k: version_data[k]})
|
||||
|
||||
# getting file path
|
||||
file = api.get_representation_path(representation).replace("\\", "/")
|
||||
|
||||
with anlib.maintained_selection():
|
||||
model_node = nuke.toNode(object_name)
|
||||
model_node['selected'].setValue(True)
|
||||
|
||||
# collect input output dependencies
|
||||
dependencies = model_node.dependencies()
|
||||
dependent = model_node.dependent()
|
||||
|
||||
model_node["frame_rate"].setValue(float(fps))
|
||||
model_node["file"].setValue(file)
|
||||
|
||||
# workaround because nuke's bug is
|
||||
# not adding animation keys properly
|
||||
xpos = model_node.xpos()
|
||||
ypos = model_node.ypos()
|
||||
nuke.nodeCopy("%clipboard%")
|
||||
nuke.delete(model_node)
|
||||
nuke.nodePaste("%clipboard%")
|
||||
model_node = nuke.toNode(object_name)
|
||||
model_node.setXYpos(xpos, ypos)
|
||||
|
||||
# link to original input nodes
|
||||
for i, input in enumerate(dependencies):
|
||||
model_node.setInput(i, input)
|
||||
# link to original output nodes
|
||||
for d in dependent:
|
||||
index = next((i for i, dpcy in enumerate(
|
||||
d.dependencies())
|
||||
if model_node is dpcy), 0)
|
||||
d.setInput(index, model_node)
|
||||
|
||||
# color node by correct color by actual version
|
||||
self.node_version_color(version, model_node)
|
||||
|
||||
self.log.info("udated to version: {}".format(version.get("name")))
|
||||
|
||||
return update_container(model_node, data_imprint)
|
||||
|
||||
def node_version_color(self, version, node):
|
||||
""" Coloring a node by correct color by actual version
|
||||
"""
|
||||
# get all versions in list
|
||||
versions = io.find({
|
||||
"type": "version",
|
||||
"parent": version["parent"]
|
||||
}).distinct('name')
|
||||
|
||||
max_version = max(versions)
|
||||
|
||||
# change color of node
|
||||
if version.get("name") not in [max_version]:
|
||||
node["tile_color"].setValue(int("0xd88467ff", 16))
|
||||
else:
|
||||
node["tile_color"].setValue(int(self.node_color, 16))
|
||||
|
||||
def switch(self, container, representation):
|
||||
self.update(container, representation)
|
||||
|
||||
def remove(self, container):
|
||||
from avalon.nuke import viewer_update_and_undo_stop
|
||||
node = nuke.toNode(container['objectName'])
|
||||
with viewer_update_and_undo_stop():
|
||||
nuke.delete(node)
|
||||
49
openpype/hosts/nuke/plugins/publish/collect_model.py
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
import pyblish.api
|
||||
import nuke
|
||||
|
||||
|
||||
@pyblish.api.log
|
||||
class CollectModel(pyblish.api.InstancePlugin):
|
||||
"""Collect Model node instance and its content
|
||||
"""
|
||||
|
||||
order = pyblish.api.CollectorOrder + 0.22
|
||||
label = "Collect Model"
|
||||
hosts = ["nuke"]
|
||||
families = ["model"]
|
||||
|
||||
def process(self, instance):
|
||||
|
||||
grpn = instance[0]
|
||||
|
||||
# add family to familiess
|
||||
instance.data["families"].insert(0, instance.data["family"])
|
||||
# make label nicer
|
||||
instance.data["label"] = grpn.name()
|
||||
|
||||
# Get frame range
|
||||
handle_start = instance.context.data["handleStart"]
|
||||
handle_end = instance.context.data["handleEnd"]
|
||||
first_frame = int(nuke.root()["first_frame"].getValue())
|
||||
last_frame = int(nuke.root()["last_frame"].getValue())
|
||||
|
||||
# Add version data to instance
|
||||
version_data = {
|
||||
"handles": handle_start,
|
||||
"handleStart": handle_start,
|
||||
"handleEnd": handle_end,
|
||||
"frameStart": first_frame + handle_start,
|
||||
"frameEnd": last_frame - handle_end,
|
||||
"colorspace": nuke.root().knob('workingSpaceLUT').value(),
|
||||
"families": [instance.data["family"]] + instance.data["families"],
|
||||
"subset": instance.data["subset"],
|
||||
"fps": instance.context.data["fps"]
|
||||
}
|
||||
|
||||
instance.data.update({
|
||||
"versionData": version_data,
|
||||
"frameStart": first_frame,
|
||||
"frameEnd": last_frame
|
||||
})
|
||||
self.log.info("Model content collected: `{}`".format(instance[:]))
|
||||
self.log.info("Model instance collected: `{}`".format(instance))
|
||||
103
openpype/hosts/nuke/plugins/publish/extract_model.py
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
import nuke
|
||||
import os
|
||||
import pyblish.api
|
||||
import openpype.api
|
||||
from avalon.nuke import lib as anlib
|
||||
from pprint import pformat
|
||||
|
||||
|
||||
class ExtractModel(openpype.api.Extractor):
|
||||
""" 3D model exctractor
|
||||
"""
|
||||
label = 'Exctract Model'
|
||||
order = pyblish.api.ExtractorOrder
|
||||
families = ["model"]
|
||||
hosts = ["nuke"]
|
||||
|
||||
# presets
|
||||
write_geo_knobs = [
|
||||
("file_type", "abc"),
|
||||
("storageFormat", "Ogawa"),
|
||||
("writeGeometries", True),
|
||||
("writePointClouds", False),
|
||||
("writeAxes", False)
|
||||
]
|
||||
|
||||
def process(self, instance):
|
||||
handle_start = instance.context.data["handleStart"]
|
||||
handle_end = instance.context.data["handleEnd"]
|
||||
first_frame = int(nuke.root()["first_frame"].getValue())
|
||||
last_frame = int(nuke.root()["last_frame"].getValue())
|
||||
|
||||
self.log.info("instance.data: `{}`".format(
|
||||
pformat(instance.data)))
|
||||
|
||||
rm_nodes = list()
|
||||
model_node = instance[0]
|
||||
self.log.info("Crating additional nodes")
|
||||
subset = instance.data["subset"]
|
||||
staging_dir = self.staging_dir(instance)
|
||||
|
||||
extension = next((k[1] for k in self.write_geo_knobs
|
||||
if k[0] == "file_type"), None)
|
||||
if not extension:
|
||||
raise RuntimeError(
|
||||
"Bad config for extension in presets. "
|
||||
"Talk to your supervisor or pipeline admin")
|
||||
|
||||
# create file name and path
|
||||
filename = subset + ".{}".format(extension)
|
||||
file_path = os.path.join(staging_dir, filename).replace("\\", "/")
|
||||
|
||||
with anlib.maintained_selection():
|
||||
# select model node
|
||||
anlib.select_nodes([model_node])
|
||||
|
||||
# create write geo node
|
||||
wg_n = nuke.createNode("WriteGeo")
|
||||
wg_n["file"].setValue(file_path)
|
||||
# add path to write to
|
||||
for k, v in self.write_geo_knobs:
|
||||
wg_n[k].setValue(v)
|
||||
rm_nodes.append(wg_n)
|
||||
|
||||
# write out model
|
||||
nuke.execute(
|
||||
wg_n,
|
||||
int(first_frame),
|
||||
int(last_frame)
|
||||
)
|
||||
# erase additional nodes
|
||||
for n in rm_nodes:
|
||||
nuke.delete(n)
|
||||
|
||||
self.log.info(file_path)
|
||||
|
||||
# create representation data
|
||||
if "representations" not in instance.data:
|
||||
instance.data["representations"] = []
|
||||
|
||||
representation = {
|
||||
'name': extension,
|
||||
'ext': extension,
|
||||
'files': filename,
|
||||
"stagingDir": staging_dir,
|
||||
"frameStart": first_frame,
|
||||
"frameEnd": last_frame
|
||||
}
|
||||
instance.data["representations"].append(representation)
|
||||
|
||||
instance.data.update({
|
||||
"path": file_path,
|
||||
"outputDir": staging_dir,
|
||||
"ext": extension,
|
||||
"handleStart": handle_start,
|
||||
"handleEnd": handle_end,
|
||||
"frameStart": first_frame + handle_start,
|
||||
"frameEnd": last_frame - handle_end,
|
||||
"frameStartHandle": first_frame,
|
||||
"frameEndHandle": last_frame,
|
||||
})
|
||||
|
||||
self.log.info("Extracted instance '{0}' to: {1}".format(
|
||||
instance.name, file_path))
|
||||
|
|
@ -31,7 +31,7 @@ class ExtractReviewDataMov(openpype.api.Extractor):
|
|||
instance.data["representations"] = []
|
||||
|
||||
staging_dir = os.path.normpath(
|
||||
os.path.dirname(instance.data['path']))
|
||||
os.path.dirname(instance.data["path"]))
|
||||
|
||||
instance.data["stagingDir"] = staging_dir
|
||||
|
||||
|
|
@ -83,9 +83,15 @@ class ExtractReviewDataMov(openpype.api.Extractor):
|
|||
"Baking output `{}` with settings: {}".format(
|
||||
o_name, o_data))
|
||||
|
||||
# check if settings have more then one preset
|
||||
# so we dont need to add outputName to representation
|
||||
# in case there is only one preset
|
||||
multiple_presets = bool(len(self.outputs.keys()) > 1)
|
||||
|
||||
# create exporter instance
|
||||
exporter = plugin.ExporterReviewMov(
|
||||
self, instance, o_name, o_data["extension"])
|
||||
self, instance, o_name, o_data["extension"],
|
||||
multiple_presets)
|
||||
|
||||
if "render.farm" in families:
|
||||
if "review" in instance.data["families"]:
|
||||
|
|
|
|||
|
|
@ -2,9 +2,10 @@ import os
|
|||
import sys
|
||||
import logging
|
||||
|
||||
from Qt import QtWidgets
|
||||
|
||||
from avalon import io
|
||||
from avalon import api as avalon
|
||||
from avalon.vendor import Qt
|
||||
from openpype import lib
|
||||
from pyblish import api as pyblish
|
||||
import openpype.hosts.photoshop
|
||||
|
|
@ -38,10 +39,10 @@ def check_inventory():
|
|||
|
||||
# Warn about outdated containers.
|
||||
print("Starting new QApplication..")
|
||||
app = Qt.QtWidgets.QApplication(sys.argv)
|
||||
app = QtWidgets.QApplication(sys.argv)
|
||||
|
||||
message_box = Qt.QtWidgets.QMessageBox()
|
||||
message_box.setIcon(Qt.QtWidgets.QMessageBox.Warning)
|
||||
message_box = QtWidgets.QMessageBox()
|
||||
message_box.setIcon(QtWidgets.QMessageBox.Warning)
|
||||
msg = "There are outdated containers in the scene."
|
||||
message_box.setText(msg)
|
||||
message_box.exec_()
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
from Qt import QtWidgets
|
||||
import openpype.api
|
||||
from avalon.vendor import Qt
|
||||
from avalon import photoshop
|
||||
|
||||
|
||||
|
|
@ -26,21 +26,21 @@ class CreateImage(openpype.api.Creator):
|
|||
if len(selection) > 1:
|
||||
# Ask user whether to create one image or image per selected
|
||||
# item.
|
||||
msg_box = Qt.QtWidgets.QMessageBox()
|
||||
msg_box.setIcon(Qt.QtWidgets.QMessageBox.Warning)
|
||||
msg_box = QtWidgets.QMessageBox()
|
||||
msg_box.setIcon(QtWidgets.QMessageBox.Warning)
|
||||
msg_box.setText(
|
||||
"Multiple layers selected."
|
||||
"\nDo you want to make one image per layer?"
|
||||
)
|
||||
msg_box.setStandardButtons(
|
||||
Qt.QtWidgets.QMessageBox.Yes |
|
||||
Qt.QtWidgets.QMessageBox.No |
|
||||
Qt.QtWidgets.QMessageBox.Cancel
|
||||
QtWidgets.QMessageBox.Yes |
|
||||
QtWidgets.QMessageBox.No |
|
||||
QtWidgets.QMessageBox.Cancel
|
||||
)
|
||||
ret = msg_box.exec_()
|
||||
if ret == Qt.QtWidgets.QMessageBox.Yes:
|
||||
if ret == QtWidgets.QMessageBox.Yes:
|
||||
multiple_instances = True
|
||||
elif ret == Qt.QtWidgets.QMessageBox.Cancel:
|
||||
elif ret == QtWidgets.QMessageBox.Cancel:
|
||||
return
|
||||
|
||||
if multiple_instances:
|
||||
|
|
|
|||
|
|
@ -61,6 +61,9 @@ class OpenPypeMenu(QtWidgets.QWidget):
|
|||
inventory_btn = QtWidgets.QPushButton("Inventory ...", self)
|
||||
subsetm_btn = QtWidgets.QPushButton("Subset Manager ...", self)
|
||||
libload_btn = QtWidgets.QPushButton("Library ...", self)
|
||||
experimental_btn = QtWidgets.QPushButton(
|
||||
"Experimental tools ...", self
|
||||
)
|
||||
# rename_btn = QtWidgets.QPushButton("Rename", self)
|
||||
# set_colorspace_btn = QtWidgets.QPushButton(
|
||||
# "Set colorspace from presets", self
|
||||
|
|
@ -91,6 +94,8 @@ class OpenPypeMenu(QtWidgets.QWidget):
|
|||
|
||||
# layout.addWidget(set_colorspace_btn)
|
||||
# layout.addWidget(reset_resolution_btn)
|
||||
layout.addWidget(Spacer(15, self))
|
||||
layout.addWidget(experimental_btn)
|
||||
|
||||
self.setLayout(layout)
|
||||
|
||||
|
|
@ -104,6 +109,7 @@ class OpenPypeMenu(QtWidgets.QWidget):
|
|||
# rename_btn.clicked.connect(self.on_rename_clicked)
|
||||
# set_colorspace_btn.clicked.connect(self.on_set_colorspace_clicked)
|
||||
# reset_resolution_btn.clicked.connect(self.on_reset_resolution_clicked)
|
||||
experimental_btn.clicked.connect(self.on_experimental_clicked)
|
||||
|
||||
def on_workfile_clicked(self):
|
||||
print("Clicked Workfile")
|
||||
|
|
@ -142,6 +148,9 @@ class OpenPypeMenu(QtWidgets.QWidget):
|
|||
def on_reset_resolution_clicked(self):
|
||||
print("Clicked Reset Resolution")
|
||||
|
||||
def on_experimental_clicked(self):
|
||||
host_tools.show_experimental_tools_dialog()
|
||||
|
||||
|
||||
def launch_pype_menu():
|
||||
app = QtWidgets.QApplication(sys.argv)
|
||||
|
|
|
|||
|
|
@ -89,8 +89,10 @@ class Anatomy:
|
|||
|
||||
self.project_name = project_name
|
||||
|
||||
self._data = get_anatomy_settings(project_name, site_name)
|
||||
|
||||
self._data = self._prepare_anatomy_data(
|
||||
get_anatomy_settings(project_name, site_name)
|
||||
)
|
||||
self._site_name = site_name
|
||||
self._templates_obj = Templates(self)
|
||||
self._roots_obj = Roots(self)
|
||||
|
||||
|
|
@ -121,9 +123,36 @@ class Anatomy:
|
|||
"""
|
||||
return get_default_anatomy_settings(clear_metadata=False)
|
||||
|
||||
@staticmethod
|
||||
def _prepare_anatomy_data(anatomy_data):
|
||||
"""Prepare anatomy data for futher processing.
|
||||
|
||||
Method added to replace `{task}` with `{task[name]}` in templates.
|
||||
"""
|
||||
templates_data = anatomy_data.get("templates")
|
||||
if templates_data:
|
||||
# Replace `{task}` with `{task[name]}` in templates
|
||||
value_queue = collections.deque()
|
||||
value_queue.append(templates_data)
|
||||
while value_queue:
|
||||
item = value_queue.popleft()
|
||||
if not isinstance(item, dict):
|
||||
continue
|
||||
|
||||
for key in tuple(item.keys()):
|
||||
value = item[key]
|
||||
if isinstance(value, dict):
|
||||
value_queue.append(value)
|
||||
|
||||
elif isinstance(value, StringType):
|
||||
item[key] = value.replace("{task}", "{task[name]}")
|
||||
return anatomy_data
|
||||
|
||||
def reset(self):
|
||||
"""Reset values of cached data in templates and roots objects."""
|
||||
self._data = get_anatomy_settings(self.project_name)
|
||||
self._data = self._prepare_anatomy_data(
|
||||
get_anatomy_settings(self.project_name, self._site_name)
|
||||
)
|
||||
self.templates_obj.reset()
|
||||
self.roots_obj.reset()
|
||||
|
||||
|
|
@ -981,6 +1010,14 @@ class Templates:
|
|||
TemplateResult: Filled or partially filled template containing all
|
||||
data needed or missing for filling template.
|
||||
"""
|
||||
task_data = data.get("task")
|
||||
if (
|
||||
isinstance(task_data, StringType)
|
||||
and "{task[name]}" in orig_template
|
||||
):
|
||||
# Change task to dictionary if template expect dictionary
|
||||
data["task"] = {"name": task_data}
|
||||
|
||||
template, missing_optional, invalid_optional = (
|
||||
self._filter_optional(orig_template, data)
|
||||
)
|
||||
|
|
@ -990,13 +1027,6 @@ class Templates:
|
|||
missing_required = []
|
||||
replace_keys = []
|
||||
|
||||
task_data = data.get("task")
|
||||
if (
|
||||
isinstance(task_data, StringType)
|
||||
and "{task[name]}" in orig_template
|
||||
):
|
||||
data["task"] = {"name": task_data}
|
||||
|
||||
for group in self.key_pattern.findall(template):
|
||||
orig_key = group[1:-1]
|
||||
key = str(orig_key)
|
||||
|
|
|
|||
|
|
@ -271,9 +271,17 @@ def get_linked_asset_ids(asset_doc):
|
|||
if not asset_doc:
|
||||
return output
|
||||
|
||||
input_links = asset_doc["data"].get("inputsLinks") or []
|
||||
input_links = asset_doc["data"].get("inputLinks") or []
|
||||
if input_links:
|
||||
output = [item["_id"] for item in input_links]
|
||||
for item in input_links:
|
||||
# Backwards compatibility for "_id" key which was replaced with
|
||||
# "id"
|
||||
if "_id" in item:
|
||||
link_id = item["_id"]
|
||||
else:
|
||||
link_id = item["id"]
|
||||
output.append(link_id)
|
||||
|
||||
return output
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -60,12 +60,13 @@ def path_from_representation(representation, anatomy):
|
|||
path = pipeline.format_template_with_optional_keys(
|
||||
context, template
|
||||
)
|
||||
path = os.path.normpath(path.replace("/", "\\"))
|
||||
|
||||
except KeyError:
|
||||
# Template references unavailable data
|
||||
return None
|
||||
|
||||
return os.path.normpath(path)
|
||||
return path
|
||||
|
||||
|
||||
def copy_file(src_path, dst_path):
|
||||
|
|
@ -179,9 +180,11 @@ def process_single_file(
|
|||
Returns:
|
||||
(collections.defaultdict , int)
|
||||
"""
|
||||
# Make sure path is valid for all platforms
|
||||
src_path = os.path.normpath(src_path.replace("\\", "/"))
|
||||
|
||||
if not os.path.exists(src_path):
|
||||
msg = "{} doesn't exist for {}".format(src_path,
|
||||
repre["_id"])
|
||||
msg = "{} doesn't exist for {}".format(src_path, repre["_id"])
|
||||
report_items["Source file was not found"].append(msg)
|
||||
return report_items, 0
|
||||
|
||||
|
|
@ -192,8 +195,10 @@ def process_single_file(
|
|||
else:
|
||||
delivery_path = anatomy_filled["delivery"][template_name]
|
||||
|
||||
# context.representation could be .psd
|
||||
# Backwards compatibility when extension contained `.`
|
||||
delivery_path = delivery_path.replace("..", ".")
|
||||
# Make sure path is valid for all platforms
|
||||
delivery_path = os.path.normpath(delivery_path.replace("\\", "/"))
|
||||
|
||||
delivery_folder = os.path.dirname(delivery_path)
|
||||
if not os.path.exists(delivery_folder):
|
||||
|
|
@ -230,14 +235,14 @@ def process_sequence(
|
|||
Returns:
|
||||
(collections.defaultdict , int)
|
||||
"""
|
||||
src_path = os.path.normpath(src_path.replace("\\", "/"))
|
||||
|
||||
def hash_path_exist(myPath):
|
||||
res = myPath.replace('#', '*')
|
||||
glob_search_results = glob.glob(res)
|
||||
if len(glob_search_results) > 0:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
return False
|
||||
|
||||
if not hash_path_exist(src_path):
|
||||
msg = "{} doesn't exist for {}".format(src_path,
|
||||
|
|
@ -307,6 +312,7 @@ def process_sequence(
|
|||
else:
|
||||
delivery_path = anatomy_filled["delivery"][template_name]
|
||||
|
||||
delivery_path = os.path.normpath(delivery_path.replace("\\", "/"))
|
||||
delivery_folder = os.path.dirname(delivery_path)
|
||||
dst_head, dst_tail = delivery_path.split(frame_indicator)
|
||||
dst_padding = src_collection.padding
|
||||
|
|
|
|||
|
|
@ -124,7 +124,7 @@ def run_subprocess(*args, **kwargs):
|
|||
if full_output:
|
||||
full_output += "\n"
|
||||
full_output += _stderr
|
||||
logger.warning(_stderr)
|
||||
logger.info(_stderr)
|
||||
|
||||
if proc.returncode != 0:
|
||||
exc_msg = "Executing arguments was not successful: \"{}\"".format(args)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import os
|
||||
import json
|
||||
|
||||
import requests
|
||||
import hou
|
||||
|
||||
from avalon import api, io
|
||||
from avalon.vendor import requests
|
||||
|
||||
import pyblish.api
|
||||
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ import os
|
|||
import json
|
||||
import getpass
|
||||
|
||||
import requests
|
||||
from avalon import api
|
||||
from avalon.vendor import requests
|
||||
|
||||
import pyblish.api
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
import os
|
||||
import re
|
||||
import json
|
||||
import getpass
|
||||
|
||||
import requests
|
||||
|
||||
from avalon import api
|
||||
from avalon.vendor import requests
|
||||
import re
|
||||
import pyblish.api
|
||||
import nuke
|
||||
|
||||
|
|
|
|||
|
|
@ -5,10 +5,11 @@ import os
|
|||
import json
|
||||
import re
|
||||
from copy import copy, deepcopy
|
||||
import requests
|
||||
import clique
|
||||
import openpype.api
|
||||
|
||||
from avalon import api, io
|
||||
from avalon.vendor import requests, clique
|
||||
|
||||
import pyblish.api
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import pyblish.api
|
||||
|
||||
from avalon.vendor import requests
|
||||
import os
|
||||
import requests
|
||||
|
||||
import pyblish.api
|
||||
|
||||
|
||||
class ValidateDeadlineConnection(pyblish.api.InstancePlugin):
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import os
|
||||
import json
|
||||
import pyblish.api
|
||||
import requests
|
||||
|
||||
from avalon.vendor import requests
|
||||
import pyblish.api
|
||||
|
||||
from openpype.lib.abstract_submit_deadline import requests_get
|
||||
from openpype.lib.delivery import collect_frames
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ class SyncLinksToAvalon(BaseEvent):
|
|||
continue
|
||||
|
||||
links.append({
|
||||
"_id": ObjectId(link_mongo_id),
|
||||
"id": ObjectId(link_mongo_id),
|
||||
"linkedBy": "ftrack",
|
||||
"type": "breakdown"
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1479,7 +1479,7 @@ class SyncEntitiesFactory:
|
|||
mongo_id = self.ftrack_avalon_mapper.get(ftrack_link_id)
|
||||
if mongo_id is not None:
|
||||
input_links.append({
|
||||
"_id": ObjectId(mongo_id),
|
||||
"id": ObjectId(mongo_id),
|
||||
"linkedBy": "ftrack",
|
||||
"type": "breakdown"
|
||||
})
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ def collect(root,
|
|||
frame_end=None):
|
||||
"""Collect sequence collections in root"""
|
||||
|
||||
from avalon.vendor import clique
|
||||
import clique
|
||||
|
||||
files = []
|
||||
for filename in os.listdir(root):
|
||||
|
|
|
|||
|
|
@ -56,6 +56,13 @@ representation.files.sites:
|
|||
`db.getCollection('MY_PROJECT').update({type:"representation"},
|
||||
{$set:{"files.$[].sites.MY_CONFIGURED_REMOTE_SITE" : {}}}, true, true)`
|
||||
|
||||
I want to create new custom provider:
|
||||
-----------------------------------
|
||||
- take `providers\abstract_provider.py` as a base class
|
||||
- create provider class in `providers` with a name according to a provider (eg. 'gdrive.py' for gdrive provider etc.)
|
||||
- upload provider icon in png format, 24x24, into `providers\resources`, its name must follow name of provider (eg. 'gdrive.png' for gdrive provider)
|
||||
- register new provider into `providers.lib.py`, test how many files could be manipulated at same time, check provider's API for limits
|
||||
|
||||
Needed configuration:
|
||||
--------------------
|
||||
`pype/settings/defaults/project_settings/global.json`.`sync_server`:
|
||||
|
|
|
|||
|
|
@ -95,8 +95,10 @@ class TimersManager(OpenPypeModule, ITrayService):
|
|||
message_time = int(timers_settings["message_time"] * 60)
|
||||
|
||||
auto_stop = timers_settings["auto_stop"]
|
||||
platform_name = platform.system().lower()
|
||||
# Turn of auto stop on MacOs because pynput requires root permissions
|
||||
if platform.system().lower() == "darwin" or full_time <= 0:
|
||||
# and on linux can cause thread locks on application close
|
||||
if full_time <= 0 or platform_name in ("darwin", "linux"):
|
||||
auto_stop = False
|
||||
|
||||
self.auto_stop = auto_stop
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ class CopyFile(api.Loader):
|
|||
|
||||
@staticmethod
|
||||
def copy_file_to_clipboard(path):
|
||||
from avalon.vendor.Qt import QtCore, QtWidgets
|
||||
from Qt import QtCore, QtWidgets
|
||||
|
||||
clipboard = QtWidgets.QApplication.clipboard()
|
||||
assert clipboard, "Must have running QApplication instance"
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ class CopyFilePath(api.Loader):
|
|||
|
||||
@staticmethod
|
||||
def copy_path_to_clipboard(path):
|
||||
from avalon.vendor.Qt import QtWidgets
|
||||
from Qt import QtWidgets
|
||||
|
||||
clipboard = QtWidgets.QApplication.clipboard()
|
||||
assert clipboard, "Must have running QApplication instance"
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@ import uuid
|
|||
import clique
|
||||
from pymongo import UpdateOne
|
||||
import ftrack_api
|
||||
from Qt import QtWidgets, QtCore
|
||||
|
||||
from avalon import api, style
|
||||
from avalon.vendor.Qt import QtWidgets, QtCore
|
||||
from avalon.vendor import qargparse
|
||||
from avalon.api import AvalonMongoDB
|
||||
import avalon.pipeline
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ class OpenInDJV(api.Loader):
|
|||
|
||||
def load(self, context, name, namespace, data):
|
||||
directory = os.path.dirname(self.fname)
|
||||
from avalon.vendor import clique
|
||||
import clique
|
||||
|
||||
pattern = clique.PATTERNS["frames"]
|
||||
files = os.listdir(directory)
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ class Openfile(api.Loader):
|
|||
color = "orange"
|
||||
|
||||
def load(self, context, name, namespace, data):
|
||||
from avalon.vendor import clique
|
||||
import clique
|
||||
|
||||
directory = os.path.dirname(self.fname)
|
||||
pattern = clique.PATTERNS["frames"]
|
||||
|
|
|
|||
|
|
@ -1062,10 +1062,11 @@ class ExtractReview(pyblish.api.InstancePlugin):
|
|||
streams = ffprobe_streams(
|
||||
full_input_path_single_file, self.log
|
||||
)
|
||||
except Exception:
|
||||
except Exception as exc:
|
||||
raise AssertionError((
|
||||
"FFprobe couldn't read information about input file: \"{}\""
|
||||
).format(full_input_path_single_file))
|
||||
"FFprobe couldn't read information about input file: \"{}\"."
|
||||
" Error message: {}"
|
||||
).format(full_input_path_single_file, str(exc)))
|
||||
|
||||
# Try to find first stream with defined 'width' and 'height'
|
||||
# - this is to avoid order of streams where audio can be as first
|
||||
|
|
|
|||
|
|
@ -81,7 +81,8 @@ class IntegrateInputLinks(pyblish.api.ContextPlugin):
|
|||
version_doc=instance.data["versionEntity"],
|
||||
)
|
||||
|
||||
publishing.append(workfile)
|
||||
if workfile is not None:
|
||||
publishing.append(workfile)
|
||||
self.write_links_to_database(publishing)
|
||||
|
||||
def add_link(self, link_type, input_id, version_doc):
|
||||
|
|
@ -103,7 +104,7 @@ class IntegrateInputLinks(pyblish.api.ContextPlugin):
|
|||
# future.
|
||||
link = OrderedDict()
|
||||
link["type"] = link_type
|
||||
link["input"] = io.ObjectId(input_id)
|
||||
link["id"] = io.ObjectId(input_id)
|
||||
link["linkedBy"] = "publish"
|
||||
|
||||
if "inputLinks" not in version_doc["data"]:
|
||||
|
|
|
|||
|
|
@ -24,6 +24,9 @@
|
|||
{
|
||||
"nukenodes": "nukenodes"
|
||||
},
|
||||
{
|
||||
"model": "model"
|
||||
},
|
||||
{
|
||||
"camera": "camera"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -59,7 +59,12 @@
|
|||
"color-selected": "#F0F2F5",
|
||||
"color-hover": "#F0F2F5"
|
||||
},
|
||||
|
||||
"nice-checkbox": {
|
||||
"bg-checked": "#56a06f",
|
||||
"bg-unchecked": "#434b56",
|
||||
"bg-checker": "#D3D8DE",
|
||||
"bg-checker-hover": "#F0F2F5"
|
||||
},
|
||||
"loader": {
|
||||
"asset-view": {
|
||||
"selected": "rgba(168, 175, 189, 0.6)",
|
||||
|
|
@ -79,6 +84,34 @@
|
|||
"bg-expander-hover": "#2d6c9f",
|
||||
"bg-expander-selected-hover": "#3784c5"
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"invalid-light": "#C93636",
|
||||
"invalid-dark": "#AD2E2E",
|
||||
|
||||
"modified-light": "#46b1f3",
|
||||
"modified-mid": "#189AEA",
|
||||
"modified-dark": "#106AA2",
|
||||
|
||||
"studio-light": "#73C990",
|
||||
"studio-dark": "#56a06f",
|
||||
"studio-label-hover": "#FFFFFF",
|
||||
|
||||
"project-light": "#FFA64D",
|
||||
"project-mid": "#FF8C1A",
|
||||
"project-dark": "#E67300",
|
||||
|
||||
"label-fg": "#969b9e",
|
||||
"label-fg-hover": "#b8c1c5",
|
||||
|
||||
"breadcrumbs-btn-bg": "rgba(127, 127, 127, 60)",
|
||||
"breadcrumbs-btn-bg-hover": "rgba(127, 127, 127, 90)",
|
||||
|
||||
"content-hightlighted": "rgba(19, 26, 32, 15)",
|
||||
"focus-border": "#839caf",
|
||||
"image-btn": "#bfccd6",
|
||||
"image-btn-hover": "#189aea",
|
||||
"image-btn-disabled": "#bfccd6"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -660,15 +660,6 @@ QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {
|
|||
background: none;
|
||||
}
|
||||
|
||||
/* Globally used names */
|
||||
#Separator {
|
||||
background: {color:bg-menu-separator};
|
||||
}
|
||||
|
||||
#IconButton {
|
||||
padding: 4px 4px 4px 4px;
|
||||
}
|
||||
|
||||
/* Password dialog*/
|
||||
#PasswordBtn {
|
||||
border: none;
|
||||
|
|
@ -971,7 +962,235 @@ QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {
|
|||
background: transparent;
|
||||
}
|
||||
|
||||
/* Settings - NOT USED YET
|
||||
- we need to define font family for settings UI */
|
||||
|
||||
#SettingsMainWidget {
|
||||
background: #141a1f;
|
||||
}
|
||||
/* Change focus borders. */
|
||||
#SettingsMainWidget QAbstractSpinBox:focus, #SettingsMainWidget QLineEdit:focus, #SettingsMainWidget QPlainTextEdit:focus, #SettingsMainWidget QTextEdit:focus {
|
||||
border-color: {color:settings:focus-border};
|
||||
}
|
||||
/* Modify tab widget for settings */
|
||||
#SettingsMainWidget QTabWidget::pane {
|
||||
border-top-style: none;
|
||||
}
|
||||
|
||||
#SettingsMainWidget QTabBar {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
#SettingsMainWidget QTabBar::tab {
|
||||
border: none;
|
||||
border-top-left-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
#SettingsMainWidget QTabBar::tab:selected {
|
||||
background: {color:bg};
|
||||
border-color: #9B9B9B;
|
||||
border-bottom-color: #C2C7CB;
|
||||
}
|
||||
|
||||
#SettingsMainWidget QTabBar::tab:!selected {
|
||||
margin-top: 2px;
|
||||
background: #21252B;
|
||||
}
|
||||
|
||||
#SettingsMainWidget QTabBar::tab:!selected:hover {
|
||||
background: #333840;
|
||||
}
|
||||
|
||||
#SettingsMainWidget QTabBar::tab:first:selected {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
#SettingsMainWidget QTabBar::tab:last:selected {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
#SettingsMainWidget QTabBar::tab:only-one {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#SettingsToolIconBtn {
|
||||
border: 0px solid #bfccd6;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
#SettingsToolBtn {
|
||||
border: 1px solid #bfccd6;
|
||||
border-radius: 10px;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
#SettingsToolBtn:hover {
|
||||
border-color: #189aea;
|
||||
color: {color:settings:modified-light};
|
||||
background-color: transparent;
|
||||
}
|
||||
#SettingsToolBtn:disabled {
|
||||
background-color: #464b54;
|
||||
}
|
||||
|
||||
#ExpandToggleBtn {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
#SettingsLabel {
|
||||
background: transparent;
|
||||
color: {color:settings:label-fg};
|
||||
}
|
||||
#SettingsLabel:hover {color: {color:settings:label-fg-hover};}
|
||||
#SettingsLabel[state="studio"] {color: {color:settings:studio-light};}
|
||||
#SettingsLabel[state="studio"]:hover {color: {color:settings:studio-label-hover};}
|
||||
#SettingsLabel[state="modified"] {color: {color:settings:modified-mid};}
|
||||
#SettingsLabel[state="modified"]:hover {color: {color:settings:modified-light};}
|
||||
#SettingsLabel[state="overriden-modified"] {color: {color:settings:modified-mid};}
|
||||
#SettingsLabel[state="overriden-modified"]:hover {color: {color:settings:modified-light};}
|
||||
#SettingsLabel[state="overriden"] {color: {color:settings:project-mid};}
|
||||
#SettingsLabel[state="overriden"]:hover {color: {color:settings:project-light};}
|
||||
#SettingsLabel[state="invalid"] {color:{color:settings:invalid-dark};}
|
||||
#SettingsLabel[state="invalid"]:hover {color: {color:settings:invalid-dark};}
|
||||
|
||||
/* TODO Replace these with explicit widget types if possible */
|
||||
#SettingsMainWidget QWidget[input-state="modified"] {
|
||||
border-color: {color:settings:modified-mid};
|
||||
}
|
||||
#SettingsMainWidget QWidget[input-state="overriden-modified"] {
|
||||
border-color: {color:settings:modified-mid};
|
||||
}
|
||||
#SettingsMainWidget QWidget[input-state="overriden"] {
|
||||
border-color: {color:settings:project-mid};
|
||||
}
|
||||
#SettingsMainWidget QWidget[input-state="invalid"] {
|
||||
border-color: {color:settings:invalid-dark};
|
||||
}
|
||||
|
||||
#GroupWidget {
|
||||
border-bottom: 1px solid #21252B;
|
||||
}
|
||||
|
||||
#ProjectListWidget QLabel {
|
||||
background: transparent;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#MultiSelectionComboBox {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
#DictKey[state="modified"] {border-color: {color:settings:modified-mid};}
|
||||
#DictKey[state="invalid"] {border-color: {color:settings:invalid-dark};}
|
||||
|
||||
#ExpandLabel {
|
||||
font-weight: bold;
|
||||
color: {color:settings:label-fg};
|
||||
}
|
||||
#ExpandLabel:hover {
|
||||
color: {color:settings:label-fg-hover};
|
||||
}
|
||||
|
||||
#ContentWidget {
|
||||
background-color: transparent;
|
||||
}
|
||||
#ContentWidget[content_state="hightlighted"] {
|
||||
background-color: {color:settings:content-hightlighted};
|
||||
}
|
||||
|
||||
#SideLineWidget {
|
||||
background-color: #333942;
|
||||
border-style: solid;
|
||||
border-color: #4e5254;
|
||||
border-left-width: 3px;
|
||||
border-bottom-width: 0px;
|
||||
border-right-width: 0px;
|
||||
border-top-width: 0px;
|
||||
}
|
||||
|
||||
#SideLineWidget:hover {
|
||||
border-color: #7d8386;
|
||||
}
|
||||
|
||||
#SideLineWidget[state="child-studio"] {border-color: {color:settings:studio-dark};}
|
||||
#SideLineWidget[state="child-studio"]:hover {border-color: {color:settings:studio-light};}
|
||||
|
||||
#SideLineWidget[state="child-modified"] {border-color: {color:settings:modified-dark};}
|
||||
#SideLineWidget[state="child-modified"]:hover {border-color: {color:settings:modified-mid};}
|
||||
|
||||
#SideLineWidget[state="child-invalid"] {border-color: {color:settings:invalid-dark};}
|
||||
#SideLineWidget[state="child-invalid"]:hover {border-color: {color:settings:invalid-light};}
|
||||
|
||||
#SideLineWidget[state="child-overriden"] {border-color: {color:settings:project-dark};}
|
||||
#SideLineWidget[state="child-overriden"]:hover {border-color: {color:settings:project-mid};}
|
||||
|
||||
#SideLineWidget[state="child-overriden-modified"] {border-color: {color:settings:modified-dark};}
|
||||
#SideLineWidget[state="child-overriden-modified"]:hover {border-color: {color:settings:modified-mid};}
|
||||
|
||||
#DictAsWidgetBody {
|
||||
background: transparent;
|
||||
}
|
||||
#DictAsWidgetBody[show_borders="1"] {
|
||||
border: 1px solid #4e5254;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
#ShadowWidget {
|
||||
font-size: 36pt;
|
||||
}
|
||||
|
||||
#BreadcrumbsPathInput {
|
||||
padding: 2px;
|
||||
font-size: 9pt;
|
||||
}
|
||||
|
||||
#BreadcrumbsButton {
|
||||
padding-right: 12px;
|
||||
font-size: 9pt;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
#BreadcrumbsButton[empty="1"] {
|
||||
padding-right: 0px;
|
||||
}
|
||||
|
||||
#BreadcrumbsButton::menu-button {
|
||||
border: none;
|
||||
width: 12px;
|
||||
background: {color:settings:breadcrumbs-btn-bg};
|
||||
}
|
||||
#BreadcrumbsButton::menu-button:hover {
|
||||
background: {color:settings:breadcrumbs-btn-bg-hover};
|
||||
}
|
||||
|
||||
#BreadcrumbsPanel {
|
||||
border: 1px solid #4e5254;
|
||||
border-radius: 5px;
|
||||
background: #21252B;
|
||||
}
|
||||
|
||||
/* Globally used names */
|
||||
#Separator {
|
||||
background: {color:bg-menu-separator};
|
||||
}
|
||||
|
||||
#IconButton {
|
||||
padding: 4px 4px 4px 4px;
|
||||
}
|
||||
|
||||
#NiceCheckbox {
|
||||
/* Default size hint of NiceCheckbox is defined by font size. */
|
||||
font-size: 7pt;
|
||||
}
|
||||
|
||||
#ImageButton {
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
font-size: 11pt;
|
||||
}
|
||||
|
||||
#ImageButton:disabled {
|
||||
background: {color:bg-buttons-disabled};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
import re
|
||||
import logging
|
||||
import collections
|
||||
|
||||
from avalon.vendor.Qt import QtCore, QtWidgets
|
||||
from Qt import QtCore, QtWidgets
|
||||
from avalon.vendor import qtawesome
|
||||
from avalon import io
|
||||
from avalon import style
|
||||
|
|
|
|||
|
|
@ -37,8 +37,13 @@ class SimpleLinkView(QtWidgets.QWidget):
|
|||
# inputs
|
||||
#
|
||||
for link in version_doc["data"].get("inputLinks", []):
|
||||
# Backwards compatibility for "input" key used as "id"
|
||||
if "id" not in link:
|
||||
link_id = link["input"]
|
||||
else:
|
||||
link_id = link["id"]
|
||||
version = self.dbcon.find_one(
|
||||
{"_id": link["input"], "type": "version"},
|
||||
{"_id": link_id, "type": "version"},
|
||||
projection={"name": 1, "parent": 1}
|
||||
)
|
||||
if not version:
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
import sys
|
||||
from Qt import QtWidgets, QtGui
|
||||
|
||||
from openpype import style
|
||||
from .lib import (
|
||||
BTN_FIXED_SIZE,
|
||||
CHILD_OFFSET
|
||||
)
|
||||
from .local_settings import LocalSettingsWindow
|
||||
from .settings import (
|
||||
style,
|
||||
MainWidget,
|
||||
ProjectListWidget
|
||||
)
|
||||
|
|
@ -36,8 +37,6 @@ __all__ = (
|
|||
"BTN_FIXED_SIZE",
|
||||
"CHILD_OFFSET",
|
||||
|
||||
"style",
|
||||
|
||||
"MainWidget",
|
||||
"ProjectListWidget",
|
||||
"LocalSettingsWindow",
|
||||
|
|
|
|||
|
|
@ -172,7 +172,9 @@ class LocalApplicationsWidgets(QtWidgets.QWidget):
|
|||
def _reset_app_widgets(self):
|
||||
while self.content_layout.count() > 0:
|
||||
item = self.content_layout.itemAt(0)
|
||||
item.widget().hide()
|
||||
widget = item.widget()
|
||||
if widget is not None:
|
||||
widget.setVisible(False)
|
||||
self.content_layout.removeItem(item)
|
||||
self.widgets_by_group_name.clear()
|
||||
|
||||
|
|
|
|||
|
|
@ -6,10 +6,7 @@ from openpype.settings.constants import (
|
|||
PROJECT_ANATOMY_KEY,
|
||||
DEFAULT_PROJECT_KEY
|
||||
)
|
||||
from .widgets import (
|
||||
SpacerWidget,
|
||||
ProxyLabelWidget
|
||||
)
|
||||
from .widgets import ProxyLabelWidget
|
||||
from .constants import (
|
||||
LABEL_REMOVE_DEFAULT,
|
||||
LABEL_ADD_DEFAULT,
|
||||
|
|
@ -238,9 +235,9 @@ class SitesWidget(QtWidgets.QWidget):
|
|||
|
||||
comboboxes_layout = QtWidgets.QHBoxLayout(comboboxes_widget)
|
||||
comboboxes_layout.setContentsMargins(0, 0, 0, 0)
|
||||
comboboxes_layout.addWidget(active_site_widget)
|
||||
comboboxes_layout.addWidget(remote_site_widget)
|
||||
comboboxes_layout.addWidget(SpacerWidget(comboboxes_widget), 1)
|
||||
comboboxes_layout.addWidget(active_site_widget, 0)
|
||||
comboboxes_layout.addWidget(remote_site_widget, 0)
|
||||
comboboxes_layout.addStretch(1)
|
||||
|
||||
content_widget = QtWidgets.QWidget(self)
|
||||
content_layout = QtWidgets.QVBoxLayout(content_widget)
|
||||
|
|
@ -259,7 +256,9 @@ class SitesWidget(QtWidgets.QWidget):
|
|||
def _clear_widgets(self):
|
||||
while self.content_layout.count():
|
||||
item = self.content_layout.itemAt(0)
|
||||
item.widget().hide()
|
||||
widget = item.widget()
|
||||
if widget is not None:
|
||||
widget.setVisible(False)
|
||||
self.content_layout.removeItem(item)
|
||||
self.input_objects = {}
|
||||
|
||||
|
|
@ -383,7 +382,7 @@ class SitesWidget(QtWidgets.QWidget):
|
|||
self.input_objects[site_name] = site_input_objects
|
||||
|
||||
# Add spacer so other widgets are squeezed to top
|
||||
self.content_layout.addWidget(SpacerWidget(self), 1)
|
||||
self.content_layout.addStretch(1)
|
||||
|
||||
def _on_input_value_change(self, site_name, key):
|
||||
if (
|
||||
|
|
@ -456,6 +455,8 @@ class _SiteCombobox(QtWidgets.QWidget):
|
|||
self
|
||||
)
|
||||
combobox_input = QtWidgets.QComboBox(self)
|
||||
combobox_delegate = QtWidgets.QStyledItemDelegate()
|
||||
combobox_input.setItemDelegate(combobox_delegate)
|
||||
|
||||
main_layout = QtWidgets.QHBoxLayout(self)
|
||||
main_layout.addWidget(label_widget)
|
||||
|
|
@ -464,6 +465,7 @@ class _SiteCombobox(QtWidgets.QWidget):
|
|||
combobox_input.currentIndexChanged.connect(self._on_index_change)
|
||||
self.label_widget = label_widget
|
||||
self.combobox_input = combobox_input
|
||||
self._combobox_delegate = combobox_delegate
|
||||
|
||||
def _set_current_text(self, text):
|
||||
index = None
|
||||
|
|
@ -777,7 +779,7 @@ class RootSiteWidget(QtWidgets.QWidget):
|
|||
|
||||
main_layout = QtWidgets.QVBoxLayout(self)
|
||||
main_layout.addWidget(sites_widget)
|
||||
main_layout.addWidget(SpacerWidget(self), 1)
|
||||
main_layout.addStretch(1)
|
||||
|
||||
self.sites_widget = sites_widget
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
from Qt import QtWidgets, QtCore
|
||||
from openpype.tools.settings.settings.widgets import (
|
||||
ExpandingWidget,
|
||||
SpacerWidget
|
||||
ExpandingWidget
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -56,7 +55,5 @@ class ProxyLabelWidget(QtWidgets.QWidget):
|
|||
|
||||
__all__ = (
|
||||
"ExpandingWidget",
|
||||
"SpacerWidget",
|
||||
"Separator",
|
||||
"SpacerWidget"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import logging
|
||||
from Qt import QtWidgets, QtGui
|
||||
|
||||
from ..settings import style
|
||||
from openpype import style
|
||||
|
||||
from openpype.settings.lib import (
|
||||
get_local_settings,
|
||||
|
|
@ -15,7 +15,6 @@ from openpype.api import (
|
|||
from openpype.modules import ModulesManager
|
||||
|
||||
from .widgets import (
|
||||
SpacerWidget,
|
||||
ExpandingWidget
|
||||
)
|
||||
from .mongo_widget import OpenPypeMongoWidget
|
||||
|
|
@ -58,8 +57,7 @@ class LocalSettingsWidget(QtWidgets.QWidget):
|
|||
self._create_app_ui()
|
||||
self._create_project_ui()
|
||||
|
||||
# Add spacer to main layout
|
||||
self.main_layout.addWidget(SpacerWidget(self), 1)
|
||||
self.main_layout.addStretch(1)
|
||||
|
||||
def _create_pype_mongo_ui(self):
|
||||
pype_mongo_expand_widget = ExpandingWidget("OpenPype Mongo URL", self)
|
||||
|
|
@ -210,7 +208,7 @@ class LocalSettingsWindow(QtWidgets.QWidget):
|
|||
|
||||
footer_layout = QtWidgets.QHBoxLayout(footer)
|
||||
footer_layout.addWidget(reset_btn, 0)
|
||||
footer_layout.addWidget(SpacerWidget(footer), 1)
|
||||
footer_layout.addStretch(1)
|
||||
footer_layout.addWidget(save_btn, 0)
|
||||
|
||||
main_layout = QtWidgets.QVBoxLayout(self)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
from . import style
|
||||
from .window import MainWidget
|
||||
from .widgets import ProjectListWidget
|
||||
|
||||
|
||||
__all__ = (
|
||||
"style",
|
||||
"MainWidget",
|
||||
"ProjectListWidget"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -470,10 +470,9 @@ class GUIWidget(BaseWidget):
|
|||
self.entity_widget.add_widget_to_layout(self)
|
||||
|
||||
def _create_label_ui(self):
|
||||
self.setObjectName("LabelWidget")
|
||||
|
||||
label = self.entity["label"]
|
||||
label_widget = QtWidgets.QLabel(label, self)
|
||||
label_widget.setObjectName("SettingsLabel")
|
||||
|
||||
layout = QtWidgets.QHBoxLayout(self)
|
||||
layout.setContentsMargins(0, 5, 0, 5)
|
||||
|
|
@ -481,7 +480,7 @@ class GUIWidget(BaseWidget):
|
|||
|
||||
def _create_separator_ui(self):
|
||||
splitter_item = QtWidgets.QWidget(self)
|
||||
splitter_item.setObjectName("SplitterItem")
|
||||
splitter_item.setObjectName("Separator")
|
||||
splitter_item.setMinimumHeight(self.separator_height)
|
||||
splitter_item.setMaximumHeight(self.separator_height)
|
||||
|
||||
|
|
@ -513,10 +512,9 @@ class MockUpWidget(BaseWidget):
|
|||
child_invalid = False
|
||||
|
||||
def create_ui(self):
|
||||
self.setObjectName("LabelWidget")
|
||||
|
||||
label = "Mockup widget for entity {}".format(self.entity.path)
|
||||
label_widget = QtWidgets.QLabel(label, self)
|
||||
label_widget.setObjectName("SettingsLabel")
|
||||
|
||||
layout = QtWidgets.QHBoxLayout(self)
|
||||
layout.setContentsMargins(0, 5, 0, 5)
|
||||
|
|
|
|||
|
|
@ -391,7 +391,9 @@ class SettingsCategoryWidget(QtWidgets.QWidget):
|
|||
|
||||
while self.content_layout.count() != 0:
|
||||
widget = self.content_layout.itemAt(0).widget()
|
||||
widget.hide()
|
||||
if widget is not None:
|
||||
widget.setVisible(False)
|
||||
|
||||
self.content_layout.removeWidget(widget)
|
||||
widget.deleteLater()
|
||||
|
||||
|
|
|
|||
|
|
@ -164,6 +164,7 @@ class DictConditionalWidget(BaseWidget):
|
|||
content_widget.setProperty("show_borders", show_borders)
|
||||
|
||||
label_widget = QtWidgets.QLabel(self.entity.label)
|
||||
label_widget.setObjectName("SettingsLabel")
|
||||
|
||||
content_layout = QtWidgets.QGridLayout(content_widget)
|
||||
content_layout.setContentsMargins(5, 5, 5, 5)
|
||||
|
|
|
|||
|
|
@ -3,7 +3,12 @@ from uuid import uuid4
|
|||
from Qt import QtWidgets, QtCore, QtGui
|
||||
|
||||
from .base import BaseWidget
|
||||
from .lib import create_deffered_value_change_timer
|
||||
from .lib import (
|
||||
create_deffered_value_change_timer,
|
||||
create_add_btn,
|
||||
create_remove_btn,
|
||||
create_confirm_btn
|
||||
)
|
||||
from .widgets import (
|
||||
ExpandingWidget,
|
||||
IconButton
|
||||
|
|
@ -21,92 +26,6 @@ KEY_INPUT_TOOLTIP = (
|
|||
)
|
||||
|
||||
|
||||
class PaintHelper:
|
||||
cached_icons = {}
|
||||
|
||||
@classmethod
|
||||
def _draw_image(cls, width, height, brush):
|
||||
image = QtGui.QPixmap(width, height)
|
||||
image.fill(QtCore.Qt.transparent)
|
||||
|
||||
icon_path_stroker = QtGui.QPainterPathStroker()
|
||||
icon_path_stroker.setCapStyle(QtCore.Qt.RoundCap)
|
||||
icon_path_stroker.setJoinStyle(QtCore.Qt.RoundJoin)
|
||||
icon_path_stroker.setWidth(height / 5)
|
||||
|
||||
painter = QtGui.QPainter(image)
|
||||
painter.setPen(QtCore.Qt.transparent)
|
||||
painter.setBrush(brush)
|
||||
rect = QtCore.QRect(0, 0, image.width(), image.height())
|
||||
fifteenth = rect.height() / 15
|
||||
# Left point
|
||||
p1 = QtCore.QPoint(
|
||||
rect.x() + (5 * fifteenth),
|
||||
rect.y() + (9 * fifteenth)
|
||||
)
|
||||
# Middle bottom point
|
||||
p2 = QtCore.QPoint(
|
||||
rect.center().x(),
|
||||
rect.y() + (11 * fifteenth)
|
||||
)
|
||||
# Top right point
|
||||
p3 = QtCore.QPoint(
|
||||
rect.x() + (10 * fifteenth),
|
||||
rect.y() + (5 * fifteenth)
|
||||
)
|
||||
|
||||
path = QtGui.QPainterPath(p1)
|
||||
path.lineTo(p2)
|
||||
path.lineTo(p3)
|
||||
|
||||
stroked_path = icon_path_stroker.createStroke(path)
|
||||
painter.drawPath(stroked_path)
|
||||
|
||||
painter.end()
|
||||
|
||||
return image
|
||||
|
||||
@classmethod
|
||||
def get_confirm_icon(cls, width, height):
|
||||
key = "{}x{}-confirm_image".format(width, height)
|
||||
icon = cls.cached_icons.get(key)
|
||||
|
||||
if icon is None:
|
||||
image = cls._draw_image(width, height, QtCore.Qt.white)
|
||||
icon = QtGui.QIcon(image)
|
||||
cls.cached_icons[key] = icon
|
||||
return icon
|
||||
|
||||
|
||||
def create_add_btn(parent):
|
||||
add_btn = QtWidgets.QPushButton("+", parent)
|
||||
add_btn.setFocusPolicy(QtCore.Qt.ClickFocus)
|
||||
add_btn.setProperty("btn-type", "tool-item")
|
||||
add_btn.setFixedSize(BTN_FIXED_SIZE, BTN_FIXED_SIZE)
|
||||
return add_btn
|
||||
|
||||
|
||||
def create_remove_btn(parent):
|
||||
remove_btn = QtWidgets.QPushButton("-", parent)
|
||||
remove_btn.setFocusPolicy(QtCore.Qt.ClickFocus)
|
||||
remove_btn.setProperty("btn-type", "tool-item")
|
||||
remove_btn.setFixedSize(BTN_FIXED_SIZE, BTN_FIXED_SIZE)
|
||||
return remove_btn
|
||||
|
||||
|
||||
def create_confirm_btn(parent):
|
||||
confirm_btn = QtWidgets.QPushButton(parent)
|
||||
|
||||
icon = PaintHelper.get_confirm_icon(
|
||||
BTN_FIXED_SIZE, BTN_FIXED_SIZE
|
||||
)
|
||||
confirm_btn.setIcon(icon)
|
||||
confirm_btn.setFocusPolicy(QtCore.Qt.ClickFocus)
|
||||
confirm_btn.setProperty("btn-type", "tool-item")
|
||||
confirm_btn.setFixedSize(BTN_FIXED_SIZE, BTN_FIXED_SIZE)
|
||||
return confirm_btn
|
||||
|
||||
|
||||
class ModifiableDictEmptyItem(QtWidgets.QWidget):
|
||||
def __init__(self, entity_widget, store_as_list, parent):
|
||||
super(ModifiableDictEmptyItem, self).__init__(parent)
|
||||
|
|
@ -375,7 +294,7 @@ class ModifiableDictItem(QtWidgets.QWidget):
|
|||
"fa.edit", QtCore.Qt.lightGray, QtCore.Qt.white
|
||||
)
|
||||
edit_btn.setFocusPolicy(QtCore.Qt.ClickFocus)
|
||||
edit_btn.setProperty("btn-type", "tool-item-icon")
|
||||
edit_btn.setObjectName("SettingsToolIconBtn")
|
||||
edit_btn.setFixedHeight(BTN_FIXED_SIZE)
|
||||
|
||||
confirm_btn = create_confirm_btn(self)
|
||||
|
|
|
|||
19
openpype/tools/settings/settings/images/__init__.py
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import os
|
||||
from Qt import QtGui
|
||||
|
||||
|
||||
def get_image_path(image_filename):
|
||||
return os.path.join(
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
image_filename
|
||||
)
|
||||
|
||||
|
||||
def get_image(image_filename):
|
||||
image_path = get_image_path(image_filename)
|
||||
return QtGui.QImage(image_path)
|
||||
|
||||
|
||||
def get_pixmap(image_filename):
|
||||
image_path = get_image_path(image_filename)
|
||||
return QtGui.QPixmap(image_path)
|
||||
BIN
openpype/tools/settings/settings/images/add.png
Normal file
|
After Width: | Height: | Size: 778 B |
BIN
openpype/tools/settings/settings/images/confirm.png
Normal file
|
After Width: | Height: | Size: 853 B |
BIN
openpype/tools/settings/settings/images/down.png
Normal file
|
After Width: | Height: | Size: 820 B |
BIN
openpype/tools/settings/settings/images/mask.png
Normal file
|
After Width: | Height: | Size: 720 B |
BIN
openpype/tools/settings/settings/images/remove.png
Normal file
|
After Width: | Height: | Size: 767 B |
BIN
openpype/tools/settings/settings/images/up.png
Normal file
|
After Width: | Height: | Size: 819 B |
|
|
@ -7,8 +7,8 @@ from .widgets import (
|
|||
NumberSpinBox,
|
||||
GridLabelWidget,
|
||||
SettingsComboBox,
|
||||
NiceCheckbox,
|
||||
SettingsPlainTextEdit,
|
||||
SettingsNiceCheckbox,
|
||||
SettingsLineEdit
|
||||
)
|
||||
from .multiselection_combobox import MultiSelectionComboBox
|
||||
|
|
@ -21,6 +21,7 @@ from .base import (
|
|||
BaseWidget,
|
||||
InputWidget
|
||||
)
|
||||
|
||||
from openpype.widgets.sliders import NiceSlider
|
||||
from openpype.tools.settings import CHILD_OFFSET
|
||||
|
||||
|
|
@ -129,6 +130,7 @@ class DictImmutableKeysWidget(BaseWidget):
|
|||
content_widget.setProperty("show_borders", show_borders)
|
||||
|
||||
label_widget = QtWidgets.QLabel(self.entity.label)
|
||||
label_widget.setObjectName("SettingsLabel")
|
||||
|
||||
content_layout = QtWidgets.QGridLayout(content_widget)
|
||||
content_layout.setContentsMargins(5, 5, 5, 5)
|
||||
|
|
@ -324,12 +326,7 @@ class DictImmutableKeysWidget(BaseWidget):
|
|||
|
||||
class BoolWidget(InputWidget):
|
||||
def _add_inputs_to_layout(self):
|
||||
checkbox_height = self.style().pixelMetric(
|
||||
QtWidgets.QStyle.PM_IndicatorHeight
|
||||
)
|
||||
self.input_field = NiceCheckbox(
|
||||
height=checkbox_height, parent=self.content_widget
|
||||
)
|
||||
self.input_field = SettingsNiceCheckbox(parent=self.content_widget)
|
||||
|
||||
self.content_layout.addWidget(self.input_field, 0)
|
||||
self.content_layout.addStretch(1)
|
||||
|
|
@ -352,6 +349,9 @@ class BoolWidget(InputWidget):
|
|||
def _on_value_change(self):
|
||||
if self.ignore_input_changes:
|
||||
return
|
||||
self.start_value_timer()
|
||||
|
||||
def _on_value_change_timer(self):
|
||||
self.entity.set(self.input_field.isChecked())
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
from Qt import QtCore
|
||||
|
||||
from .widgets import SettingsToolBtn
|
||||
|
||||
# Offset of value change trigger in ms
|
||||
VALUE_CHANGE_OFFSET_MS = 300
|
||||
|
||||
|
|
@ -16,3 +18,33 @@ def create_deffered_value_change_timer(callback):
|
|||
timer.setInterval(VALUE_CHANGE_OFFSET_MS)
|
||||
timer.timeout.connect(callback)
|
||||
return timer
|
||||
|
||||
|
||||
def create_add_btn(parent):
|
||||
add_btn = SettingsToolBtn("add", parent)
|
||||
add_btn.setFocusPolicy(QtCore.Qt.ClickFocus)
|
||||
return add_btn
|
||||
|
||||
|
||||
def create_remove_btn(parent):
|
||||
remove_btn = SettingsToolBtn("remove", parent)
|
||||
remove_btn.setFocusPolicy(QtCore.Qt.ClickFocus)
|
||||
return remove_btn
|
||||
|
||||
|
||||
def create_up_btn(parent):
|
||||
remove_btn = SettingsToolBtn("up", parent)
|
||||
remove_btn.setFocusPolicy(QtCore.Qt.ClickFocus)
|
||||
return remove_btn
|
||||
|
||||
|
||||
def create_down_btn(parent):
|
||||
add_btn = SettingsToolBtn("down", parent)
|
||||
add_btn.setFocusPolicy(QtCore.Qt.ClickFocus)
|
||||
return add_btn
|
||||
|
||||
|
||||
def create_confirm_btn(parent):
|
||||
remove_btn = SettingsToolBtn("confirm", parent)
|
||||
remove_btn.setFocusPolicy(QtCore.Qt.ClickFocus)
|
||||
return remove_btn
|
||||
|
|
|
|||
|
|
@ -1,13 +1,17 @@
|
|||
from Qt import QtWidgets, QtCore
|
||||
|
||||
from .base import InputWidget
|
||||
from .widgets import ExpandingWidget
|
||||
from openpype.tools.settings import (
|
||||
BTN_FIXED_SIZE,
|
||||
CHILD_OFFSET
|
||||
)
|
||||
|
||||
from avalon.vendor import qtawesome
|
||||
from .base import InputWidget
|
||||
from .widgets import ExpandingWidget
|
||||
from .lib import (
|
||||
create_add_btn,
|
||||
create_remove_btn,
|
||||
create_up_btn,
|
||||
create_down_btn
|
||||
)
|
||||
|
||||
|
||||
class EmptyListItem(QtWidgets.QWidget):
|
||||
|
|
@ -16,18 +20,11 @@ class EmptyListItem(QtWidgets.QWidget):
|
|||
|
||||
self.entity_widget = entity_widget
|
||||
|
||||
add_btn = QtWidgets.QPushButton("+", self)
|
||||
remove_btn = QtWidgets.QPushButton("-", self)
|
||||
add_btn = create_add_btn(self)
|
||||
remove_btn = create_remove_btn(self)
|
||||
|
||||
add_btn.setFocusPolicy(QtCore.Qt.ClickFocus)
|
||||
remove_btn.setEnabled(False)
|
||||
|
||||
add_btn.setFixedSize(BTN_FIXED_SIZE, BTN_FIXED_SIZE)
|
||||
remove_btn.setFixedSize(BTN_FIXED_SIZE, BTN_FIXED_SIZE)
|
||||
|
||||
add_btn.setProperty("btn-type", "tool-item")
|
||||
remove_btn.setProperty("btn-type", "tool-item")
|
||||
|
||||
layout = QtWidgets.QHBoxLayout(self)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.setSpacing(3)
|
||||
|
|
@ -52,32 +49,10 @@ class ListItem(QtWidgets.QWidget):
|
|||
|
||||
self.ignore_input_changes = entity_widget.ignore_input_changes
|
||||
|
||||
char_up = qtawesome.charmap("fa.angle-up")
|
||||
char_down = qtawesome.charmap("fa.angle-down")
|
||||
|
||||
add_btn = QtWidgets.QPushButton("+")
|
||||
remove_btn = QtWidgets.QPushButton("-")
|
||||
up_btn = QtWidgets.QPushButton(char_up)
|
||||
down_btn = QtWidgets.QPushButton(char_down)
|
||||
|
||||
font_up_down = qtawesome.font("fa", 13)
|
||||
up_btn.setFont(font_up_down)
|
||||
down_btn.setFont(font_up_down)
|
||||
|
||||
add_btn.setFocusPolicy(QtCore.Qt.ClickFocus)
|
||||
remove_btn.setFocusPolicy(QtCore.Qt.ClickFocus)
|
||||
up_btn.setFocusPolicy(QtCore.Qt.ClickFocus)
|
||||
down_btn.setFocusPolicy(QtCore.Qt.ClickFocus)
|
||||
|
||||
add_btn.setFixedSize(BTN_FIXED_SIZE, BTN_FIXED_SIZE)
|
||||
remove_btn.setFixedSize(BTN_FIXED_SIZE, BTN_FIXED_SIZE)
|
||||
up_btn.setFixedSize(BTN_FIXED_SIZE, BTN_FIXED_SIZE)
|
||||
down_btn.setFixedSize(BTN_FIXED_SIZE, BTN_FIXED_SIZE)
|
||||
|
||||
add_btn.setProperty("btn-type", "tool-item")
|
||||
remove_btn.setProperty("btn-type", "tool-item")
|
||||
up_btn.setProperty("btn-type", "tool-item")
|
||||
down_btn.setProperty("btn-type", "tool-item")
|
||||
add_btn = create_add_btn(self)
|
||||
remove_btn = create_remove_btn(self)
|
||||
up_btn = create_up_btn(self)
|
||||
down_btn = create_down_btn(self)
|
||||
|
||||
add_btn.clicked.connect(self._on_add_clicked)
|
||||
remove_btn.clicked.connect(self._on_remove_clicked)
|
||||
|
|
|
|||
|
|
@ -1,13 +0,0 @@
|
|||
import os
|
||||
from openpype import resources
|
||||
|
||||
|
||||
def load_stylesheet():
|
||||
style_path = os.path.join(os.path.dirname(__file__), "style.css")
|
||||
with open(style_path, "r") as style_file:
|
||||
stylesheet = style_file.read()
|
||||
return stylesheet
|
||||
|
||||
|
||||
def app_icon_path():
|
||||
return resources.get_openpype_icon_filepath()
|
||||
|
|
@ -1,453 +0,0 @@
|
|||
/* :root {
|
||||
--border-color-: #464b54;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
QWidget {
|
||||
color: #bfccd6;
|
||||
background-color: #282C34;
|
||||
font-size: 12px;
|
||||
border-radius: 0px;
|
||||
}
|
||||
|
||||
QMenu {
|
||||
border: 1px solid #555555;
|
||||
background-color: #21252B;
|
||||
}
|
||||
|
||||
QMenu::item {
|
||||
padding: 5px 10px 5px 10px;
|
||||
border-left: 5px solid #313131;
|
||||
}
|
||||
|
||||
QMenu::item:selected {
|
||||
border-left-color: #61839e;
|
||||
background-color: #222d37;
|
||||
}
|
||||
QCheckBox {
|
||||
spacing: 0px;
|
||||
}
|
||||
QCheckBox::indicator {}
|
||||
QCheckBox::indicator:focus {}
|
||||
|
||||
QLineEdit, QSpinBox, QDoubleSpinBox, QPlainTextEdit, QTextEdit {
|
||||
border: 1px solid #464b54;
|
||||
border-radius: 3px;
|
||||
background-color: #21252B;
|
||||
}
|
||||
|
||||
QLineEdit:disabled, QSpinBox:disabled, QDoubleSpinBox:disabled, QPlainTextEdit:disabled, QTextEdit:disabled, QPushButton:disabled {
|
||||
background-color: #464b54;
|
||||
}
|
||||
|
||||
QLineEdit:focus, QSpinBox:focus, QDoubleSpinBox:focus, QPlainTextEdit:focus, QTextEdit:focus {
|
||||
border: 1px solid #839caf;
|
||||
}
|
||||
|
||||
QComboBox {
|
||||
border: 1px solid #464b54;
|
||||
border-radius: 3px;
|
||||
padding: 2px 2px 4px 4px;
|
||||
background: #21252B;
|
||||
}
|
||||
|
||||
QComboBox QAbstractItemView::item {
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
QToolButton {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
QLabel {
|
||||
background: transparent;
|
||||
color: #969b9e;
|
||||
}
|
||||
QLabel:hover {color: #b8c1c5;}
|
||||
|
||||
QLabel[state="studio"] {color: #73C990;}
|
||||
QLabel[state="studio"]:hover {color: #ffffff;}
|
||||
QLabel[state="modified"] {color: #189aea;}
|
||||
QLabel[state="modified"]:hover {color: #46b1f3;}
|
||||
QLabel[state="overriden-modified"] {color: #189aea;}
|
||||
QLabel[state="overriden-modified"]:hover {color: #46b1f3;}
|
||||
QLabel[state="overriden"] {color: #ff8c1a;}
|
||||
QLabel[state="overriden"]:hover {color: #ffa64d;}
|
||||
QLabel[state="invalid"] {color: #ad2e2e;}
|
||||
QLabel[state="invalid"]:hover {color: #ad2e2e;}
|
||||
|
||||
|
||||
QWidget[input-state="studio"] {border-color: #858a94;}
|
||||
QWidget[input-state="modified"] {border-color: #189aea;}
|
||||
QWidget[input-state="overriden-modified"] {border-color: #189aea;}
|
||||
QWidget[input-state="overriden"] {border-color: #ff8c1a;}
|
||||
QWidget[input-state="invalid"] {border-color: #ad2e2e;}
|
||||
|
||||
QPushButton {
|
||||
border: 1px solid #aaaaaa;
|
||||
border-radius: 3px;
|
||||
padding: 5px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #333840;
|
||||
border: 1px solid #fff;
|
||||
color: #fff;
|
||||
}
|
||||
QPushButton[btn-type="tool-item"] {
|
||||
border: 1px solid #bfccd6;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
QPushButton[btn-type="tool-item"]:hover {
|
||||
border-color: #189aea;
|
||||
color: #46b1f3;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
QPushButton[btn-type="tool-item-icon"] {
|
||||
border: 0px solid #bfccd6;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
QPushButton[btn-type="expand-toggle"] {
|
||||
background: #21252B;
|
||||
}
|
||||
|
||||
/* SLider */
|
||||
QSlider::groove {
|
||||
border: 1px solid #464b54;
|
||||
border-radius: 0.3em;
|
||||
}
|
||||
QSlider::groove:horizontal {
|
||||
height: 8px;
|
||||
}
|
||||
QSlider::groove:vertical {
|
||||
width: 8px;
|
||||
}
|
||||
QSlider::handle {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
|
||||
border-radius: 5px;
|
||||
}
|
||||
QSlider::handle:horizontal {
|
||||
margin: -2px 0;
|
||||
}
|
||||
QSlider::handle:vertical {
|
||||
margin: 0 -2px;
|
||||
}
|
||||
|
||||
#GroupWidget {
|
||||
border-bottom: 1px solid #21252B;
|
||||
}
|
||||
|
||||
#ProjectListWidget QListView {
|
||||
border: 1px solid #464b54;
|
||||
background: #21252B;
|
||||
}
|
||||
|
||||
#ProjectListWidget QListView:disabled {
|
||||
background: #282C34;
|
||||
}
|
||||
|
||||
#ProjectListWidget QListView::item:disabled {
|
||||
color: #4e5254;
|
||||
}
|
||||
|
||||
#ProjectListWidget QLabel {
|
||||
background: transparent;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#MultiSelectionComboBox {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
#DictKey[state="studio"] {border-color: #464b54;}
|
||||
#DictKey[state="modified"] {border-color: #189aea;}
|
||||
#DictKey[state="overriden"] {border-color: #00f;}
|
||||
#DictKey[state="overriden-modified"] {border-color: #0f0;}
|
||||
#DictKey[state="invalid"] {border-color: #ad2e2e;}
|
||||
|
||||
#DictLabel {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#ContentWidget {
|
||||
background-color: transparent;
|
||||
}
|
||||
#ContentWidget[content_state="hightlighted"] {
|
||||
background-color: rgba(19, 26, 32, 15%);
|
||||
}
|
||||
|
||||
#SideLineWidget {
|
||||
background-color: #333942;
|
||||
border-style: solid;
|
||||
border-color: #4e5254;
|
||||
border-left-width: 3px;
|
||||
border-bottom-width: 0px;
|
||||
border-right-width: 0px;
|
||||
border-top-width: 0px;
|
||||
}
|
||||
|
||||
#SideLineWidget:hover {
|
||||
border-color: #7d8386;
|
||||
}
|
||||
|
||||
#SideLineWidget[state="child-studio"] {border-color: #56a06f;}
|
||||
#SideLineWidget[state="child-studio"]:hover {border-color: #73C990;}
|
||||
|
||||
#SideLineWidget[state="child-modified"] {border-color: #106aa2;}
|
||||
#SideLineWidget[state="child-modified"]:hover {border-color: #189aea;}
|
||||
|
||||
#SideLineWidget[state="child-invalid"] {border-color: #ad2e2e;}
|
||||
#SideLineWidget[state="child-invalid"]:hover {border-color: #c93636;}
|
||||
|
||||
#SideLineWidget[state="child-overriden"] {border-color: #e67300;}
|
||||
#SideLineWidget[state="child-overriden"]:hover {border-color: #ff8c1a;}
|
||||
|
||||
#SideLineWidget[state="child-overriden-modified"] {border-color: #106aa2;}
|
||||
#SideLineWidget[state="child-overriden-modified"]:hover {border-color: #189aea;}
|
||||
|
||||
#MainWidget {
|
||||
background: #141a1f;
|
||||
}
|
||||
|
||||
#DictAsWidgetBody {
|
||||
background: transparent;
|
||||
}
|
||||
#DictAsWidgetBody[show_borders="1"] {
|
||||
border: 1px solid #4e5254;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
#SplitterItem {
|
||||
background-color: #21252B;
|
||||
}
|
||||
|
||||
#ShadowWidget {
|
||||
font-size: 36pt;
|
||||
}
|
||||
QTabWidget::pane {
|
||||
border-top-style: none;
|
||||
}
|
||||
|
||||
QTabBar {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
QTabBar::tab {
|
||||
border-top-left-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
QTabBar::tab:selected {
|
||||
background: #282C34;
|
||||
border-color: #9B9B9B;
|
||||
border-bottom-color: #C2C7CB;
|
||||
}
|
||||
|
||||
QTabBar::tab:!selected {
|
||||
margin-top: 2px;
|
||||
background: #21252B;
|
||||
}
|
||||
|
||||
QTabBar::tab:!selected:hover {
|
||||
background: #333840;
|
||||
}
|
||||
|
||||
QTabBar::tab:first:selected {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
QTabBar::tab:last:selected {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
QTabBar::tab:only-one {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
QScrollBar:horizontal {
|
||||
height: 15px;
|
||||
margin: 3px 15px 3px 15px;
|
||||
border: 1px transparent #21252B;
|
||||
border-radius: 4px;
|
||||
background-color: #21252B;
|
||||
}
|
||||
|
||||
QScrollBar::handle:horizontal {
|
||||
background-color: #4B5362;
|
||||
min-width: 5px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
QScrollBar::add-line:horizontal {
|
||||
margin: 0px 3px 0px 3px;
|
||||
border-image: url(:/qss_icons/rc/right_arrow_disabled.png);
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
subcontrol-position: right;
|
||||
subcontrol-origin: margin;
|
||||
}
|
||||
|
||||
QScrollBar::sub-line:horizontal {
|
||||
margin: 0px 3px 0px 3px;
|
||||
border-image: url(:/qss_icons/rc/left_arrow_disabled.png);
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
subcontrol-position: left;
|
||||
subcontrol-origin: margin;
|
||||
}
|
||||
|
||||
QScrollBar::add-line:horizontal:hover,QScrollBar::add-line:horizontal:on {
|
||||
border-image: url(:/qss_icons/rc/right_arrow.png);
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
subcontrol-position: right;
|
||||
subcontrol-origin: margin;
|
||||
}
|
||||
|
||||
QScrollBar::sub-line:horizontal:hover, QScrollBar::sub-line:horizontal:on {
|
||||
border-image: url(:/qss_icons/rc/left_arrow.png);
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
subcontrol-position: left;
|
||||
subcontrol-origin: margin;
|
||||
}
|
||||
|
||||
QScrollBar::up-arrow:horizontal, QScrollBar::down-arrow:horizontal {
|
||||
background: none;
|
||||
}
|
||||
|
||||
QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal {
|
||||
background: none;
|
||||
}
|
||||
|
||||
QScrollBar:vertical {
|
||||
background-color: #21252B;
|
||||
width: 15px;
|
||||
margin: 15px 3px 15px 3px;
|
||||
border: 1px transparent #21252B;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
QScrollBar::handle:vertical {
|
||||
background-color: #4B5362;
|
||||
min-height: 5px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
QScrollBar::sub-line:vertical {
|
||||
margin: 3px 0px 3px 0px;
|
||||
border-image: url(:/qss_icons/rc/up_arrow_disabled.png);
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
subcontrol-position: top;
|
||||
subcontrol-origin: margin;
|
||||
}
|
||||
|
||||
QScrollBar::add-line:vertical {
|
||||
margin: 3px 0px 3px 0px;
|
||||
border-image: url(:/qss_icons/rc/down_arrow_disabled.png);
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
subcontrol-position: bottom;
|
||||
subcontrol-origin: margin;
|
||||
}
|
||||
|
||||
QScrollBar::sub-line:vertical:hover,QScrollBar::sub-line:vertical:on {
|
||||
|
||||
border-image: url(:/qss_icons/rc/up_arrow.png);
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
subcontrol-position: top;
|
||||
subcontrol-origin: margin;
|
||||
}
|
||||
|
||||
|
||||
QScrollBar::add-line:vertical:hover, QScrollBar::add-line:vertical:on {
|
||||
border-image: url(:/qss_icons/rc/down_arrow.png);
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
subcontrol-position: bottom;
|
||||
subcontrol-origin: margin;
|
||||
}
|
||||
|
||||
QScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical {
|
||||
background: none;
|
||||
}
|
||||
|
||||
|
||||
QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {
|
||||
background: none;
|
||||
}
|
||||
|
||||
QTableView
|
||||
{
|
||||
border: 1px solid #444;
|
||||
gridline-color: #6c6c6c;
|
||||
background-color: #201F1F;
|
||||
alternate-background-color:#21252B;
|
||||
}
|
||||
|
||||
QHeaderView
|
||||
{
|
||||
border: 1px transparent;
|
||||
border-radius: 2px;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
QHeaderView::section {
|
||||
background-color: #21252B;
|
||||
/*color: silver;*/
|
||||
padding: 4px;
|
||||
border: 1px solid #6c6c6c;
|
||||
border-radius: 0px;
|
||||
text-align: center;
|
||||
color: #969b9e;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
QAbstractItemView::item:pressed {
|
||||
background: #78879b;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
QAbstractItemView::item:selected:active {
|
||||
background: #3d8ec9;
|
||||
}
|
||||
QAbstractItemView::item:selected:!active {
|
||||
background: #3d8ec9;
|
||||
}
|
||||
|
||||
#BreadcrumbsPathInput {
|
||||
padding: 2px;
|
||||
font-size: 9pt;
|
||||
}
|
||||
|
||||
#BreadcrumbsButton {
|
||||
padding-right: 12px;
|
||||
font-size: 9pt;
|
||||
}
|
||||
|
||||
#BreadcrumbsButton[empty="1"] {
|
||||
padding-right: 0px;
|
||||
}
|
||||
|
||||
#BreadcrumbsButton::menu-button {
|
||||
width: 12px;
|
||||
background: rgba(127, 127, 127, 60);
|
||||
}
|
||||
#BreadcrumbsButton::menu-button:hover {
|
||||
background: rgba(127, 127, 127, 90);
|
||||
}
|
||||
|
||||
#BreadcrumbsPanel {
|
||||
border: 1px solid #4e5254;
|
||||
border-radius: 5px;
|
||||
background: #21252B;;
|
||||
}
|
||||
|
|
@ -6,7 +6,16 @@ from avalon.mongodb import (
|
|||
AvalonMongoDB
|
||||
)
|
||||
|
||||
from openpype.style import get_objected_colors
|
||||
from openpype.tools.utils.widgets import ImageButton
|
||||
from openpype.tools.utils.lib import paint_image_with_color
|
||||
|
||||
from openpype.widgets.nice_checkbox import NiceCheckbox
|
||||
from openpype.settings.lib import get_system_settings
|
||||
from .images import (
|
||||
get_pixmap,
|
||||
get_image
|
||||
)
|
||||
from .constants import (
|
||||
DEFAULT_PROJECT_LABEL,
|
||||
PROJECT_NAME_ROLE,
|
||||
|
|
@ -31,6 +40,78 @@ class SettingsPlainTextEdit(QtWidgets.QPlainTextEdit):
|
|||
self.focused_in.emit()
|
||||
|
||||
|
||||
class SettingsToolBtn(ImageButton):
|
||||
_mask_pixmap = None
|
||||
_cached_icons = {}
|
||||
|
||||
def __init__(self, btn_type, parent):
|
||||
super(SettingsToolBtn, self).__init__(parent)
|
||||
|
||||
icon, hover_icon = self._get_icon_type(btn_type)
|
||||
|
||||
self.setIcon(icon)
|
||||
|
||||
self._icon = icon
|
||||
self._hover_icon = hover_icon
|
||||
|
||||
@classmethod
|
||||
def _get_icon_type(cls, btn_type):
|
||||
if btn_type not in cls._cached_icons:
|
||||
settings_colors = get_objected_colors()["settings"]
|
||||
normal_color = settings_colors["image-btn"].get_qcolor()
|
||||
hover_color = settings_colors["image-btn-hover"].get_qcolor()
|
||||
disabled_color = settings_colors["image-btn-disabled"].get_qcolor()
|
||||
|
||||
image = get_image("{}.png".format(btn_type))
|
||||
|
||||
pixmap = paint_image_with_color(image, normal_color)
|
||||
hover_pixmap = paint_image_with_color(image, hover_color)
|
||||
disabled_pixmap = paint_image_with_color(image, disabled_color)
|
||||
|
||||
icon = QtGui.QIcon(pixmap)
|
||||
hover_icon = QtGui.QIcon(hover_pixmap)
|
||||
icon.addPixmap(
|
||||
disabled_pixmap, QtGui.QIcon.Disabled, QtGui.QIcon.On
|
||||
)
|
||||
icon.addPixmap(
|
||||
disabled_pixmap, QtGui.QIcon.Disabled, QtGui.QIcon.Off
|
||||
)
|
||||
hover_icon.addPixmap(
|
||||
disabled_pixmap, QtGui.QIcon.Disabled, QtGui.QIcon.On
|
||||
)
|
||||
hover_icon.addPixmap(
|
||||
disabled_pixmap, QtGui.QIcon.Disabled, QtGui.QIcon.Off
|
||||
)
|
||||
cls._cached_icons[btn_type] = icon, hover_icon
|
||||
return cls._cached_icons[btn_type]
|
||||
|
||||
def enterEvent(self, event):
|
||||
self.setIcon(self._hover_icon)
|
||||
super(SettingsToolBtn, self).enterEvent(event)
|
||||
|
||||
def leaveEvent(self, event):
|
||||
self.setIcon(self._icon)
|
||||
super(SettingsToolBtn, self).leaveEvent(event)
|
||||
|
||||
@classmethod
|
||||
def _get_mask_pixmap(cls):
|
||||
if cls._mask_pixmap is None:
|
||||
mask_pixmap = get_pixmap("mask.png")
|
||||
cls._mask_pixmap = mask_pixmap
|
||||
return cls._mask_pixmap
|
||||
|
||||
def _change_size(self):
|
||||
super(SettingsToolBtn, self)._change_size()
|
||||
size = self.iconSize()
|
||||
scaled = self._get_mask_pixmap().scaled(
|
||||
size.width(),
|
||||
size.height(),
|
||||
QtCore.Qt.IgnoreAspectRatio,
|
||||
QtCore.Qt.SmoothTransformation
|
||||
)
|
||||
self.setMask(scaled.mask())
|
||||
|
||||
|
||||
class ShadowWidget(QtWidgets.QWidget):
|
||||
def __init__(self, message, parent):
|
||||
super(ShadowWidget, self).__init__(parent)
|
||||
|
|
@ -132,9 +213,14 @@ class SettingsComboBox(QtWidgets.QComboBox):
|
|||
def __init__(self, *args, **kwargs):
|
||||
super(SettingsComboBox, self).__init__(*args, **kwargs)
|
||||
|
||||
delegate = QtWidgets.QStyledItemDelegate()
|
||||
self.setItemDelegate(delegate)
|
||||
|
||||
self.currentIndexChanged.connect(self._on_change)
|
||||
self.setFocusPolicy(QtCore.Qt.StrongFocus)
|
||||
|
||||
self._delegate = delegate
|
||||
|
||||
def wheelEvent(self, event):
|
||||
if self.hasFocus():
|
||||
return super(SettingsComboBox, self).wheelEvent(event)
|
||||
|
|
@ -180,14 +266,14 @@ class ExpandingWidget(QtWidgets.QWidget):
|
|||
|
||||
button_size = QtCore.QSize(5, 5)
|
||||
button_toggle = QtWidgets.QToolButton(parent=side_line_widget)
|
||||
button_toggle.setProperty("btn-type", "expand-toggle")
|
||||
button_toggle.setObjectName("ExpandToggleBtn")
|
||||
button_toggle.setIconSize(button_size)
|
||||
button_toggle.setArrowType(QtCore.Qt.RightArrow)
|
||||
button_toggle.setCheckable(True)
|
||||
button_toggle.setChecked(False)
|
||||
|
||||
label_widget = QtWidgets.QLabel(label, parent=side_line_widget)
|
||||
label_widget.setObjectName("DictLabel")
|
||||
label_widget.setObjectName("ExpandLabel")
|
||||
|
||||
before_label_widget = QtWidgets.QWidget(side_line_widget)
|
||||
before_label_layout = QtWidgets.QHBoxLayout(before_label_widget)
|
||||
|
|
@ -381,6 +467,7 @@ class GridLabelWidget(QtWidgets.QWidget):
|
|||
self.properties = {}
|
||||
|
||||
label_widget = QtWidgets.QLabel(label, self)
|
||||
label_widget.setObjectName("SettingsLabel")
|
||||
|
||||
label_proxy_layout = QtWidgets.QHBoxLayout()
|
||||
label_proxy_layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
|
@ -415,197 +502,12 @@ class GridLabelWidget(QtWidgets.QWidget):
|
|||
return super(GridLabelWidget, self).mouseReleaseEvent(event)
|
||||
|
||||
|
||||
class NiceCheckboxMoveWidget(QtWidgets.QFrame):
|
||||
def __init__(self, height, border_width, parent):
|
||||
super(NiceCheckboxMoveWidget, self).__init__(parent=parent)
|
||||
|
||||
self.checkstate = False
|
||||
|
||||
self.half_size = int(height / 2)
|
||||
self.full_size = self.half_size * 2
|
||||
self.border_width = border_width
|
||||
self.setFixedHeight(self.full_size)
|
||||
self.setFixedWidth(self.full_size)
|
||||
|
||||
self.setStyleSheet((
|
||||
"background: #444444;border-style: none;"
|
||||
"border-radius: {};border-width:{}px;"
|
||||
).format(self.half_size, self.border_width))
|
||||
|
||||
def update_position(self):
|
||||
parent_rect = self.parent().rect()
|
||||
if self.checkstate is True:
|
||||
pos_x = (
|
||||
parent_rect.x()
|
||||
+ parent_rect.width()
|
||||
- self.full_size
|
||||
- self.border_width
|
||||
)
|
||||
else:
|
||||
pos_x = parent_rect.x() + self.border_width
|
||||
|
||||
pos_y = parent_rect.y() + int(
|
||||
parent_rect.height() / 2 - self.half_size
|
||||
)
|
||||
self.setGeometry(pos_x, pos_y, self.width(), self.height())
|
||||
|
||||
def state_offset(self):
|
||||
diff_x = (
|
||||
self.parent().rect().width()
|
||||
- self.full_size
|
||||
- (2 * self.border_width)
|
||||
)
|
||||
return QtCore.QPoint(diff_x, 0)
|
||||
|
||||
def change_position(self, checkstate):
|
||||
self.checkstate = checkstate
|
||||
|
||||
self.update_position()
|
||||
|
||||
def resizeEvent(self, event):
|
||||
super().resizeEvent(event)
|
||||
self.update_position()
|
||||
|
||||
|
||||
class NiceCheckbox(QtWidgets.QFrame):
|
||||
stateChanged = QtCore.Signal(int)
|
||||
checked_bg_color = QtGui.QColor(69, 128, 86)
|
||||
unchecked_bg_color = QtGui.QColor(170, 80, 80)
|
||||
class SettingsNiceCheckbox(NiceCheckbox):
|
||||
focused_in = QtCore.Signal()
|
||||
|
||||
def set_bg_color(self, color):
|
||||
self._bg_color = color
|
||||
self.setStyleSheet(self._stylesheet_template.format(
|
||||
color.red(), color.green(), color.blue()
|
||||
))
|
||||
|
||||
def bg_color(self):
|
||||
return self._bg_color
|
||||
|
||||
bgcolor = QtCore.Property(QtGui.QColor, bg_color, set_bg_color)
|
||||
|
||||
def __init__(self, checked=True, height=30, *args, **kwargs):
|
||||
super(NiceCheckbox, self).__init__(*args, **kwargs)
|
||||
|
||||
self._checkstate = checked
|
||||
if checked:
|
||||
bg_color = self.checked_bg_color
|
||||
else:
|
||||
bg_color = self.unchecked_bg_color
|
||||
|
||||
self.half_height = int(height / 2)
|
||||
height = self.half_height * 2
|
||||
tenth_height = int(height / 10)
|
||||
|
||||
self.setFixedHeight(height)
|
||||
self.setFixedWidth((height - tenth_height) * 2)
|
||||
|
||||
move_item_size = height - (2 * tenth_height)
|
||||
|
||||
self.move_item = NiceCheckboxMoveWidget(
|
||||
move_item_size, tenth_height, self
|
||||
)
|
||||
self.move_item.change_position(self._checkstate)
|
||||
|
||||
self._stylesheet_template = (
|
||||
"border-radius: {}px;"
|
||||
"border-width: {}px;"
|
||||
"background: #333333;"
|
||||
"border-style: solid;"
|
||||
"border-color: #555555;"
|
||||
).format(self.half_height, tenth_height)
|
||||
self._stylesheet_template += "background: rgb({},{},{});"
|
||||
|
||||
self.set_bg_color(bg_color)
|
||||
|
||||
def resizeEvent(self, event):
|
||||
super(NiceCheckbox, self).resizeEvent(event)
|
||||
self.move_item.update_position()
|
||||
|
||||
def show(self, *args, **kwargs):
|
||||
super(NiceCheckbox, self).show(*args, **kwargs)
|
||||
self.move_item.update_position()
|
||||
|
||||
def checkState(self):
|
||||
if self._checkstate:
|
||||
return QtCore.Qt.Checked
|
||||
else:
|
||||
return QtCore.Qt.Unchecked
|
||||
|
||||
def _on_checkstate_change(self):
|
||||
self.stateChanged.emit(self.checkState())
|
||||
|
||||
move_start_value = self.move_item.pos()
|
||||
offset = self.move_item.state_offset()
|
||||
if self._checkstate is True:
|
||||
move_end_value = move_start_value + offset
|
||||
else:
|
||||
move_end_value = move_start_value - offset
|
||||
move_animation = QtCore.QPropertyAnimation(
|
||||
self.move_item, b"pos", self
|
||||
)
|
||||
move_animation.setDuration(150)
|
||||
move_animation.setEasingCurve(QtCore.QEasingCurve.OutQuad)
|
||||
move_animation.setStartValue(move_start_value)
|
||||
move_animation.setEndValue(move_end_value)
|
||||
|
||||
color_animation = QtCore.QPropertyAnimation(
|
||||
self, b"bgcolor"
|
||||
)
|
||||
color_animation.setDuration(150)
|
||||
if self._checkstate is True:
|
||||
color_animation.setStartValue(self.unchecked_bg_color)
|
||||
color_animation.setEndValue(self.checked_bg_color)
|
||||
else:
|
||||
color_animation.setStartValue(self.checked_bg_color)
|
||||
color_animation.setEndValue(self.unchecked_bg_color)
|
||||
|
||||
anim_group = QtCore.QParallelAnimationGroup(self)
|
||||
anim_group.addAnimation(move_animation)
|
||||
anim_group.addAnimation(color_animation)
|
||||
|
||||
def _finished():
|
||||
self.move_item.change_position(self._checkstate)
|
||||
|
||||
anim_group.finished.connect(_finished)
|
||||
anim_group.start()
|
||||
|
||||
def isChecked(self):
|
||||
return self._checkstate
|
||||
|
||||
def setChecked(self, checked):
|
||||
if checked == self._checkstate:
|
||||
return
|
||||
self._checkstate = checked
|
||||
self._on_checkstate_change()
|
||||
|
||||
def setCheckState(self, state=None):
|
||||
if state is None:
|
||||
checkstate = not self._checkstate
|
||||
elif state == QtCore.Qt.Checked:
|
||||
checkstate = True
|
||||
elif state == QtCore.Qt.Unchecked:
|
||||
checkstate = False
|
||||
else:
|
||||
return
|
||||
|
||||
if checkstate == self._checkstate:
|
||||
return
|
||||
|
||||
self._checkstate = checkstate
|
||||
|
||||
self._on_checkstate_change()
|
||||
|
||||
def mousePressEvent(self, event):
|
||||
self.focused_in.emit()
|
||||
super(NiceCheckbox, self).mousePressEvent(event)
|
||||
|
||||
def mouseReleaseEvent(self, event):
|
||||
if event.button() == QtCore.Qt.LeftButton:
|
||||
self.setCheckState()
|
||||
event.accept()
|
||||
return
|
||||
return super(NiceCheckbox, self).mouseReleaseEvent(event)
|
||||
super(SettingsNiceCheckbox, self).mousePressEvent(event)
|
||||
|
||||
|
||||
class ProjectModel(QtGui.QStandardItemModel):
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ from .categories import (
|
|||
ProjectWidget
|
||||
)
|
||||
from .widgets import ShadowWidget, RestartDialog
|
||||
from . import style
|
||||
from openpype import style
|
||||
|
||||
from openpype.lib import is_admin_password_required
|
||||
from openpype.widgets import PasswordDialog
|
||||
|
|
@ -25,7 +25,7 @@ class MainWidget(QtWidgets.QWidget):
|
|||
|
||||
self._password_dialog = None
|
||||
|
||||
self.setObjectName("MainWidget")
|
||||
self.setObjectName("SettingsMainWidget")
|
||||
self.setWindowTitle("OpenPype Settings")
|
||||
|
||||
self.resize(self.widget_width, self.widget_height)
|
||||
|
|
|
|||
|
|
@ -25,6 +25,34 @@ def center_window(window):
|
|||
window.move(geo.topLeft())
|
||||
|
||||
|
||||
def paint_image_with_color(image, color):
|
||||
"""Redraw image with single color using it's alpha.
|
||||
|
||||
It is expected that input image is singlecolor image with alpha.
|
||||
|
||||
Args:
|
||||
image (QImage): Loaded image with alpha.
|
||||
color (QColor): Color that will be used to paint image.
|
||||
"""
|
||||
width = image.width()
|
||||
height = image.height()
|
||||
|
||||
alpha_mask = image.createAlphaMask()
|
||||
alpha_region = QtGui.QRegion(QtGui.QBitmap.fromImage(alpha_mask))
|
||||
|
||||
pixmap = QtGui.QPixmap(width, height)
|
||||
pixmap.fill(QtCore.Qt.transparent)
|
||||
|
||||
painter = QtGui.QPainter(pixmap)
|
||||
painter.setClipRegion(alpha_region)
|
||||
painter.setPen(QtCore.Qt.NoPen)
|
||||
painter.setBrush(color)
|
||||
painter.drawRect(QtCore.QRect(0, 0, width, height))
|
||||
painter.end()
|
||||
|
||||
return pixmap
|
||||
|
||||
|
||||
def format_version(value, hero_version=False):
|
||||
"""Formats integer to displayable version name"""
|
||||
label = "v{0:03d}".format(value)
|
||||
|
|
@ -477,6 +505,7 @@ def create_qthread(func, *args, **kwargs):
|
|||
|
||||
|
||||
def get_repre_icons():
|
||||
"""Returns a dict {'provider_name': QIcon}"""
|
||||
try:
|
||||
from openpype_modules import sync_server
|
||||
except Exception:
|
||||
|
|
@ -488,9 +517,17 @@ def get_repre_icons():
|
|||
"providers", "resources"
|
||||
)
|
||||
icons = {}
|
||||
# TODO get from sync module
|
||||
for provider in ['studio', 'local_drive', 'gdrive']:
|
||||
pix_url = "{}/{}.png".format(resource_path, provider)
|
||||
if not os.path.exists(resource_path):
|
||||
print("No icons for Site Sync found")
|
||||
return {}
|
||||
|
||||
for file_name in os.listdir(resource_path):
|
||||
if file_name and not file_name.endswith("png"):
|
||||
continue
|
||||
|
||||
provider, _ = os.path.splitext(file_name)
|
||||
|
||||
pix_url = os.path.join(resource_path, file_name)
|
||||
icons[provider] = QtGui.QIcon(pix_url)
|
||||
|
||||
return icons
|
||||
|
|
|
|||
|
|
@ -30,6 +30,32 @@ class PlaceholderLineEdit(QtWidgets.QLineEdit):
|
|||
self.setPalette(filter_palette)
|
||||
|
||||
|
||||
class ImageButton(QtWidgets.QPushButton):
|
||||
"""PushButton with icon and size of font.
|
||||
|
||||
Using font metrics height as icon size reference.
|
||||
|
||||
TODO:
|
||||
- handle changes of screen (different resolution)
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(ImageButton, self).__init__(*args, **kwargs)
|
||||
self.setObjectName("ImageButton")
|
||||
|
||||
def _change_size(self):
|
||||
font_height = self.fontMetrics().height()
|
||||
self.setIconSize(QtCore.QSize(font_height, font_height))
|
||||
|
||||
def showEvent(self, event):
|
||||
super(ImageButton, self).showEvent(event)
|
||||
|
||||
self._change_size()
|
||||
|
||||
def sizeHint(self):
|
||||
return self.iconSize()
|
||||
|
||||
|
||||
class OptionalMenu(QtWidgets.QMenu):
|
||||
"""A subclass of `QtWidgets.QMenu` to work with `OptionalAction`
|
||||
|
||||
|
|
|
|||
|
|
@ -100,9 +100,7 @@ class NameWindow(QtWidgets.QDialog):
|
|||
|
||||
# Store project anatomy
|
||||
self.anatomy = anatomy
|
||||
self.template = anatomy.templates[template_key]["file"].replace(
|
||||
"{task}", "{task[name]}"
|
||||
)
|
||||
self.template = anatomy.templates[template_key]["file"]
|
||||
self.template_key = template_key
|
||||
|
||||
# Btns widget
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Package declaring Pype version."""
|
||||
__version__ = "3.7.0-nightly.2"
|
||||
__version__ = "3.7.0-nightly.3"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
from Qt import QtWidgets, QtCore
|
||||
import sys
|
||||
import logging
|
||||
from Qt import QtWidgets, QtCore
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,18 @@
|
|||
from math import floor, sqrt, ceil
|
||||
from Qt import QtWidgets, QtCore, QtGui
|
||||
|
||||
from openpype.style import get_objected_colors
|
||||
|
||||
|
||||
class NiceCheckbox(QtWidgets.QFrame):
|
||||
stateChanged = QtCore.Signal(int)
|
||||
clicked = QtCore.Signal()
|
||||
|
||||
_checked_bg_color = None
|
||||
_unchecked_bg_color = None
|
||||
_checker_color = None
|
||||
_checker_hover_color = None
|
||||
|
||||
def __init__(self, checked=False, draw_icons=False, parent=None):
|
||||
super(NiceCheckbox, self).__init__(parent)
|
||||
|
||||
|
|
@ -41,12 +48,6 @@ class NiceCheckbox(QtWidgets.QFrame):
|
|||
self._pressed = False
|
||||
self._under_mouse = False
|
||||
|
||||
self.checked_bg_color = QtGui.QColor(67, 181, 129)
|
||||
self.unchecked_bg_color = QtGui.QColor(79, 79, 79)
|
||||
|
||||
self.checker_checked_color = QtGui.QColor(255, 255, 255)
|
||||
self.checker_unchecked_color = self.checker_checked_color
|
||||
|
||||
self.icon_scale_factor = sqrt(2) / 2
|
||||
|
||||
icon_path_stroker = QtGui.QPainterPathStroker()
|
||||
|
|
@ -58,6 +59,37 @@ class NiceCheckbox(QtWidgets.QFrame):
|
|||
self._animation_timer.timeout.connect(self._on_animation_timeout)
|
||||
|
||||
self._base_size = QtCore.QSize(90, 50)
|
||||
self._load_colors()
|
||||
|
||||
@classmethod
|
||||
def _load_colors(cls):
|
||||
if cls._checked_bg_color is not None:
|
||||
return
|
||||
|
||||
colors_data = get_objected_colors()
|
||||
colors_info = colors_data["nice-checkbox"]
|
||||
|
||||
cls._checked_bg_color = colors_info["bg-checked"].get_qcolor()
|
||||
cls._unchecked_bg_color = colors_info["bg-unchecked"].get_qcolor()
|
||||
|
||||
cls._checker_color = colors_info["bg-checker"].get_qcolor()
|
||||
cls._checker_hover_color = colors_info["bg-checker-hover"].get_qcolor()
|
||||
|
||||
@property
|
||||
def checked_bg_color(self):
|
||||
return self._checked_bg_color
|
||||
|
||||
@property
|
||||
def unchecked_bg_color(self):
|
||||
return self._unchecked_bg_color
|
||||
|
||||
@property
|
||||
def checker_color(self):
|
||||
return self._checker_color
|
||||
|
||||
@property
|
||||
def checker_hover_color(self):
|
||||
return self._checker_hover_color
|
||||
|
||||
def setTristate(self, tristate=True):
|
||||
if self._is_tristate != tristate:
|
||||
|
|
@ -73,15 +105,6 @@ class NiceCheckbox(QtWidgets.QFrame):
|
|||
self._draw_icons = draw_icons
|
||||
self.repaint()
|
||||
|
||||
def _checkbox_size_hint(self):
|
||||
checkbox_height = self.style().pixelMetric(
|
||||
QtWidgets.QStyle.PM_IndicatorHeight
|
||||
)
|
||||
checkbox_height += checkbox_height % 2
|
||||
width = (2 * checkbox_height) - (checkbox_height / 5)
|
||||
new_size = QtCore.QSize(width, checkbox_height)
|
||||
return new_size
|
||||
|
||||
def sizeHint(self):
|
||||
height = self.fontMetrics().height()
|
||||
width = self.get_width_hint_by_height(height)
|
||||
|
|
@ -159,7 +182,7 @@ class NiceCheckbox(QtWidgets.QFrame):
|
|||
if self._animation_timer.isActive():
|
||||
self._animation_timer.stop()
|
||||
|
||||
if self.isEnabled():
|
||||
if self.isVisible() and self.isEnabled():
|
||||
# Start animation
|
||||
self._animation_timer.start(self._animation_timeout)
|
||||
else:
|
||||
|
|
@ -235,14 +258,16 @@ class NiceCheckbox(QtWidgets.QFrame):
|
|||
|
||||
def _on_animation_timeout(self):
|
||||
if self._checkstate == QtCore.Qt.Checked:
|
||||
self._current_step += 1
|
||||
if self._current_step == self._steps:
|
||||
self._animation_timer.stop()
|
||||
return
|
||||
self._current_step += 1
|
||||
|
||||
elif self._checkstate == QtCore.Qt.Unchecked:
|
||||
self._current_step -= 1
|
||||
if self._current_step == 0:
|
||||
self._animation_timer.stop()
|
||||
return
|
||||
self._current_step -= 1
|
||||
|
||||
else:
|
||||
if self._current_step < self._middle_step:
|
||||
|
|
@ -291,11 +316,9 @@ class NiceCheckbox(QtWidgets.QFrame):
|
|||
# Draw inner background
|
||||
if self._current_step == self._steps:
|
||||
bg_color = self.checked_bg_color
|
||||
checker_color = self.checker_checked_color
|
||||
|
||||
elif self._current_step == 0:
|
||||
bg_color = self.unchecked_bg_color
|
||||
checker_color = self.checker_unchecked_color
|
||||
|
||||
else:
|
||||
offset_ratio = self._current_step / self._steps
|
||||
|
|
@ -305,11 +328,6 @@ class NiceCheckbox(QtWidgets.QFrame):
|
|||
self.unchecked_bg_color,
|
||||
offset_ratio
|
||||
)
|
||||
checker_color = self.steped_color(
|
||||
self.checker_checked_color,
|
||||
self.checker_unchecked_color,
|
||||
offset_ratio
|
||||
)
|
||||
|
||||
margins_ratio = self._checker_margins_divider
|
||||
if margins_ratio > 0:
|
||||
|
|
@ -359,52 +377,14 @@ class NiceCheckbox(QtWidgets.QFrame):
|
|||
checker_rect = QtCore.QRect(pos_x, pos_y, checker_size, checker_size)
|
||||
|
||||
under_mouse = self.isEnabled() and self._under_mouse
|
||||
|
||||
shadow_x = checker_rect.x()
|
||||
shadow_y = checker_rect.y() + margin_size_c
|
||||
shadow_size = min(
|
||||
frame_rect.right() - shadow_x,
|
||||
frame_rect.bottom() - shadow_y,
|
||||
checker_size + (2 * margin_size_c)
|
||||
)
|
||||
shadow_rect = QtCore.QRect(
|
||||
checker_rect.x(),
|
||||
shadow_y,
|
||||
shadow_size,
|
||||
shadow_size
|
||||
)
|
||||
|
||||
shadow_brush = QtGui.QRadialGradient(
|
||||
shadow_rect.center(),
|
||||
shadow_rect.height() / 2
|
||||
)
|
||||
shadow_brush.setColorAt(0.6, QtCore.Qt.black)
|
||||
shadow_brush.setColorAt(1, QtCore.Qt.transparent)
|
||||
|
||||
painter.setPen(QtCore.Qt.transparent)
|
||||
painter.setBrush(shadow_brush)
|
||||
painter.drawEllipse(shadow_rect)
|
||||
if under_mouse:
|
||||
checker_color = self.checker_hover_color
|
||||
else:
|
||||
checker_color = self.checker_color
|
||||
|
||||
painter.setBrush(checker_color)
|
||||
painter.drawEllipse(checker_rect)
|
||||
|
||||
if under_mouse:
|
||||
adjust = margin_size_c
|
||||
if adjust < 1 and checker_rect.height() > 4:
|
||||
adjust = 1
|
||||
|
||||
smaller_checker_rect = checker_rect.adjusted(
|
||||
adjust, adjust, -adjust, -adjust
|
||||
)
|
||||
gradient = QtGui.QLinearGradient(
|
||||
smaller_checker_rect.bottomRight(),
|
||||
smaller_checker_rect.topLeft()
|
||||
)
|
||||
gradient.setColorAt(0, checker_color)
|
||||
gradient.setColorAt(1, checker_color.darker(155))
|
||||
painter.setBrush(gradient)
|
||||
painter.drawEllipse(smaller_checker_rect)
|
||||
|
||||
if self._draw_icons:
|
||||
painter.setBrush(bg_color)
|
||||
icon_path = self._get_icon_path(painter, checker_rect)
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import logging
|
|||
import contextlib
|
||||
|
||||
|
||||
from avalon.vendor.Qt import QtCore, QtWidgets, QtGui
|
||||
from Qt import QtCore, QtWidgets
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
|
||||
|
||||
from avalon.vendor.Qt import QtCore, QtGui, QtWidgets
|
||||
import os
|
||||
import getpass
|
||||
import platform
|
||||
|
||||
from Qt import QtCore, QtGui, QtWidgets
|
||||
|
||||
from avalon import style
|
||||
import ftrack_api
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[tool.poetry]
|
||||
name = "OpenPype"
|
||||
version = "3.7.0-nightly.2" # OpenPype
|
||||
version = "3.7.0-nightly.3" # OpenPype
|
||||
description = "Open VFX and Animation pipeline with support."
|
||||
authors = ["OpenPype Team <info@openpype.io>"]
|
||||
license = "MIT License"
|
||||
|
|
|
|||
28
start.py
|
|
@ -100,6 +100,7 @@ import platform
|
|||
import traceback
|
||||
import subprocess
|
||||
import site
|
||||
import distutils.spawn
|
||||
from pathlib import Path
|
||||
|
||||
# OPENPYPE_ROOT is variable pointing to build (or code) directory
|
||||
|
|
@ -384,23 +385,6 @@ def set_modules_environments():
|
|||
os.environ.update(env)
|
||||
|
||||
|
||||
def is_tool(name):
|
||||
try:
|
||||
import os.errno as errno
|
||||
except ImportError:
|
||||
import errno
|
||||
|
||||
try:
|
||||
devnull = open(os.devnull, "w")
|
||||
subprocess.Popen(
|
||||
[name], stdout=devnull, stderr=devnull
|
||||
).communicate()
|
||||
except OSError as exc:
|
||||
if exc.errno == errno.ENOENT:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def _startup_validations():
|
||||
"""Validations before OpenPype starts."""
|
||||
try:
|
||||
|
|
@ -443,7 +427,8 @@ def _validate_thirdparty_binaries():
|
|||
if low_platform == "windows":
|
||||
ffmpeg_dir = os.path.join(ffmpeg_dir, "bin")
|
||||
ffmpeg_executable = os.path.join(ffmpeg_dir, "ffmpeg")
|
||||
if not is_tool(ffmpeg_executable):
|
||||
ffmpeg_result = distutils.spawn.find_executable(ffmpeg_executable)
|
||||
if ffmpeg_result is None:
|
||||
raise RuntimeError(error_msg.format("FFmpeg"))
|
||||
|
||||
# Validate existence of OpenImageIO (not on MacOs)
|
||||
|
|
@ -463,8 +448,11 @@ def _validate_thirdparty_binaries():
|
|||
low_platform,
|
||||
"oiiotool"
|
||||
)
|
||||
if oiio_tool_path is not None and not is_tool(oiio_tool_path):
|
||||
raise RuntimeError(error_msg.format("OpenImageIO"))
|
||||
oiio_result = None
|
||||
if oiio_tool_path is not None:
|
||||
oiio_result = distutils.spawn.find_executable(oiio_tool_path)
|
||||
if oiio_result is None:
|
||||
raise RuntimeError(error_msg.format("OpenImageIO"))
|
||||
|
||||
|
||||
def _process_arguments() -> tuple:
|
||||
|
|
|
|||
|
|
@ -16,8 +16,9 @@ def inject_openpype_environment(deadlinePlugin):
|
|||
job = deadlinePlugin.GetJob()
|
||||
job = RepositoryUtils.GetJob(job.JobId, True) # invalidates cache
|
||||
|
||||
print("inject_openpype_environment start")
|
||||
print(">>> Injecting OpenPype environments ...")
|
||||
try:
|
||||
print(">>> Getting OpenPype executable ...")
|
||||
exe_list = job.GetJobExtraInfoKeyValue("openpype_executables")
|
||||
openpype_app = FileUtils.SearchFileList(exe_list)
|
||||
if openpype_app == "":
|
||||
|
|
@ -27,11 +28,13 @@ def inject_openpype_environment(deadlinePlugin):
|
|||
"The path to the render executable can be configured " +
|
||||
"from the Plugin Configuration in the Deadline Monitor.")
|
||||
|
||||
print("--- OpenPype executable: {}".format(openpype_app))
|
||||
|
||||
# tempfile.TemporaryFile cannot be used because of locking
|
||||
export_url = os.path.join(tempfile.gettempdir(),
|
||||
time.strftime('%Y%m%d%H%M%S'),
|
||||
'env.json') # add HHMMSS + delete later
|
||||
print("export_url {}".format(export_url))
|
||||
print(">>> Temporary path: {}".format(export_url))
|
||||
|
||||
args = [
|
||||
openpype_app,
|
||||
|
|
@ -55,41 +58,52 @@ def inject_openpype_environment(deadlinePlugin):
|
|||
"AVALON_TASK, AVALON_APP_NAME"
|
||||
raise RuntimeError(msg)
|
||||
|
||||
print("args:::{}".format(args))
|
||||
if not os.environ.get("OPENPYPE_MONGO"):
|
||||
print(">>> Missing OPENPYPE_MONGO env var, process won't work")
|
||||
|
||||
exit_code = subprocess.call(args, cwd=os.path.dirname(openpype_app))
|
||||
if exit_code != 0:
|
||||
raise RuntimeError("Publishing failed, check worker's log")
|
||||
env = os.environ
|
||||
env["OPENPYPE_HEADLESS_MODE"] = "1"
|
||||
env["AVALON_TIMEOUT"] = "5000"
|
||||
|
||||
print(">>> Executing: {}".format(args))
|
||||
std_output = subprocess.check_output(args,
|
||||
cwd=os.path.dirname(openpype_app),
|
||||
env=env)
|
||||
print(">>> Process result {}".format(std_output))
|
||||
|
||||
print(">>> Loading file ...")
|
||||
with open(export_url) as fp:
|
||||
contents = json.load(fp)
|
||||
for key, value in contents.items():
|
||||
deadlinePlugin.SetProcessEnvironmentVariable(key, value)
|
||||
|
||||
print(">>> Removing temporary file")
|
||||
os.remove(export_url)
|
||||
|
||||
print("inject_openpype_environment end")
|
||||
except Exception:
|
||||
print(">> Injection end.")
|
||||
except Exception as e:
|
||||
if hasattr(e, "output"):
|
||||
print(">>> Exception {}".format(e.output))
|
||||
import traceback
|
||||
print(traceback.format_exc())
|
||||
print("inject_openpype_environment failed")
|
||||
print("!!! Injection failed.")
|
||||
RepositoryUtils.FailJob(job)
|
||||
raise
|
||||
|
||||
|
||||
def inject_render_job_id(deadlinePlugin):
|
||||
"""Inject dependency ids to publish process as env var for validation."""
|
||||
print("inject_render_job_id start")
|
||||
print(">>> Injecting render job id ...")
|
||||
job = deadlinePlugin.GetJob()
|
||||
job = RepositoryUtils.GetJob(job.JobId, True) # invalidates cache
|
||||
|
||||
dependency_ids = job.JobDependencyIDs
|
||||
print("dependency_ids {}".format(dependency_ids))
|
||||
print(">>> Dependency IDs: {}".format(dependency_ids))
|
||||
render_job_ids = ",".join(dependency_ids)
|
||||
|
||||
deadlinePlugin.SetProcessEnvironmentVariable("RENDER_JOB_IDS",
|
||||
render_job_ids)
|
||||
print("inject_render_job_id end")
|
||||
print(">>> Injection end.")
|
||||
|
||||
|
||||
def pype_command_line(executable, arguments, workingDirectory):
|
||||
|
|
@ -133,10 +147,13 @@ def pype(deadlinePlugin):
|
|||
deadlinePlugin: Deadline job plugin passed by Deadline
|
||||
|
||||
"""
|
||||
print(">>> Getting job ...")
|
||||
job = deadlinePlugin.GetJob()
|
||||
# PYPE should be here, not OPENPYPE - backward compatibility!!
|
||||
pype_metadata = job.GetJobEnvironmentKeyValue("PYPE_METADATA_FILE")
|
||||
pype_python = job.GetJobEnvironmentKeyValue("PYPE_PYTHON_EXE")
|
||||
print(">>> Having backward compatible env vars {}/{}".format(pype_metadata,
|
||||
pype_python))
|
||||
# test if it is pype publish job.
|
||||
if pype_metadata:
|
||||
pype_metadata = RepositoryUtils.CheckPathMapping(pype_metadata)
|
||||
|
|
@ -162,6 +179,8 @@ def pype(deadlinePlugin):
|
|||
|
||||
|
||||
def __main__(deadlinePlugin):
|
||||
print("*** GlobalJobPreload start ...")
|
||||
print(">>> Getting job ...")
|
||||
job = deadlinePlugin.GetJob()
|
||||
job = RepositoryUtils.GetJob(job.JobId, True) # invalidates cache
|
||||
|
||||
|
|
@ -170,6 +189,8 @@ def __main__(deadlinePlugin):
|
|||
openpype_publish_job = \
|
||||
job.GetJobEnvironmentKeyValue('OPENPYPE_PUBLISH_JOB') or '0'
|
||||
|
||||
print("--- Job type - render {}".format(openpype_render_job))
|
||||
print("--- Job type - publish {}".format(openpype_publish_job))
|
||||
if openpype_publish_job == '1' and openpype_render_job == '1':
|
||||
raise RuntimeError("Misconfiguration. Job couldn't be both " +
|
||||
"render and publish.")
|
||||
|
|
|
|||
|
|
@ -197,7 +197,7 @@ h5, h6 { font-weight: var(--ifm-font-weight-semibold); }
|
|||
}
|
||||
|
||||
.showcase .client img {
|
||||
max-height: 80px;
|
||||
max-height: 70px;
|
||||
padding: 20px;
|
||||
max-width: 120px;
|
||||
align-self: center;
|
||||
|
|
@ -215,10 +215,10 @@ h5, h6 { font-weight: var(--ifm-font-weight-semibold); }
|
|||
}
|
||||
|
||||
.showcase .collab img {
|
||||
max-height: 60px;
|
||||
max-height: 70px;
|
||||
padding: 20px;
|
||||
align-self: center;
|
||||
max-width: 200px;
|
||||
max-width: 160px;
|
||||
}
|
||||
|
||||
.showcase .pype_logo img{
|
||||
|
|
|
|||
|
|
@ -64,6 +64,10 @@ const collab = [
|
|||
title: 'Clothcat Animation',
|
||||
image: '/img/clothcat.png',
|
||||
infoLink: 'https://www.clothcatanimation.com/'
|
||||
}, {
|
||||
title: 'Ellipse Studio',
|
||||
image: '/img/ellipse-studio.png',
|
||||
infoLink: 'http://www.dargaudmedia.com'
|
||||
}
|
||||
];
|
||||
|
||||
|
|
@ -125,7 +129,7 @@ const studios = [
|
|||
title: "Moonrock Animation Studio",
|
||||
image: "/img/moonrock_logo.png",
|
||||
infoLink: "https://www.moonrock.eu/",
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
function Service({imageUrl, title, description}) {
|
||||
|
|
|
|||
BIN
website/static/img/ellipse-studio.png
Normal file
|
After Width: | Height: | Size: 14 KiB |