mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +01:00
[Automated] Merged develop into main
This commit is contained in:
commit
8eadfda065
90 changed files with 1336 additions and 1114 deletions
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -386,10 +386,13 @@ class CollectLook(pyblish.api.InstancePlugin):
|
|||
|
||||
self.log.info("Collected resources: {}".format(instance.data["resources"]))
|
||||
|
||||
# Log a warning when no relevant sets were retrieved for the look.
|
||||
if not instance.data["lookData"]["relationships"]:
|
||||
self.log.warning("No sets found for the nodes in the instance: "
|
||||
"%s" % instance[:])
|
||||
# Log warning when no relevant sets were retrieved for the look.
|
||||
if (
|
||||
not instance.data["lookData"]["relationships"]
|
||||
and "model" not in self.families
|
||||
):
|
||||
self.log.warning("No sets found for the nodes in the "
|
||||
"instance: %s" % instance[:])
|
||||
|
||||
# Ensure unique shader sets
|
||||
# Add shader sets to the instance for unify ID validation
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ from openpype.hosts.maya.api import lib
|
|||
class ValidateModelContent(pyblish.api.InstancePlugin):
|
||||
"""Adheres to the content of 'model' family
|
||||
|
||||
- Must have one top group.
|
||||
- Must have one top group. (configurable)
|
||||
- Must only contain: transforms, meshes and groups
|
||||
|
||||
"""
|
||||
|
|
@ -20,6 +20,8 @@ class ValidateModelContent(pyblish.api.InstancePlugin):
|
|||
label = "Model Content"
|
||||
actions = [openpype.hosts.maya.api.action.SelectInvalidAction]
|
||||
|
||||
validate_top_group = True
|
||||
|
||||
@classmethod
|
||||
def get_invalid(cls, instance):
|
||||
|
||||
|
|
@ -59,12 +61,13 @@ class ValidateModelContent(pyblish.api.InstancePlugin):
|
|||
|
||||
# Top group
|
||||
assemblies = cmds.ls(content_instance, assemblies=True, long=True)
|
||||
if len(assemblies) != 1:
|
||||
if len(assemblies) != 1 and cls.validate_top_group:
|
||||
cls.log.error("Must have exactly one top group")
|
||||
if len(assemblies) == 0:
|
||||
cls.log.warning("No top group found. "
|
||||
"(Are there objects in the instance?"
|
||||
" Or is it parented in another group?)")
|
||||
return assemblies
|
||||
if len(assemblies) == 0:
|
||||
cls.log.warning("No top group found. "
|
||||
"(Are there objects in the instance?"
|
||||
" Or is it parented in another group?)")
|
||||
return assemblies or True
|
||||
|
||||
def _is_visible(node):
|
||||
|
|
|
|||
|
|
@ -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
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
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
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
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,29 @@ def _load_modules():
|
|||
|
||||
log = PypeLogger.get_logger("ModulesLoader")
|
||||
|
||||
# Look for OpenPype modules in paths defined with `get_module_dirs`
|
||||
dirpaths = get_module_dirs()
|
||||
# Import default modules imported from 'openpype.modules'
|
||||
for default_module_name in (
|
||||
"settings_action",
|
||||
"launcher_action",
|
||||
"project_manager_action",
|
||||
"standalonepublish_action",
|
||||
):
|
||||
try:
|
||||
default_module = __import__(
|
||||
"openpype.modules.{}".format(default_module_name),
|
||||
fromlist=("", )
|
||||
)
|
||||
setattr(openpype_modules, default_module_name, default_module)
|
||||
|
||||
except Exception:
|
||||
msg = (
|
||||
"Failed to import default module '{}'."
|
||||
).format(default_module_name)
|
||||
log.error(msg, exc_info=True)
|
||||
|
||||
# Look for OpenPype modules in paths defined with `get_module_dirs`
|
||||
# - dynamically imported OpenPype modules and addons
|
||||
dirpaths = get_module_dirs()
|
||||
for dirpath in dirpaths:
|
||||
if not os.path.exists(dirpath):
|
||||
log.warning((
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
from .settings_action import (
|
||||
LocalSettingsAction,
|
||||
SettingsAction
|
||||
)
|
||||
|
||||
__all__ = (
|
||||
"LocalSettingsAction",
|
||||
"SettingsAction"
|
||||
)
|
||||
|
|
@ -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"]
|
||||
|
|
|
|||
|
|
@ -1010,10 +1010,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
|
||||
|
|
|
|||
|
|
@ -275,6 +275,11 @@
|
|||
"optional": true,
|
||||
"active": true
|
||||
},
|
||||
"ValidateModelContent": {
|
||||
"enabled": true,
|
||||
"optional": false,
|
||||
"validate_top_group": true
|
||||
},
|
||||
"ValidateNoAnimation": {
|
||||
"enabled": false,
|
||||
"optional": true,
|
||||
|
|
|
|||
|
|
@ -214,6 +214,30 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "ValidateModelContent",
|
||||
"label": "Validate Model Content",
|
||||
"checkbox_key": "enabled",
|
||||
"children": [
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "enabled",
|
||||
"label": "Enabled"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "optional",
|
||||
"label": "Optional"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "validate_top_group",
|
||||
"label": "Validate one top group"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -101,7 +101,8 @@ class LookModel(models.TreeModel):
|
|||
for look in asset_item["looks"]:
|
||||
look_subsets[look["name"]].append(asset)
|
||||
|
||||
for subset, assets in sorted(look_subsets.iteritems()):
|
||||
for subset in sorted(look_subsets.keys()):
|
||||
assets = look_subsets[subset]
|
||||
|
||||
# Define nice label without "look" prefix for readability
|
||||
label = subset if not subset.startswith("look") else subset[4:]
|
||||
|
|
|
|||
|
|
@ -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
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
BIN
openpype/tools/settings/settings/images/add.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 778 B |
BIN
openpype/tools/settings/settings/images/confirm.png
Normal file
BIN
openpype/tools/settings/settings/images/confirm.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 853 B |
BIN
openpype/tools/settings/settings/images/down.png
Normal file
BIN
openpype/tools/settings/settings/images/down.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 820 B |
BIN
openpype/tools/settings/settings/images/mask.png
Normal file
BIN
openpype/tools/settings/settings/images/mask.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 720 B |
BIN
openpype/tools/settings/settings/images/remove.png
Normal file
BIN
openpype/tools/settings/settings/images/remove.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 767 B |
BIN
openpype/tools/settings/settings/images/up.png
Normal file
BIN
openpype/tools/settings/settings/images/up.png
Normal file
Binary file not shown.
|
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)
|
||||
|
|
|
|||
|
|
@ -370,8 +370,12 @@ class PypeTrayStarter(QtCore.QObject):
|
|||
splash = self._get_splash()
|
||||
splash.show()
|
||||
self._tray_widget.show()
|
||||
# Make sure tray and splash are painted out
|
||||
QtWidgets.QApplication.processEvents()
|
||||
|
||||
elif self._timer_counter == 1:
|
||||
# Second processing of events to make sure splash is painted
|
||||
QtWidgets.QApplication.processEvents()
|
||||
self._timer_counter += 1
|
||||
self._tray_widget.initialize_modules()
|
||||
|
||||
|
|
|
|||
|
|
@ -347,14 +347,22 @@ class AssetModel(QtGui.QStandardItemModel):
|
|||
|
||||
return self.get_indexes_by_asset_ids(asset_ids)
|
||||
|
||||
def refresh(self, force=False):
|
||||
"""Refresh the data for the model."""
|
||||
def refresh(self, force=False, clear=False):
|
||||
"""Refresh the data for the model.
|
||||
|
||||
Args:
|
||||
force (bool): Stop currently running refresh start new refresh.
|
||||
clear (bool): Clear model before refresh thread starts.
|
||||
"""
|
||||
# Skip fetch if there is already other thread fetching documents
|
||||
if self._refreshing:
|
||||
if not force:
|
||||
return
|
||||
self.stop_refresh()
|
||||
|
||||
if clear:
|
||||
self._clear_items()
|
||||
|
||||
# Fetch documents from mongo
|
||||
# Restart payload
|
||||
self._refreshing = True
|
||||
|
|
@ -379,15 +387,18 @@ class AssetModel(QtGui.QStandardItemModel):
|
|||
continue
|
||||
item.setData(colors, ASSET_UNDERLINE_COLORS_ROLE)
|
||||
|
||||
def _clear_items(self):
|
||||
root_item = self.invisibleRootItem()
|
||||
root_item.removeRows(0, root_item.rowCount())
|
||||
self._items_by_asset_id = {}
|
||||
self._items_with_color_by_id = {}
|
||||
|
||||
def _on_docs_fetched(self):
|
||||
# Make sure refreshing did not change
|
||||
# - since this line is refreshing sequential and
|
||||
# triggering of new refresh will happen when this method is done
|
||||
if not self._refreshing:
|
||||
root_item = self.invisibleRootItem()
|
||||
root_item.removeRows(0, root_item.rowCount())
|
||||
self._items_by_asset_id = {}
|
||||
self._items_with_color_by_id = {}
|
||||
self._clear_items()
|
||||
return
|
||||
|
||||
# Collect asset documents as needed
|
||||
|
|
@ -419,8 +430,6 @@ class AssetModel(QtGui.QStandardItemModel):
|
|||
parent_id, parent_item = asset_items_queue.popleft()
|
||||
# Skip if there are no children
|
||||
children_ids = asset_ids_by_parents[parent_id]
|
||||
if not children_ids:
|
||||
continue
|
||||
|
||||
# Go through current children of parent item
|
||||
# - find out items that were deleted and skip creation of already
|
||||
|
|
@ -626,6 +635,7 @@ class AssetsWidget(QtWidgets.QWidget):
|
|||
self._model = model
|
||||
self._proxy = proxy
|
||||
self._view = view
|
||||
self._last_project_name = None
|
||||
|
||||
self.model_selection = {}
|
||||
|
||||
|
|
@ -634,7 +644,12 @@ class AssetsWidget(QtWidgets.QWidget):
|
|||
return self._model.refreshing
|
||||
|
||||
def refresh(self):
|
||||
self._refresh_model()
|
||||
project_name = self.dbcon.Session.get("AVALON_PROJECT")
|
||||
clear_model = False
|
||||
if project_name != self._last_project_name:
|
||||
clear_model = True
|
||||
self._last_project_name = project_name
|
||||
self._refresh_model(clear_model)
|
||||
|
||||
def stop_refresh(self):
|
||||
self._model.stop_refresh()
|
||||
|
|
@ -680,14 +695,14 @@ class AssetsWidget(QtWidgets.QWidget):
|
|||
self._set_loading_state(loading=False, empty=not has_item)
|
||||
self.refreshed.emit()
|
||||
|
||||
def _refresh_model(self):
|
||||
def _refresh_model(self, clear=False):
|
||||
# Store selection
|
||||
self._set_loading_state(loading=True, empty=True)
|
||||
|
||||
# Trigger signal before refresh is called
|
||||
self.refresh_triggered.emit()
|
||||
# Refresh model
|
||||
self._model.refresh()
|
||||
self._model.refresh(clear=clear)
|
||||
|
||||
def _set_loading_state(self, loading, empty):
|
||||
self._view.set_loading_state(loading, empty)
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ use singleton approach with global functions (using helper anyway).
|
|||
"""
|
||||
|
||||
import avalon.api
|
||||
from .lib import qt_app_context
|
||||
|
||||
|
||||
class HostToolsHelper:
|
||||
|
|
@ -61,22 +62,23 @@ class HostToolsHelper:
|
|||
if save is None:
|
||||
save = True
|
||||
|
||||
workfiles_tool = self.get_workfiles_tool(parent)
|
||||
workfiles_tool.set_save_enabled(save)
|
||||
with qt_app_context():
|
||||
workfiles_tool = self.get_workfiles_tool(parent)
|
||||
workfiles_tool.set_save_enabled(save)
|
||||
|
||||
if not workfiles_tool.isVisible():
|
||||
workfiles_tool.show()
|
||||
if not workfiles_tool.isVisible():
|
||||
workfiles_tool.show()
|
||||
|
||||
if use_context:
|
||||
context = {
|
||||
"asset": avalon.api.Session["AVALON_ASSET"],
|
||||
"task": avalon.api.Session["AVALON_TASK"]
|
||||
}
|
||||
workfiles_tool.set_context(context)
|
||||
if use_context:
|
||||
context = {
|
||||
"asset": avalon.api.Session["AVALON_ASSET"],
|
||||
"task": avalon.api.Session["AVALON_TASK"]
|
||||
}
|
||||
workfiles_tool.set_context(context)
|
||||
|
||||
# Pull window to the front.
|
||||
workfiles_tool.raise_()
|
||||
workfiles_tool.activateWindow()
|
||||
# Pull window to the front.
|
||||
workfiles_tool.raise_()
|
||||
workfiles_tool.activateWindow()
|
||||
|
||||
def get_loader_tool(self, parent):
|
||||
"""Create, cache and return loader tool window."""
|
||||
|
|
@ -90,20 +92,21 @@ class HostToolsHelper:
|
|||
|
||||
def show_loader(self, parent=None, use_context=None):
|
||||
"""Loader tool for loading representations."""
|
||||
loader_tool = self.get_loader_tool(parent)
|
||||
with qt_app_context():
|
||||
loader_tool = self.get_loader_tool(parent)
|
||||
|
||||
loader_tool.show()
|
||||
loader_tool.raise_()
|
||||
loader_tool.activateWindow()
|
||||
loader_tool.show()
|
||||
loader_tool.raise_()
|
||||
loader_tool.activateWindow()
|
||||
|
||||
if use_context is None:
|
||||
use_context = False
|
||||
if use_context is None:
|
||||
use_context = False
|
||||
|
||||
if use_context:
|
||||
context = {"asset": avalon.api.Session["AVALON_ASSET"]}
|
||||
loader_tool.set_context(context, refresh=True)
|
||||
else:
|
||||
loader_tool.refresh()
|
||||
if use_context:
|
||||
context = {"asset": avalon.api.Session["AVALON_ASSET"]}
|
||||
loader_tool.set_context(context, refresh=True)
|
||||
else:
|
||||
loader_tool.refresh()
|
||||
|
||||
def get_creator_tool(self, parent):
|
||||
"""Create, cache and return creator tool window."""
|
||||
|
|
@ -117,13 +120,14 @@ class HostToolsHelper:
|
|||
|
||||
def show_creator(self, parent=None):
|
||||
"""Show tool to create new instantes for publishing."""
|
||||
creator_tool = self.get_creator_tool(parent)
|
||||
creator_tool.refresh()
|
||||
creator_tool.show()
|
||||
with qt_app_context():
|
||||
creator_tool = self.get_creator_tool(parent)
|
||||
creator_tool.refresh()
|
||||
creator_tool.show()
|
||||
|
||||
# Pull window to the front.
|
||||
creator_tool.raise_()
|
||||
creator_tool.activateWindow()
|
||||
# Pull window to the front.
|
||||
creator_tool.raise_()
|
||||
creator_tool.activateWindow()
|
||||
|
||||
def get_subset_manager_tool(self, parent):
|
||||
"""Create, cache and return subset manager tool window."""
|
||||
|
|
@ -139,12 +143,13 @@ class HostToolsHelper:
|
|||
|
||||
def show_subset_manager(self, parent=None):
|
||||
"""Show tool display/remove existing created instances."""
|
||||
subset_manager_tool = self.get_subset_manager_tool(parent)
|
||||
subset_manager_tool.show()
|
||||
with qt_app_context():
|
||||
subset_manager_tool = self.get_subset_manager_tool(parent)
|
||||
subset_manager_tool.show()
|
||||
|
||||
# Pull window to the front.
|
||||
subset_manager_tool.raise_()
|
||||
subset_manager_tool.activateWindow()
|
||||
# Pull window to the front.
|
||||
subset_manager_tool.raise_()
|
||||
subset_manager_tool.activateWindow()
|
||||
|
||||
def get_scene_inventory_tool(self, parent):
|
||||
"""Create, cache and return scene inventory tool window."""
|
||||
|
|
@ -160,13 +165,14 @@ class HostToolsHelper:
|
|||
|
||||
def show_scene_inventory(self, parent=None):
|
||||
"""Show tool maintain loaded containers."""
|
||||
scene_inventory_tool = self.get_scene_inventory_tool(parent)
|
||||
scene_inventory_tool.show()
|
||||
scene_inventory_tool.refresh()
|
||||
with qt_app_context():
|
||||
scene_inventory_tool = self.get_scene_inventory_tool(parent)
|
||||
scene_inventory_tool.show()
|
||||
scene_inventory_tool.refresh()
|
||||
|
||||
# Pull window to the front.
|
||||
scene_inventory_tool.raise_()
|
||||
scene_inventory_tool.activateWindow()
|
||||
# Pull window to the front.
|
||||
scene_inventory_tool.raise_()
|
||||
scene_inventory_tool.activateWindow()
|
||||
|
||||
def get_library_loader_tool(self, parent):
|
||||
"""Create, cache and return library loader tool window."""
|
||||
|
|
@ -182,11 +188,12 @@ class HostToolsHelper:
|
|||
|
||||
def show_library_loader(self, parent=None):
|
||||
"""Loader tool for loading representations from library project."""
|
||||
library_loader_tool = self.get_library_loader_tool(parent)
|
||||
library_loader_tool.show()
|
||||
library_loader_tool.raise_()
|
||||
library_loader_tool.activateWindow()
|
||||
library_loader_tool.refresh()
|
||||
with qt_app_context():
|
||||
library_loader_tool = self.get_library_loader_tool(parent)
|
||||
library_loader_tool.show()
|
||||
library_loader_tool.raise_()
|
||||
library_loader_tool.activateWindow()
|
||||
library_loader_tool.refresh()
|
||||
|
||||
def show_publish(self, parent=None):
|
||||
"""Publish UI."""
|
||||
|
|
@ -207,9 +214,10 @@ class HostToolsHelper:
|
|||
"""Look manager is Maya specific tool for look management."""
|
||||
from avalon import style
|
||||
|
||||
look_assigner_tool = self.get_look_assigner_tool(parent)
|
||||
look_assigner_tool.show()
|
||||
look_assigner_tool.setStyleSheet(style.load_stylesheet())
|
||||
with qt_app_context():
|
||||
look_assigner_tool = self.get_look_assigner_tool(parent)
|
||||
look_assigner_tool.show()
|
||||
look_assigner_tool.setStyleSheet(style.load_stylesheet())
|
||||
|
||||
def get_experimental_tools_dialog(self, parent=None):
|
||||
"""Dialog of experimental tools.
|
||||
|
|
@ -232,11 +240,12 @@ class HostToolsHelper:
|
|||
|
||||
def show_experimental_tools_dialog(self, parent=None):
|
||||
"""Show dialog with experimental tools."""
|
||||
dialog = self.get_experimental_tools_dialog(parent)
|
||||
with qt_app_context():
|
||||
dialog = self.get_experimental_tools_dialog(parent)
|
||||
|
||||
dialog.show()
|
||||
dialog.raise_()
|
||||
dialog.activateWindow()
|
||||
dialog.show()
|
||||
dialog.raise_()
|
||||
dialog.activateWindow()
|
||||
|
||||
def get_tool_by_name(self, tool_name, parent=None, *args, **kwargs):
|
||||
"""Show tool by it's name.
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,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
|
||||
|
||||
|
|
|
|||
28
start.py
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:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue