Merge branch 'develop' into feature/extract_review_bg_color
|
|
@ -23,18 +23,32 @@ def add_implementation_envs(env, _app):
|
|||
env["PYTHONPATH"] = os.pathsep.join(python_path_parts)
|
||||
|
||||
# Modify Blender user scripts path
|
||||
previous_user_scripts = set()
|
||||
# Implementation path is added to set for easier paths check inside loops
|
||||
# - will be removed at the end
|
||||
previous_user_scripts.add(implementation_user_script_path)
|
||||
|
||||
openpype_blender_user_scripts = (
|
||||
env.get("OPENPYPE_BLENDER_USER_SCRIPTS") or ""
|
||||
)
|
||||
for path in openpype_blender_user_scripts.split(os.pathsep):
|
||||
if path and os.path.exists(path):
|
||||
previous_user_scripts.add(os.path.normpath(path))
|
||||
|
||||
blender_user_scripts = env.get("BLENDER_USER_SCRIPTS") or ""
|
||||
previous_user_scripts = []
|
||||
for path in blender_user_scripts.split(os.pathsep):
|
||||
if path and os.path.exists(path):
|
||||
path = os.path.normpath(path)
|
||||
if path != implementation_user_script_path:
|
||||
previous_user_scripts.append(path)
|
||||
previous_user_scripts.add(os.path.normpath(path))
|
||||
|
||||
# Remove implementation path from user script paths as is set to
|
||||
# `BLENDER_USER_SCRIPTS`
|
||||
previous_user_scripts.remove(implementation_user_script_path)
|
||||
env["BLENDER_USER_SCRIPTS"] = implementation_user_script_path
|
||||
|
||||
# Set custom user scripts env
|
||||
env["OPENPYPE_BLENDER_USER_SCRIPTS"] = os.pathsep.join(
|
||||
previous_user_scripts
|
||||
)
|
||||
env["BLENDER_USER_SCRIPTS"] = implementation_user_script_path
|
||||
|
||||
# Define Qt binding if not defined
|
||||
if not env.get("QT_PREFERRED_BINDING"):
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ import traceback
|
|||
|
||||
import bpy
|
||||
|
||||
from .lib import append_user_scripts
|
||||
|
||||
from avalon import api as avalon
|
||||
from pyblish import api as pyblish
|
||||
|
||||
|
|
@ -29,7 +31,7 @@ def install():
|
|||
pyblish.register_plugin_path(str(PUBLISH_PATH))
|
||||
avalon.register_plugin_path(avalon.Loader, str(LOAD_PATH))
|
||||
avalon.register_plugin_path(avalon.Creator, str(CREATE_PATH))
|
||||
|
||||
append_user_scripts()
|
||||
avalon.on("new", on_new)
|
||||
avalon.on("open", on_open)
|
||||
|
||||
|
|
|
|||
127
openpype/hosts/blender/api/lib.py
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
import os
|
||||
import traceback
|
||||
import importlib
|
||||
|
||||
import bpy
|
||||
import addon_utils
|
||||
|
||||
|
||||
def load_scripts(paths):
|
||||
"""Copy of `load_scripts` from Blender's implementation.
|
||||
|
||||
It is possible that whis function will be changed in future and usage will
|
||||
be based on Blender version.
|
||||
"""
|
||||
import bpy_types
|
||||
|
||||
loaded_modules = set()
|
||||
|
||||
previous_classes = [
|
||||
cls
|
||||
for cls in bpy.types.bpy_struct.__subclasses__()
|
||||
]
|
||||
|
||||
def register_module_call(mod):
|
||||
register = getattr(mod, "register", None)
|
||||
if register:
|
||||
try:
|
||||
register()
|
||||
except:
|
||||
traceback.print_exc()
|
||||
else:
|
||||
print("\nWarning! '%s' has no register function, "
|
||||
"this is now a requirement for registerable scripts" %
|
||||
mod.__file__)
|
||||
|
||||
def unregister_module_call(mod):
|
||||
unregister = getattr(mod, "unregister", None)
|
||||
if unregister:
|
||||
try:
|
||||
unregister()
|
||||
except:
|
||||
traceback.print_exc()
|
||||
|
||||
def test_reload(mod):
|
||||
# reloading this causes internal errors
|
||||
# because the classes from this module are stored internally
|
||||
# possibly to refresh internal references too but for now, best not to.
|
||||
if mod == bpy_types:
|
||||
return mod
|
||||
|
||||
try:
|
||||
return importlib.reload(mod)
|
||||
except:
|
||||
traceback.print_exc()
|
||||
|
||||
def test_register(mod):
|
||||
if mod:
|
||||
register_module_call(mod)
|
||||
bpy.utils._global_loaded_modules.append(mod.__name__)
|
||||
|
||||
from bpy_restrict_state import RestrictBlend
|
||||
|
||||
with RestrictBlend():
|
||||
for base_path in paths:
|
||||
for path_subdir in bpy.utils._script_module_dirs:
|
||||
path = os.path.join(base_path, path_subdir)
|
||||
if not os.path.isdir(path):
|
||||
continue
|
||||
|
||||
bpy.utils._sys_path_ensure_prepend(path)
|
||||
|
||||
# Only add to 'sys.modules' unless this is 'startup'.
|
||||
if path_subdir != "startup":
|
||||
continue
|
||||
for mod in bpy.utils.modules_from_path(path, loaded_modules):
|
||||
test_register(mod)
|
||||
|
||||
addons_paths = []
|
||||
for base_path in paths:
|
||||
addons_path = os.path.join(base_path, "addons")
|
||||
if not os.path.exists(addons_path):
|
||||
continue
|
||||
addons_paths.append(addons_path)
|
||||
addons_module_path = os.path.join(addons_path, "modules")
|
||||
if os.path.exists(addons_module_path):
|
||||
bpy.utils._sys_path_ensure_prepend(addons_module_path)
|
||||
|
||||
if addons_paths:
|
||||
# Fake addons
|
||||
origin_paths = addon_utils.paths
|
||||
|
||||
def new_paths():
|
||||
paths = origin_paths() + addons_paths
|
||||
return paths
|
||||
|
||||
addon_utils.paths = new_paths
|
||||
addon_utils.modules_refresh()
|
||||
|
||||
# load template (if set)
|
||||
if any(bpy.utils.app_template_paths()):
|
||||
import bl_app_template_utils
|
||||
bl_app_template_utils.reset(reload_scripts=False)
|
||||
del bl_app_template_utils
|
||||
|
||||
for cls in bpy.types.bpy_struct.__subclasses__():
|
||||
if cls in previous_classes:
|
||||
continue
|
||||
if not getattr(cls, "is_registered", False):
|
||||
continue
|
||||
for subcls in cls.__subclasses__():
|
||||
if not subcls.is_registered:
|
||||
print(
|
||||
"Warning, unregistered class: %s(%s)" %
|
||||
(subcls.__name__, cls.__name__)
|
||||
)
|
||||
|
||||
|
||||
def append_user_scripts():
|
||||
user_scripts = os.environ.get("OPENPYPE_BLENDER_USER_SCRIPTS")
|
||||
if not user_scripts:
|
||||
return
|
||||
|
||||
try:
|
||||
load_scripts(user_scripts.split(os.pathsep))
|
||||
except Exception:
|
||||
print("Couldn't load user scripts \"{}\"".format(user_scripts))
|
||||
traceback.print_exc()
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
import bpy
|
||||
|
||||
from avalon import api, blender
|
||||
import openpype.hosts.blender.api.plugin
|
||||
|
||||
|
||||
class CreateSetDress(openpype.hosts.blender.api.plugin.Creator):
|
||||
"""A grouped package of loaded content"""
|
||||
|
||||
name = "setdressMain"
|
||||
label = "Set Dress"
|
||||
family = "setdress"
|
||||
icon = "cubes"
|
||||
defaults = ["Main", "Anim"]
|
||||
|
||||
def process(self):
|
||||
asset = self.data["asset"]
|
||||
subset = self.data["subset"]
|
||||
name = openpype.hosts.blender.api.plugin.asset_name(asset, subset)
|
||||
collection = bpy.data.collections.new(name=name)
|
||||
bpy.context.scene.collection.children.link(collection)
|
||||
self.data['task'] = api.Session.get('AVALON_TASK')
|
||||
blender.lib.imprint(collection, self.data)
|
||||
|
||||
return collection
|
||||
|
|
@ -25,9 +25,6 @@ class BlendLayoutLoader(plugin.AssetLoader):
|
|||
icon = "code-fork"
|
||||
color = "orange"
|
||||
|
||||
animation_creator_name = "CreateAnimation"
|
||||
setdress_creator_name = "CreateSetDress"
|
||||
|
||||
def _remove(self, objects, obj_container):
|
||||
for obj in list(objects):
|
||||
if obj.type == 'ARMATURE':
|
||||
|
|
@ -293,7 +290,6 @@ class UnrealLayoutLoader(plugin.AssetLoader):
|
|||
color = "orange"
|
||||
|
||||
animation_creator_name = "CreateAnimation"
|
||||
setdress_creator_name = "CreateSetDress"
|
||||
|
||||
def _remove_objects(self, objects):
|
||||
for obj in list(objects):
|
||||
|
|
@ -383,7 +379,7 @@ class UnrealLayoutLoader(plugin.AssetLoader):
|
|||
|
||||
def _process(
|
||||
self, libpath, layout_container, container_name, representation,
|
||||
actions, parent
|
||||
actions, parent_collection
|
||||
):
|
||||
with open(libpath, "r") as fp:
|
||||
data = json.load(fp)
|
||||
|
|
@ -392,6 +388,11 @@ class UnrealLayoutLoader(plugin.AssetLoader):
|
|||
layout_collection = bpy.data.collections.new(container_name)
|
||||
scene.collection.children.link(layout_collection)
|
||||
|
||||
parent = parent_collection
|
||||
|
||||
if parent is None:
|
||||
parent = scene.collection
|
||||
|
||||
all_loaders = api.discover(api.Loader)
|
||||
|
||||
avalon_container = bpy.data.collections.get(
|
||||
|
|
@ -516,23 +517,9 @@ class UnrealLayoutLoader(plugin.AssetLoader):
|
|||
container_metadata["libpath"] = libpath
|
||||
container_metadata["lib_container"] = lib_container
|
||||
|
||||
# Create a setdress subset to contain all the animation for all
|
||||
# the rigs in the layout
|
||||
creator_plugin = get_creator_by_name(self.setdress_creator_name)
|
||||
if not creator_plugin:
|
||||
raise ValueError("Creator plugin \"{}\" was not found.".format(
|
||||
self.setdress_creator_name
|
||||
))
|
||||
parent = api.create(
|
||||
creator_plugin,
|
||||
name="animation",
|
||||
asset=api.Session["AVALON_ASSET"],
|
||||
options={"useSelection": True},
|
||||
data={"dependencies": str(context["representation"]["_id"])})
|
||||
|
||||
layout_collection = self._process(
|
||||
libpath, layout_container, container_name,
|
||||
str(context["representation"]["_id"]), None, parent)
|
||||
str(context["representation"]["_id"]), None, None)
|
||||
|
||||
container_metadata["obj_container"] = layout_collection
|
||||
|
||||
|
|
|
|||
|
|
@ -107,6 +107,9 @@ class BlendRigLoader(plugin.AssetLoader):
|
|||
|
||||
if action is not None:
|
||||
local_obj.animation_data.action = action
|
||||
elif local_obj.animation_data.action is not None:
|
||||
plugin.prepare_data(
|
||||
local_obj.animation_data.action, collection_name)
|
||||
|
||||
# Set link the drivers to the local object
|
||||
if local_obj.data.animation_data:
|
||||
|
|
|
|||
|
|
@ -1,61 +0,0 @@
|
|||
import os
|
||||
import json
|
||||
|
||||
import openpype.api
|
||||
import pyblish.api
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
class ExtractSetDress(openpype.api.Extractor):
|
||||
"""Extract setdress."""
|
||||
|
||||
label = "Extract SetDress"
|
||||
hosts = ["blender"]
|
||||
families = ["setdress"]
|
||||
optional = True
|
||||
order = pyblish.api.ExtractorOrder + 0.1
|
||||
|
||||
def process(self, instance):
|
||||
stagingdir = self.staging_dir(instance)
|
||||
|
||||
json_data = []
|
||||
|
||||
for i in instance.context:
|
||||
collection = i.data.get("name")
|
||||
container = None
|
||||
for obj in bpy.data.collections[collection].objects:
|
||||
if obj.type == "ARMATURE":
|
||||
container_name = obj.get("avalon").get("container_name")
|
||||
container = bpy.data.collections[container_name]
|
||||
if container:
|
||||
json_dict = {
|
||||
"subset": i.data.get("subset"),
|
||||
"container": container.name,
|
||||
}
|
||||
json_dict["instance_name"] = container.get("avalon").get(
|
||||
"instance_name"
|
||||
)
|
||||
json_data.append(json_dict)
|
||||
|
||||
if "representations" not in instance.data:
|
||||
instance.data["representations"] = []
|
||||
|
||||
json_filename = f"{instance.name}.json"
|
||||
json_path = os.path.join(stagingdir, json_filename)
|
||||
|
||||
with open(json_path, "w+") as file:
|
||||
json.dump(json_data, fp=file, indent=2)
|
||||
|
||||
json_representation = {
|
||||
"name": "json",
|
||||
"ext": "json",
|
||||
"files": json_filename,
|
||||
"stagingDir": stagingdir,
|
||||
}
|
||||
instance.data["representations"].append(json_representation)
|
||||
|
||||
self.log.info(
|
||||
"Extracted instance '{}' to: {}".format(instance.name,
|
||||
json_representation)
|
||||
)
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import os
|
||||
import json
|
||||
|
||||
import openpype.api
|
||||
|
||||
|
|
@ -121,6 +122,25 @@ class ExtractAnimationFBX(openpype.api.Extractor):
|
|||
pair[1].user_clear()
|
||||
bpy.data.actions.remove(pair[1])
|
||||
|
||||
json_filename = f"{instance.name}.json"
|
||||
json_path = os.path.join(stagingdir, json_filename)
|
||||
|
||||
json_dict = {}
|
||||
|
||||
collection = instance.data.get("name")
|
||||
container = None
|
||||
for obj in bpy.data.collections[collection].objects:
|
||||
if obj.type == "ARMATURE":
|
||||
container_name = obj.get("avalon").get("container_name")
|
||||
container = bpy.data.collections[container_name]
|
||||
if container:
|
||||
json_dict = {
|
||||
"instance_name": container.get("avalon").get("instance_name")
|
||||
}
|
||||
|
||||
with open(json_path, "w+") as file:
|
||||
json.dump(json_dict, fp=file, indent=2)
|
||||
|
||||
if "representations" not in instance.data:
|
||||
instance.data["representations"] = []
|
||||
|
||||
|
|
@ -130,7 +150,15 @@ class ExtractAnimationFBX(openpype.api.Extractor):
|
|||
'files': fbx_filename,
|
||||
"stagingDir": stagingdir,
|
||||
}
|
||||
json_representation = {
|
||||
'name': 'json',
|
||||
'ext': 'json',
|
||||
'files': json_filename,
|
||||
"stagingDir": stagingdir,
|
||||
}
|
||||
instance.data["representations"].append(fbx_representation)
|
||||
instance.data["representations"].append(json_representation)
|
||||
|
||||
|
||||
self.log.info("Extracted instance '{}' to: {}".format(
|
||||
instance.name, fbx_representation))
|
||||
|
|
|
|||
|
|
@ -53,11 +53,10 @@ def get_created_node_imageio_setting(**kwarg):
|
|||
imageio_node = None
|
||||
for node in imageio_nodes:
|
||||
log.info(node)
|
||||
if (node["nukeNodeClass"] != nodeclass) and (
|
||||
creator not in node["plugins"]):
|
||||
continue
|
||||
|
||||
imageio_node = node
|
||||
if (nodeclass in node["nukeNodeClass"]) and (
|
||||
creator in node["plugins"]):
|
||||
imageio_node = node
|
||||
break
|
||||
|
||||
log.info("ImageIO node: {}".format(imageio_node))
|
||||
return imageio_node
|
||||
|
|
@ -340,9 +339,9 @@ def create_write_node(name, data, input=None, prenodes=None, review=True):
|
|||
nuke.message(msg)
|
||||
|
||||
# build file path to workfiles
|
||||
fpath = str(anatomy_filled["work"]["folder"]).replace("\\", "/")
|
||||
fdir = str(anatomy_filled["work"]["folder"]).replace("\\", "/")
|
||||
fpath = data["fpath_template"].format(
|
||||
work=fpath, version=data["version"], subset=data["subset"],
|
||||
work=fdir, version=data["version"], subset=data["subset"],
|
||||
frame=data["frame"],
|
||||
ext=representation
|
||||
)
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ class CreateImage(openpype.api.Creator):
|
|||
name = "imageDefault"
|
||||
label = "Image"
|
||||
family = "image"
|
||||
defaults = ["Main"]
|
||||
|
||||
def process(self):
|
||||
groups = []
|
||||
|
|
@ -16,7 +17,9 @@ class CreateImage(openpype.api.Creator):
|
|||
create_group = False
|
||||
|
||||
stub = photoshop.stub()
|
||||
useSelection = False
|
||||
if (self.options or {}).get("useSelection"):
|
||||
useSelection = True
|
||||
multiple_instances = False
|
||||
selection = stub.get_selected_layers()
|
||||
self.log.info("selection {}".format(selection))
|
||||
|
|
@ -61,7 +64,9 @@ class CreateImage(openpype.api.Creator):
|
|||
# No selection creates an empty group.
|
||||
create_group = True
|
||||
else:
|
||||
create_group = True
|
||||
stub.select_layers(stub.get_layers())
|
||||
group = stub.group_selected_layers(self.name)
|
||||
groups.append(group)
|
||||
|
||||
if create_group:
|
||||
group = stub.create_group(self.name)
|
||||
|
|
@ -77,13 +82,19 @@ class CreateImage(openpype.api.Creator):
|
|||
group.name = group.name.replace(stub.PUBLISH_ICON, ''). \
|
||||
replace(stub.LOADED_ICON, '')
|
||||
|
||||
if useSelection:
|
||||
subset_name = self.data["subset"] + group.name
|
||||
else:
|
||||
# use value provided by user from Creator
|
||||
subset_name = self.data["subset"]
|
||||
|
||||
if group.long_name:
|
||||
for directory in group.long_name[::-1]:
|
||||
name = directory.replace(stub.PUBLISH_ICON, '').\
|
||||
replace(stub.LOADED_ICON, '')
|
||||
long_names.append(name)
|
||||
|
||||
self.data.update({"subset": "image" + group.name})
|
||||
self.data.update({"subset": subset_name})
|
||||
self.data.update({"uuid": str(group.id)})
|
||||
self.data.update({"long_name": "_".join(long_names)})
|
||||
stub.imprint(group, self.data)
|
||||
|
|
|
|||
|
|
@ -35,21 +35,16 @@ class ExtractImage(openpype.api.Extractor):
|
|||
if layer.visible and layer.id not in extract_ids:
|
||||
stub.set_visible(layer.id, False)
|
||||
|
||||
save_options = []
|
||||
if "png" in self.formats:
|
||||
save_options.append('png')
|
||||
if "jpg" in self.formats:
|
||||
save_options.append('jpg')
|
||||
|
||||
file_basename = os.path.splitext(
|
||||
stub.get_active_document_name()
|
||||
)[0]
|
||||
for extension in save_options:
|
||||
for extension in self.formats:
|
||||
_filename = "{}.{}".format(file_basename, extension)
|
||||
files[extension] = _filename
|
||||
|
||||
full_filename = os.path.join(staging_dir, _filename)
|
||||
stub.saveAs(full_filename, extension, True)
|
||||
self.log.info(f"Extracted: {extension}")
|
||||
|
||||
representations = []
|
||||
for extension, filename in files.items():
|
||||
|
|
|
|||
|
|
@ -6,7 +6,12 @@ from avalon import photoshop
|
|||
|
||||
|
||||
class ExtractReview(openpype.api.Extractor):
|
||||
"""Produce a flattened image file from all instances."""
|
||||
"""
|
||||
Produce a flattened image file from all 'image' instances.
|
||||
|
||||
If no 'image' instance is created, it produces flattened image from
|
||||
all visible layers.
|
||||
"""
|
||||
|
||||
label = "Extract Review"
|
||||
hosts = ["photoshop"]
|
||||
|
|
@ -30,14 +35,15 @@ class ExtractReview(openpype.api.Extractor):
|
|||
)
|
||||
output_image_path = os.path.join(staging_dir, output_image)
|
||||
with photoshop.maintained_visibility():
|
||||
# Hide all other layers.
|
||||
extract_ids = set([ll.id for ll in stub.
|
||||
get_layers_in_layers(layers)])
|
||||
self.log.info("extract_ids {}".format(extract_ids))
|
||||
for layer in stub.get_layers():
|
||||
# limit unnecessary calls to client
|
||||
if layer.visible and layer.id not in extract_ids:
|
||||
stub.set_visible(layer.id, False)
|
||||
if layers:
|
||||
# Hide all other layers.
|
||||
extract_ids = set([ll.id for ll in stub.
|
||||
get_layers_in_layers(layers)])
|
||||
self.log.debug("extract_ids {}".format(extract_ids))
|
||||
for layer in stub.get_layers():
|
||||
# limit unnecessary calls to client
|
||||
if layer.visible and layer.id not in extract_ids:
|
||||
stub.set_visible(layer.id, False)
|
||||
|
||||
stub.saveAs(output_image_path, 'jpg', True)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import os
|
||||
import json
|
||||
|
||||
from avalon import api, pipeline
|
||||
from avalon.unreal import lib
|
||||
|
|
@ -61,10 +62,16 @@ class AnimationFBXLoader(api.Loader):
|
|||
task = unreal.AssetImportTask()
|
||||
task.options = unreal.FbxImportUI()
|
||||
|
||||
# If there are no options, the process cannot be automated
|
||||
if options:
|
||||
libpath = self.fname.replace("fbx", "json")
|
||||
|
||||
with open(libpath, "r") as fp:
|
||||
data = json.load(fp)
|
||||
|
||||
instance_name = data.get("instance_name")
|
||||
|
||||
if instance_name:
|
||||
automated = True
|
||||
actor_name = 'PersistentLevel.' + options.get('instance_name')
|
||||
actor_name = 'PersistentLevel.' + instance_name
|
||||
actor = unreal.EditorLevelLibrary.get_actor_reference(actor_name)
|
||||
skeleton = actor.skeletal_mesh_component.skeletal_mesh.skeleton
|
||||
task.options.set_editor_property('skeleton', skeleton)
|
||||
|
|
@ -81,16 +88,31 @@ class AnimationFBXLoader(api.Loader):
|
|||
|
||||
# set import options here
|
||||
task.options.set_editor_property(
|
||||
'automated_import_should_detect_type', True)
|
||||
'automated_import_should_detect_type', False)
|
||||
task.options.set_editor_property(
|
||||
'original_import_type', unreal.FBXImportType.FBXIT_ANIMATION)
|
||||
'original_import_type', unreal.FBXImportType.FBXIT_SKELETAL_MESH)
|
||||
task.options.set_editor_property(
|
||||
'mesh_type_to_import', unreal.FBXImportType.FBXIT_ANIMATION)
|
||||
task.options.set_editor_property('import_mesh', False)
|
||||
task.options.set_editor_property('import_animations', True)
|
||||
task.options.set_editor_property('override_full_name', True)
|
||||
|
||||
task.options.skeletal_mesh_import_data.set_editor_property(
|
||||
'import_content_type',
|
||||
unreal.FBXImportContentType.FBXICT_SKINNING_WEIGHTS
|
||||
task.options.anim_sequence_import_data.set_editor_property(
|
||||
'animation_length',
|
||||
unreal.FBXAnimationLengthImportType.FBXALIT_EXPORTED_TIME
|
||||
)
|
||||
task.options.anim_sequence_import_data.set_editor_property(
|
||||
'import_meshes_in_bone_hierarchy', False)
|
||||
task.options.anim_sequence_import_data.set_editor_property(
|
||||
'use_default_sample_rate', True)
|
||||
task.options.anim_sequence_import_data.set_editor_property(
|
||||
'import_custom_attribute', True)
|
||||
task.options.anim_sequence_import_data.set_editor_property(
|
||||
'import_bone_tracks', True)
|
||||
task.options.anim_sequence_import_data.set_editor_property(
|
||||
'remove_redundant_keys', True)
|
||||
task.options.anim_sequence_import_data.set_editor_property(
|
||||
'convert_scene', True)
|
||||
|
||||
unreal.AssetToolsHelpers.get_asset_tools().import_asset_tasks([task])
|
||||
|
||||
|
|
|
|||
|
|
@ -1,127 +0,0 @@
|
|||
import json
|
||||
|
||||
from avalon import api
|
||||
import unreal
|
||||
|
||||
|
||||
class AnimationCollectionLoader(api.Loader):
|
||||
"""Load Unreal SkeletalMesh from FBX"""
|
||||
|
||||
families = ["setdress"]
|
||||
representations = ["json"]
|
||||
|
||||
label = "Load Animation Collection"
|
||||
icon = "cube"
|
||||
color = "orange"
|
||||
|
||||
def load(self, context, name, namespace, options):
|
||||
from avalon import api, pipeline
|
||||
from avalon.unreal import lib
|
||||
from avalon.unreal import pipeline as unreal_pipeline
|
||||
import unreal
|
||||
|
||||
# Create directory for asset and avalon container
|
||||
root = "/Game/Avalon/Assets"
|
||||
asset = context.get('asset').get('name')
|
||||
suffix = "_CON"
|
||||
|
||||
tools = unreal.AssetToolsHelpers().get_asset_tools()
|
||||
asset_dir, container_name = tools.create_unique_asset_name(
|
||||
"{}/{}".format(root, asset), suffix="")
|
||||
|
||||
container_name += suffix
|
||||
|
||||
unreal.EditorAssetLibrary.make_directory(asset_dir)
|
||||
|
||||
libpath = self.fname
|
||||
|
||||
with open(libpath, "r") as fp:
|
||||
data = json.load(fp)
|
||||
|
||||
all_loaders = api.discover(api.Loader)
|
||||
|
||||
for element in data:
|
||||
reference = element.get('_id')
|
||||
|
||||
loaders = api.loaders_from_representation(all_loaders, reference)
|
||||
loader = None
|
||||
for l in loaders:
|
||||
if l.__name__ == "AnimationFBXLoader":
|
||||
loader = l
|
||||
break
|
||||
|
||||
if not loader:
|
||||
continue
|
||||
|
||||
instance_name = element.get('instance_name')
|
||||
|
||||
api.load(
|
||||
loader,
|
||||
reference,
|
||||
namespace=instance_name,
|
||||
options=element
|
||||
)
|
||||
|
||||
# Create Asset Container
|
||||
lib.create_avalon_container(
|
||||
container=container_name, path=asset_dir)
|
||||
|
||||
data = {
|
||||
"schema": "openpype:container-2.0",
|
||||
"id": pipeline.AVALON_CONTAINER_ID,
|
||||
"asset": asset,
|
||||
"namespace": asset_dir,
|
||||
"container_name": container_name,
|
||||
"loader": str(self.__class__.__name__),
|
||||
"representation": context["representation"]["_id"],
|
||||
"parent": context["representation"]["parent"],
|
||||
"family": context["representation"]["context"]["family"]
|
||||
}
|
||||
unreal_pipeline.imprint(
|
||||
"{}/{}".format(asset_dir, container_name), data)
|
||||
|
||||
asset_content = unreal.EditorAssetLibrary.list_assets(
|
||||
asset_dir, recursive=True, include_folder=True
|
||||
)
|
||||
|
||||
return asset_content
|
||||
|
||||
def update(self, container, representation):
|
||||
from avalon import api, io
|
||||
from avalon.unreal import pipeline
|
||||
|
||||
source_path = api.get_representation_path(representation)
|
||||
|
||||
with open(source_path, "r") as fp:
|
||||
data = json.load(fp)
|
||||
|
||||
animation_containers = [
|
||||
i for i in pipeline.ls() if
|
||||
i.get('asset') == container.get('asset') and
|
||||
i.get('family') == 'animation']
|
||||
|
||||
for element in data:
|
||||
new_version = io.find_one({"_id": io.ObjectId(element.get('_id'))})
|
||||
new_version_number = new_version.get('context').get('version')
|
||||
anim_container = None
|
||||
for i in animation_containers:
|
||||
if i.get('container_name') == (element.get('subset') + "_CON"):
|
||||
anim_container = i
|
||||
break
|
||||
if not anim_container:
|
||||
continue
|
||||
|
||||
api.update(anim_container, new_version_number)
|
||||
|
||||
container_path = "{}/{}".format(container["namespace"],
|
||||
container["objectName"])
|
||||
# update metadata
|
||||
pipeline.imprint(
|
||||
container_path,
|
||||
{
|
||||
"representation": str(representation["_id"]),
|
||||
"parent": str(representation["parent"])
|
||||
})
|
||||
|
||||
def remove(self, container):
|
||||
unreal.EditorAssetLibrary.delete_directory(container["namespace"])
|
||||
|
|
@ -65,12 +65,19 @@ class CollectAnatomyContextData(pyblish.api.ContextPlugin):
|
|||
"username": context.data["user"]
|
||||
}
|
||||
|
||||
app_manager = ApplicationManager()
|
||||
app_name = os.environ.get("AVALON_APP_NAME")
|
||||
if app_name:
|
||||
app = app_manager.applications.get(app_name)
|
||||
if app:
|
||||
context_data["app"] = app.host_name
|
||||
# Use AVALON_APP as first if available it is the same as host name
|
||||
# - only if is not defined use AVALON_APP_NAME (e.g. on Farm) and
|
||||
# set it back to AVALON_APP env variable
|
||||
host_name = os.environ.get("AVALON_APP")
|
||||
if not host_name:
|
||||
app_manager = ApplicationManager()
|
||||
app_name = os.environ.get("AVALON_APP_NAME")
|
||||
if app_name:
|
||||
app = app_manager.applications.get(app_name)
|
||||
if app:
|
||||
host_name = app.host_name
|
||||
os.environ["AVALON_APP"] = host_name
|
||||
context_data["app"] = host_name
|
||||
|
||||
datetime_data = context.data.get("datetimeData") or {}
|
||||
context_data.update(datetime_data)
|
||||
|
|
|
|||
17
openpype/settings/defaults/project_settings/photoshop.json
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"create": {
|
||||
"CreateImage": {
|
||||
"defaults": [
|
||||
"Main"
|
||||
]
|
||||
}
|
||||
},
|
||||
"publish": {
|
||||
"ExtractImage": {
|
||||
"formats": [
|
||||
"png",
|
||||
"jpg"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -16,5 +16,21 @@
|
|||
"active": true
|
||||
}
|
||||
},
|
||||
"load": {
|
||||
"LoadImage": {
|
||||
"defaults": {
|
||||
"stretch": true,
|
||||
"timestretch": true,
|
||||
"preload": true
|
||||
}
|
||||
},
|
||||
"ImportImage": {
|
||||
"defaults": {
|
||||
"stretch": true,
|
||||
"timestretch": true,
|
||||
"preload": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"filters": {}
|
||||
}
|
||||
|
|
@ -57,6 +57,7 @@ from .exceptions import (
|
|||
SchemaError,
|
||||
DefaultsNotDefined,
|
||||
StudioDefaultsNotDefined,
|
||||
BaseInvalidValueType,
|
||||
InvalidValueType,
|
||||
InvalidKeySymbols,
|
||||
SchemaMissingFileInfo,
|
||||
|
|
@ -96,7 +97,7 @@ from .input_entities import (
|
|||
PathInput,
|
||||
RawJsonEntity
|
||||
)
|
||||
|
||||
from .color_entity import ColorEntity
|
||||
from .enum_entity import (
|
||||
BaseEnumEntity,
|
||||
EnumEntity,
|
||||
|
|
@ -115,6 +116,7 @@ from .anatomy_entities import AnatomyEntity
|
|||
__all__ = (
|
||||
"DefaultsNotDefined",
|
||||
"StudioDefaultsNotDefined",
|
||||
"BaseInvalidValueType",
|
||||
"InvalidValueType",
|
||||
"InvalidKeySymbols",
|
||||
"SchemaMissingFileInfo",
|
||||
|
|
@ -146,6 +148,8 @@ __all__ = (
|
|||
"PathInput",
|
||||
"RawJsonEntity",
|
||||
|
||||
"ColorEntity",
|
||||
|
||||
"BaseEnumEntity",
|
||||
"EnumEntity",
|
||||
"AppsEnumEntity",
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ from .lib import (
|
|||
)
|
||||
|
||||
from .exceptions import (
|
||||
BaseInvalidValueType,
|
||||
InvalidValueType,
|
||||
SchemeGroupHierarchyBug,
|
||||
EntitySchemaError
|
||||
|
|
@ -377,7 +378,7 @@ class BaseItemEntity(BaseEntity):
|
|||
|
||||
try:
|
||||
new_value = self.convert_to_valid_type(value)
|
||||
except InvalidValueType:
|
||||
except BaseInvalidValueType:
|
||||
new_value = NOT_SET
|
||||
|
||||
if new_value is not NOT_SET:
|
||||
|
|
@ -846,6 +847,13 @@ class ItemEntity(BaseItemEntity):
|
|||
)
|
||||
raise EntitySchemaError(self, reason)
|
||||
|
||||
if self.is_file and self.file_item is not None:
|
||||
reason = (
|
||||
"Entity has set `is_file` to true but"
|
||||
" it's parent is already marked as file item."
|
||||
)
|
||||
raise EntitySchemaError(self, reason)
|
||||
|
||||
super(ItemEntity, self).schema_validations()
|
||||
|
||||
def create_schema_object(self, *args, **kwargs):
|
||||
|
|
|
|||
54
openpype/settings/entities/color_entity.py
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
from .lib import STRING_TYPE
|
||||
from .input_entities import InputEntity
|
||||
from .exceptions import (
|
||||
BaseInvalidValueType,
|
||||
InvalidValueType
|
||||
)
|
||||
|
||||
|
||||
class ColorEntity(InputEntity):
|
||||
schema_types = ["color"]
|
||||
|
||||
def _item_initalization(self):
|
||||
self.valid_value_types = (list, )
|
||||
self.value_on_not_set = [0, 0, 0, 255]
|
||||
|
||||
def convert_to_valid_type(self, value):
|
||||
"""Conversion to valid type.
|
||||
|
||||
Complexity of entity requires to override BaseEntity implementation.
|
||||
"""
|
||||
# Convertion to valid value type `list`
|
||||
if isinstance(value, (set, tuple)):
|
||||
value = list(value)
|
||||
|
||||
# Skip other validations if is not `list`
|
||||
if not isinstance(value, list):
|
||||
raise InvalidValueType(
|
||||
self.valid_value_types, type(value), self.path
|
||||
)
|
||||
|
||||
# Allow list of len 3 (last aplha is set to max)
|
||||
if len(value) == 3:
|
||||
value.append(255)
|
||||
|
||||
if len(value) != 4:
|
||||
reason = "Color entity expect 4 items in list got {}".format(
|
||||
len(value)
|
||||
)
|
||||
raise BaseInvalidValueType(reason, self.path)
|
||||
|
||||
new_value = []
|
||||
for item in value:
|
||||
if not isinstance(item, int):
|
||||
if isinstance(item, (STRING_TYPE, float)):
|
||||
item = int(item)
|
||||
|
||||
is_valid = isinstance(item, int) and -1 < item < 256
|
||||
if not is_valid:
|
||||
reason = (
|
||||
"Color entity expect 4 integers in range 0-255 got {}"
|
||||
).format(value)
|
||||
raise BaseInvalidValueType(reason, self.path)
|
||||
new_value.append(item)
|
||||
return new_value
|
||||
|
|
@ -15,20 +15,22 @@ class StudioDefaultsNotDefined(Exception):
|
|||
super(StudioDefaultsNotDefined, self).__init__(msg)
|
||||
|
||||
|
||||
class InvalidValueType(Exception):
|
||||
msg_template = "{}"
|
||||
class BaseInvalidValueType(Exception):
|
||||
def __init__(self, reason, path):
|
||||
msg = "Path \"{}\". {}".format(path, reason)
|
||||
self.msg = msg
|
||||
super(BaseInvalidValueType, self).__init__(msg)
|
||||
|
||||
|
||||
class InvalidValueType(BaseInvalidValueType):
|
||||
def __init__(self, valid_types, invalid_type, path):
|
||||
msg = "Path \"{}\". ".format(path)
|
||||
|
||||
joined_types = ", ".join(
|
||||
[str(valid_type) for valid_type in valid_types]
|
||||
)
|
||||
msg += "Got invalid type \"{}\". Expected: {}".format(
|
||||
msg = "Got invalid type \"{}\". Expected: {}".format(
|
||||
invalid_type, joined_types
|
||||
)
|
||||
self.msg = msg
|
||||
super(InvalidValueType, self).__init__(msg)
|
||||
super(InvalidValueType, self).__init__(msg, path)
|
||||
|
||||
|
||||
class RequiredKeyModified(KeyError):
|
||||
|
|
|
|||
|
|
@ -420,6 +420,18 @@
|
|||
}
|
||||
```
|
||||
|
||||
### color
|
||||
- preimplemented entity to store and load color values
|
||||
- entity store and expect list of 4 integers in range 0-255
|
||||
- integers represents rgba [Red, Green, Blue, Alpha]
|
||||
|
||||
```
|
||||
{
|
||||
"type": "color",
|
||||
"key": "bg_color",
|
||||
"label": "Background Color"
|
||||
}
|
||||
```
|
||||
|
||||
## Noninteractive widgets
|
||||
- have nothing to do with data
|
||||
|
|
|
|||
|
|
@ -82,6 +82,10 @@
|
|||
"type": "schema",
|
||||
"name": "schema_project_aftereffects"
|
||||
},
|
||||
{
|
||||
"type": "schema",
|
||||
"name": "schema_project_photoshop"
|
||||
},
|
||||
{
|
||||
"type": "schema",
|
||||
"name": "schema_project_harmony"
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@
|
|||
"collapsible": true,
|
||||
"key": "publish",
|
||||
"label": "Publish plugins",
|
||||
"is_file": true,
|
||||
"children": [
|
||||
{
|
||||
"type": "dict",
|
||||
|
|
|
|||
|
|
@ -603,7 +603,6 @@
|
|||
"collapsible": true,
|
||||
"key": "publish",
|
||||
"label": "Publish plugins",
|
||||
"is_file": true,
|
||||
"children": [
|
||||
{
|
||||
"type": "dict",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "photoshop",
|
||||
"label": "Photoshop",
|
||||
"is_file": true,
|
||||
"children": [
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "create",
|
||||
"label": "Creator plugins",
|
||||
"children": [
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "CreateImage",
|
||||
"label": "Create Image",
|
||||
"children": [
|
||||
{
|
||||
"type": "list",
|
||||
"key": "defaults",
|
||||
"label": "Default Subsets",
|
||||
"object_type": "text"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "publish",
|
||||
"label": "Publish plugins",
|
||||
"children": [
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "ExtractImage",
|
||||
"label": "Extract Image",
|
||||
"children": [
|
||||
{
|
||||
"type": "label",
|
||||
"label": "Currently only jpg and png are supported"
|
||||
},
|
||||
{
|
||||
"type": "list",
|
||||
"key": "formats",
|
||||
"label": "Extract Formats",
|
||||
"object_type": "text"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -11,7 +11,6 @@
|
|||
"key": "create",
|
||||
"label": "Creator plugins",
|
||||
"collapsible_key": true,
|
||||
"is_file": true,
|
||||
"object_type": {
|
||||
"type": "dict",
|
||||
"children": [
|
||||
|
|
@ -56,7 +55,6 @@
|
|||
"collapsible": true,
|
||||
"key": "publish",
|
||||
"label": "Publish plugins",
|
||||
"is_file": true,
|
||||
"children": [
|
||||
{
|
||||
"type": "dict",
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
"label": "Site Sync (beta testing)",
|
||||
"collapsible": true,
|
||||
"checkbox_key": "enabled",
|
||||
"is_file": true,
|
||||
"children": [
|
||||
{
|
||||
"type": "boolean",
|
||||
|
|
@ -44,7 +43,6 @@
|
|||
"key": "sites",
|
||||
"label": "Sites",
|
||||
"collapsible_key": false,
|
||||
"is_file": true,
|
||||
"object_type":
|
||||
{
|
||||
"type": "dict",
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@
|
|||
"collapsible": true,
|
||||
"key": "publish",
|
||||
"label": "Publish plugins",
|
||||
"is_file": true,
|
||||
"children": [
|
||||
{
|
||||
"type": "schema_template",
|
||||
|
|
@ -47,6 +46,72 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "load",
|
||||
"label": "Loader plugins",
|
||||
"children": [
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "LoadImage",
|
||||
"label": "Load Image",
|
||||
"children": [
|
||||
{
|
||||
"key": "defaults",
|
||||
"type": "dict",
|
||||
"children": [
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "stretch",
|
||||
"label": "Stretch"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "timestretch",
|
||||
"label": "TimeStretch"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "preload",
|
||||
"label": "Preload"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "ImportImage",
|
||||
"label": "Import Image",
|
||||
"children": [
|
||||
{
|
||||
"key": "defaults",
|
||||
"type": "dict",
|
||||
"children": [
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "stretch",
|
||||
"label": "Stretch"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "timestretch",
|
||||
"label": "TimeStretch"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "preload",
|
||||
"label": "Preload"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "schema",
|
||||
"name": "schema_publish_gui_filter"
|
||||
|
|
|
|||
|
|
@ -242,14 +242,9 @@
|
|||
]
|
||||
},
|
||||
{
|
||||
"type": "schema_template",
|
||||
"name": "template_rgba_color",
|
||||
"template_data": [
|
||||
{
|
||||
"label": "Fill Color",
|
||||
"name": "fill_color"
|
||||
}
|
||||
]
|
||||
"type": "color",
|
||||
"label": "Fill Color",
|
||||
"key": "fill_color"
|
||||
},
|
||||
{
|
||||
"key": "line_thickness",
|
||||
|
|
@ -259,14 +254,9 @@
|
|||
"maximum": 1000
|
||||
},
|
||||
{
|
||||
"type": "schema_template",
|
||||
"name": "template_rgba_color",
|
||||
"template_data": [
|
||||
{
|
||||
"label": "Line Color",
|
||||
"name": "line_color"
|
||||
}
|
||||
]
|
||||
"type": "color",
|
||||
"label": "Line Color",
|
||||
"key": "line_color"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,11 @@
|
|||
"type": "dict",
|
||||
"is_file": true,
|
||||
"children": [
|
||||
{
|
||||
"key": "color",
|
||||
"label": "Color input",
|
||||
"type": "color"
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"key": "schema_template_exaples",
|
||||
|
|
|
|||
|
|
@ -97,7 +97,6 @@
|
|||
"key": "sites",
|
||||
"label": "Sites",
|
||||
"collapsible_key": false,
|
||||
"is_file": true,
|
||||
"object_type":
|
||||
{
|
||||
"type": "dict",
|
||||
|
|
@ -156,8 +155,7 @@
|
|||
},
|
||||
"is_group": true,
|
||||
"key": "templates_mapping",
|
||||
"label": "Templates mapping",
|
||||
"is_file": true
|
||||
"label": "Templates mapping"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1797,7 +1797,11 @@ class AssetItem(BaseItem):
|
|||
item.setData(False, DUPLICATED_ROLE)
|
||||
|
||||
def _remove_task(self, item):
|
||||
# This method is probably obsolete with changed logic and added
|
||||
# `on_task_remove_state_change` method.
|
||||
item_id = item.data(IDENTIFIER_ROLE)
|
||||
if item_id not in self._task_name_by_item_id:
|
||||
return
|
||||
|
||||
name = self._task_name_by_item_id.pop(item_id)
|
||||
self._task_items_by_name[name].remove(item)
|
||||
|
|
@ -1810,6 +1814,9 @@ class AssetItem(BaseItem):
|
|||
_item.setData(False, DUPLICATED_ROLE)
|
||||
|
||||
def _rename_task(self, item):
|
||||
if item.data(REMOVED_ROLE):
|
||||
return
|
||||
|
||||
new_name = item.data(QtCore.Qt.EditRole, "name").lower()
|
||||
item_id = item.data(IDENTIFIER_ROLE)
|
||||
prev_name = self._task_name_by_item_id[item_id]
|
||||
|
|
@ -1840,6 +1847,32 @@ class AssetItem(BaseItem):
|
|||
def on_task_name_change(self, task_item):
|
||||
self._rename_task(task_item)
|
||||
|
||||
def on_task_remove_state_change(self, task_item):
|
||||
is_removed = task_item.data(REMOVED_ROLE)
|
||||
item_id = task_item.data(IDENTIFIER_ROLE)
|
||||
if is_removed:
|
||||
name = self._task_name_by_item_id.pop(item_id)
|
||||
self._task_items_by_name[name].remove(task_item)
|
||||
|
||||
else:
|
||||
name = task_item.data(QtCore.Qt.EditRole, "name").lower()
|
||||
self._task_name_by_item_id[item_id] = name
|
||||
self._task_items_by_name[name].append(task_item)
|
||||
|
||||
# Remove from previous name mapping
|
||||
if not self._task_items_by_name[name]:
|
||||
self._task_items_by_name.pop(name)
|
||||
|
||||
elif len(self._task_items_by_name[name]) == 1:
|
||||
if name in self._duplicated_task_names:
|
||||
self._duplicated_task_names.remove(name)
|
||||
task_item.setData(False, DUPLICATED_ROLE)
|
||||
|
||||
else:
|
||||
self._duplicated_task_names.add(name)
|
||||
for _item in self._task_items_by_name[name]:
|
||||
_item.setData(True, DUPLICATED_ROLE)
|
||||
|
||||
def add_child(self, item, row=None):
|
||||
if item in self._children:
|
||||
return
|
||||
|
|
@ -1975,7 +2008,10 @@ class TaskItem(BaseItem):
|
|||
return True
|
||||
|
||||
if role == REMOVED_ROLE:
|
||||
if value == self._removed:
|
||||
return False
|
||||
self._removed = value
|
||||
self.parent().on_task_remove_state_change(self)
|
||||
return True
|
||||
|
||||
if (
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ from openpype.settings.entities import (
|
|||
TextEntity,
|
||||
PathInput,
|
||||
RawJsonEntity,
|
||||
ColorEntity,
|
||||
|
||||
DefaultsNotDefined,
|
||||
StudioDefaultsNotDefined,
|
||||
|
|
@ -44,7 +45,7 @@ from .item_widgets import (
|
|||
PathWidget,
|
||||
PathInputWidget
|
||||
)
|
||||
|
||||
from .color_widget import ColorWidget
|
||||
from avalon.vendor import qtawesome
|
||||
|
||||
|
||||
|
|
@ -113,6 +114,9 @@ class SettingsCategoryWidget(QtWidgets.QWidget):
|
|||
elif isinstance(entity, RawJsonEntity):
|
||||
return RawJsonWidget(*args)
|
||||
|
||||
elif isinstance(entity, ColorEntity):
|
||||
return ColorWidget(*args)
|
||||
|
||||
elif isinstance(entity, BaseEnumEntity):
|
||||
return EnumeratorWidget(*args)
|
||||
|
||||
|
|
|
|||
171
openpype/tools/settings/settings/color_widget.py
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
from Qt import QtWidgets, QtCore, QtGui
|
||||
|
||||
from .item_widgets import InputWidget
|
||||
|
||||
from openpype.widgets.color_widgets import (
|
||||
ColorPickerWidget,
|
||||
draw_checkerboard_tile
|
||||
)
|
||||
|
||||
|
||||
class ColorWidget(InputWidget):
|
||||
def _add_inputs_to_layout(self):
|
||||
self.input_field = ColorViewer(self.content_widget)
|
||||
|
||||
self.setFocusProxy(self.input_field)
|
||||
|
||||
self.content_layout.addWidget(self.input_field, 1)
|
||||
|
||||
self.input_field.clicked.connect(self._on_click)
|
||||
|
||||
self._dialog = None
|
||||
|
||||
def _on_click(self):
|
||||
if self._dialog:
|
||||
self._dialog.open()
|
||||
return
|
||||
|
||||
dialog = ColorDialog(self.input_field.color(), self)
|
||||
self._dialog = dialog
|
||||
|
||||
dialog.open()
|
||||
dialog.finished.connect(self._on_dialog_finish)
|
||||
|
||||
def _on_dialog_finish(self, *_args):
|
||||
if not self._dialog:
|
||||
return
|
||||
|
||||
color = self._dialog.result()
|
||||
if color is not None:
|
||||
self.input_field.set_color(color)
|
||||
self._on_value_change()
|
||||
|
||||
self._dialog.deleteLater()
|
||||
self._dialog = None
|
||||
|
||||
def _on_entity_change(self):
|
||||
if self.entity.value != self.input_value():
|
||||
self.set_entity_value()
|
||||
|
||||
def set_entity_value(self):
|
||||
self.input_field.set_color(*self.entity.value)
|
||||
|
||||
def input_value(self):
|
||||
color = self.input_field.color()
|
||||
return [color.red(), color.green(), color.blue(), color.alpha()]
|
||||
|
||||
def _on_value_change(self):
|
||||
if self.ignore_input_changes:
|
||||
return
|
||||
|
||||
self.entity.set(self.input_value())
|
||||
|
||||
|
||||
class ColorViewer(QtWidgets.QWidget):
|
||||
clicked = QtCore.Signal()
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super(ColorViewer, self).__init__(parent)
|
||||
|
||||
self.setMinimumSize(10, 10)
|
||||
|
||||
self.actual_pen = QtGui.QPen()
|
||||
self.actual_color = QtGui.QColor()
|
||||
self._checkerboard = None
|
||||
|
||||
def mouseReleaseEvent(self, event):
|
||||
if event.button() == QtCore.Qt.LeftButton:
|
||||
self.clicked.emit()
|
||||
super(ColorViewer, self).mouseReleaseEvent(event)
|
||||
|
||||
def checkerboard(self):
|
||||
if not self._checkerboard:
|
||||
self._checkerboard = draw_checkerboard_tile(self.height() / 4)
|
||||
return self._checkerboard
|
||||
|
||||
def color(self):
|
||||
return self.actual_color
|
||||
|
||||
def set_color(self, *args):
|
||||
# Create copy of entered color
|
||||
self.actual_color = QtGui.QColor(*args)
|
||||
# Repaint
|
||||
self.update()
|
||||
|
||||
def set_alpha(self, alpha):
|
||||
# Change alpha of current color
|
||||
self.actual_color.setAlpha(alpha)
|
||||
# Repaint
|
||||
self.update()
|
||||
|
||||
def paintEvent(self, event):
|
||||
rect = event.rect()
|
||||
|
||||
painter = QtGui.QPainter(self)
|
||||
painter.setRenderHint(QtGui.QPainter.Antialiasing)
|
||||
|
||||
radius = rect.height() / 2
|
||||
rounded_rect = QtGui.QPainterPath()
|
||||
rounded_rect.addRoundedRect(QtCore.QRectF(rect), radius, radius)
|
||||
painter.setClipPath(rounded_rect)
|
||||
|
||||
pen = QtGui.QPen(QtGui.QColor(255, 255, 255, 67))
|
||||
pen.setWidth(1)
|
||||
painter.setPen(pen)
|
||||
painter.drawTiledPixmap(rect, self.checkerboard())
|
||||
painter.fillRect(rect, self.actual_color)
|
||||
painter.drawPath(rounded_rect)
|
||||
|
||||
painter.end()
|
||||
|
||||
|
||||
class ColorDialog(QtWidgets.QDialog):
|
||||
def __init__(self, color=None, parent=None):
|
||||
super(ColorDialog, self).__init__(parent)
|
||||
|
||||
self.setWindowTitle("Color picker dialog")
|
||||
|
||||
picker_widget = ColorPickerWidget(color, self)
|
||||
|
||||
footer_widget = QtWidgets.QWidget(self)
|
||||
|
||||
ok_btn = QtWidgets.QPushButton("Ok", footer_widget)
|
||||
cancel_btn = QtWidgets.QPushButton("Cancel", footer_widget)
|
||||
|
||||
footer_layout = QtWidgets.QHBoxLayout(footer_widget)
|
||||
footer_layout.setContentsMargins(0, 0, 0, 0)
|
||||
footer_layout.addStretch(1)
|
||||
footer_layout.addWidget(ok_btn)
|
||||
footer_layout.addWidget(cancel_btn)
|
||||
|
||||
layout = QtWidgets.QVBoxLayout(self)
|
||||
|
||||
layout.addWidget(picker_widget, 1)
|
||||
layout.addWidget(footer_widget, 0)
|
||||
|
||||
ok_btn.clicked.connect(self.on_ok_clicked)
|
||||
cancel_btn.clicked.connect(self.on_cancel_clicked)
|
||||
|
||||
self.picker_widget = picker_widget
|
||||
self.ok_btn = ok_btn
|
||||
self.cancel_btn = cancel_btn
|
||||
|
||||
self._result = None
|
||||
|
||||
def showEvent(self, event):
|
||||
super(ColorDialog, self).showEvent(event)
|
||||
|
||||
btns_width = max(self.ok_btn.width(), self.cancel_btn.width())
|
||||
self.ok_btn.setFixedWidth(btns_width)
|
||||
self.cancel_btn.setFixedWidth(btns_width)
|
||||
|
||||
def on_ok_clicked(self):
|
||||
self._result = self.picker_widget.color()
|
||||
self.close()
|
||||
|
||||
def on_cancel_clicked(self):
|
||||
self._result = None
|
||||
self.close()
|
||||
|
||||
def result(self):
|
||||
return self._result
|
||||
14
openpype/widgets/color_widgets/__init__.py
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
from .color_picker_widget import (
|
||||
ColorPickerWidget
|
||||
)
|
||||
|
||||
from .color_view import (
|
||||
draw_checkerboard_tile
|
||||
)
|
||||
|
||||
|
||||
__all__ = (
|
||||
"ColorPickerWidget",
|
||||
|
||||
"draw_checkerboard_tile"
|
||||
)
|
||||
639
openpype/widgets/color_widgets/color_inputs.py
Normal file
|
|
@ -0,0 +1,639 @@
|
|||
import re
|
||||
from Qt import QtWidgets, QtCore, QtGui
|
||||
|
||||
from .color_view import draw_checkerboard_tile
|
||||
|
||||
|
||||
slide_style = """
|
||||
QSlider::groove:horizontal {
|
||||
background: qlineargradient(
|
||||
x1: 0, y1: 0, x2: 1, y2: 0, stop: 0 #000, stop: 1 #fff
|
||||
);
|
||||
height: 8px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
QSlider::handle:horizontal {
|
||||
background: qlineargradient(
|
||||
x1:0, y1:0, x2:1, y2:1, stop:0 #ddd, stop:1 #bbb
|
||||
);
|
||||
border: 1px solid #777;
|
||||
width: 8px;
|
||||
margin-top: -1px;
|
||||
margin-bottom: -1px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
QSlider::handle:horizontal:hover {
|
||||
background: qlineargradient(
|
||||
x1:0, y1:0, x2:1, y2:1, stop:0 #eee, stop:1 #ddd
|
||||
);
|
||||
border: 1px solid #444;ff
|
||||
border-radius: 4px;
|
||||
}"""
|
||||
|
||||
|
||||
class AlphaSlider(QtWidgets.QSlider):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(AlphaSlider, self).__init__(*args, **kwargs)
|
||||
self._mouse_clicked = False
|
||||
self.setSingleStep(1)
|
||||
self.setMinimum(0)
|
||||
self.setMaximum(255)
|
||||
self.setValue(255)
|
||||
|
||||
self._checkerboard = None
|
||||
|
||||
def checkerboard(self):
|
||||
if self._checkerboard is None:
|
||||
self._checkerboard = draw_checkerboard_tile(
|
||||
3, QtGui.QColor(173, 173, 173), QtGui.QColor(27, 27, 27)
|
||||
)
|
||||
return self._checkerboard
|
||||
|
||||
def mousePressEvent(self, event):
|
||||
self._mouse_clicked = True
|
||||
if event.button() == QtCore.Qt.LeftButton:
|
||||
self._set_value_to_pos(event.pos().x())
|
||||
return event.accept()
|
||||
return super(AlphaSlider, self).mousePressEvent(event)
|
||||
|
||||
def _set_value_to_pos(self, pos_x):
|
||||
value = (
|
||||
self.maximum() - self.minimum()
|
||||
) * pos_x / self.width() + self.minimum()
|
||||
self.setValue(value)
|
||||
|
||||
def mouseMoveEvent(self, event):
|
||||
if self._mouse_clicked:
|
||||
self._set_value_to_pos(event.pos().x())
|
||||
super(AlphaSlider, self).mouseMoveEvent(event)
|
||||
|
||||
def mouseReleaseEvent(self, event):
|
||||
self._mouse_clicked = True
|
||||
super(AlphaSlider, self).mouseReleaseEvent(event)
|
||||
|
||||
def paintEvent(self, event):
|
||||
painter = QtGui.QPainter(self)
|
||||
opt = QtWidgets.QStyleOptionSlider()
|
||||
self.initStyleOption(opt)
|
||||
|
||||
painter.fillRect(event.rect(), QtCore.Qt.transparent)
|
||||
|
||||
painter.setRenderHint(QtGui.QPainter.SmoothPixmapTransform)
|
||||
rect = self.style().subControlRect(
|
||||
QtWidgets.QStyle.CC_Slider,
|
||||
opt,
|
||||
QtWidgets.QStyle.SC_SliderGroove,
|
||||
self
|
||||
)
|
||||
final_height = 9
|
||||
offset_top = 0
|
||||
if rect.height() > final_height:
|
||||
offset_top = int((rect.height() - final_height) / 2)
|
||||
rect = QtCore.QRect(
|
||||
rect.x(),
|
||||
offset_top,
|
||||
rect.width(),
|
||||
final_height
|
||||
)
|
||||
|
||||
pix_rect = QtCore.QRect(event.rect())
|
||||
pix_rect.setX(rect.x())
|
||||
pix_rect.setWidth(rect.width() - (2 * rect.x()))
|
||||
pix = QtGui.QPixmap(pix_rect.width(), pix_rect.height())
|
||||
pix_painter = QtGui.QPainter(pix)
|
||||
pix_painter.drawTiledPixmap(pix_rect, self.checkerboard())
|
||||
gradient = QtGui.QLinearGradient(rect.topLeft(), rect.bottomRight())
|
||||
gradient.setColorAt(0, QtCore.Qt.transparent)
|
||||
gradient.setColorAt(1, QtCore.Qt.white)
|
||||
pix_painter.fillRect(pix_rect, gradient)
|
||||
pix_painter.end()
|
||||
|
||||
brush = QtGui.QBrush(pix)
|
||||
painter.save()
|
||||
painter.setPen(QtCore.Qt.NoPen)
|
||||
painter.setBrush(brush)
|
||||
ratio = rect.height() / 2
|
||||
painter.drawRoundedRect(rect, ratio, ratio)
|
||||
painter.restore()
|
||||
|
||||
_handle_rect = self.style().subControlRect(
|
||||
QtWidgets.QStyle.CC_Slider,
|
||||
opt,
|
||||
QtWidgets.QStyle.SC_SliderHandle,
|
||||
self
|
||||
)
|
||||
|
||||
handle_rect = QtCore.QRect(rect)
|
||||
if offset_top > 1:
|
||||
height = handle_rect.height()
|
||||
handle_rect.setY(handle_rect.y() - 1)
|
||||
handle_rect.setHeight(height + 2)
|
||||
handle_rect.setX(_handle_rect.x())
|
||||
handle_rect.setWidth(handle_rect.height())
|
||||
|
||||
painter.save()
|
||||
|
||||
gradient = QtGui.QRadialGradient()
|
||||
radius = handle_rect.height() / 2
|
||||
center_x = handle_rect.width() / 2 + handle_rect.x()
|
||||
center_y = handle_rect.height()
|
||||
gradient.setCenter(center_x, center_y)
|
||||
gradient.setCenterRadius(radius)
|
||||
gradient.setFocalPoint(center_x, center_y)
|
||||
|
||||
gradient.setColorAt(0.9, QtGui.QColor(127, 127, 127))
|
||||
gradient.setColorAt(1, QtCore.Qt.transparent)
|
||||
|
||||
painter.setPen(QtCore.Qt.NoPen)
|
||||
painter.setBrush(gradient)
|
||||
painter.drawEllipse(handle_rect)
|
||||
|
||||
painter.restore()
|
||||
|
||||
|
||||
class AlphaInputs(QtWidgets.QWidget):
|
||||
alpha_changed = QtCore.Signal(int)
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super(AlphaInputs, self).__init__(parent)
|
||||
|
||||
self._block_changes = False
|
||||
self.alpha_value = None
|
||||
|
||||
percent_input = QtWidgets.QDoubleSpinBox(self)
|
||||
percent_input.setButtonSymbols(QtWidgets.QSpinBox.NoButtons)
|
||||
percent_input.setMinimum(0)
|
||||
percent_input.setMaximum(100)
|
||||
percent_input.setDecimals(2)
|
||||
|
||||
int_input = QtWidgets.QSpinBox(self)
|
||||
int_input.setButtonSymbols(QtWidgets.QSpinBox.NoButtons)
|
||||
int_input.setMinimum(0)
|
||||
int_input.setMaximum(255)
|
||||
|
||||
layout = QtWidgets.QHBoxLayout(self)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.addWidget(int_input)
|
||||
layout.addWidget(QtWidgets.QLabel("0-255"))
|
||||
layout.addWidget(percent_input)
|
||||
layout.addWidget(QtWidgets.QLabel("%"))
|
||||
|
||||
percent_input.valueChanged.connect(self._on_percent_change)
|
||||
int_input.valueChanged.connect(self._on_int_change)
|
||||
|
||||
self.percent_input = percent_input
|
||||
self.int_input = int_input
|
||||
|
||||
self.set_alpha(255)
|
||||
|
||||
def set_alpha(self, alpha):
|
||||
if alpha == self.alpha_value:
|
||||
return
|
||||
self.alpha_value = alpha
|
||||
|
||||
self.update_alpha()
|
||||
|
||||
def _on_percent_change(self):
|
||||
if self._block_changes:
|
||||
return
|
||||
self.alpha_value = int(self.percent_input.value() * 255 / 100)
|
||||
self.alpha_changed.emit(self.alpha_value)
|
||||
self.update_alpha()
|
||||
|
||||
def _on_int_change(self):
|
||||
if self._block_changes:
|
||||
return
|
||||
|
||||
self.alpha_value = self.int_input.value()
|
||||
self.alpha_changed.emit(self.alpha_value)
|
||||
self.update_alpha()
|
||||
|
||||
def update_alpha(self):
|
||||
self._block_changes = True
|
||||
if self.int_input.value() != self.alpha_value:
|
||||
self.int_input.setValue(self.alpha_value)
|
||||
|
||||
percent = round(100 * self.alpha_value / 255, 2)
|
||||
if self.percent_input.value() != percent:
|
||||
self.percent_input.setValue(percent)
|
||||
|
||||
self._block_changes = False
|
||||
|
||||
|
||||
class RGBInputs(QtWidgets.QWidget):
|
||||
value_changed = QtCore.Signal()
|
||||
|
||||
def __init__(self, color, parent=None):
|
||||
super(RGBInputs, self).__init__(parent)
|
||||
|
||||
self._block_changes = False
|
||||
|
||||
self.color = color
|
||||
|
||||
input_red = QtWidgets.QSpinBox(self)
|
||||
input_green = QtWidgets.QSpinBox(self)
|
||||
input_blue = QtWidgets.QSpinBox(self)
|
||||
|
||||
input_red.setButtonSymbols(QtWidgets.QSpinBox.NoButtons)
|
||||
input_green.setButtonSymbols(QtWidgets.QSpinBox.NoButtons)
|
||||
input_blue.setButtonSymbols(QtWidgets.QSpinBox.NoButtons)
|
||||
|
||||
input_red.setMinimum(0)
|
||||
input_green.setMinimum(0)
|
||||
input_blue.setMinimum(0)
|
||||
|
||||
input_red.setMaximum(255)
|
||||
input_green.setMaximum(255)
|
||||
input_blue.setMaximum(255)
|
||||
|
||||
layout = QtWidgets.QHBoxLayout(self)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.addWidget(input_red, 1)
|
||||
layout.addWidget(input_green, 1)
|
||||
layout.addWidget(input_blue, 1)
|
||||
|
||||
input_red.valueChanged.connect(self._on_red_change)
|
||||
input_green.valueChanged.connect(self._on_green_change)
|
||||
input_blue.valueChanged.connect(self._on_blue_change)
|
||||
|
||||
self.input_red = input_red
|
||||
self.input_green = input_green
|
||||
self.input_blue = input_blue
|
||||
|
||||
def _on_red_change(self, value):
|
||||
if self._block_changes:
|
||||
return
|
||||
self.color.setRed(value)
|
||||
self._on_change()
|
||||
|
||||
def _on_green_change(self, value):
|
||||
if self._block_changes:
|
||||
return
|
||||
self.color.setGreen(value)
|
||||
self._on_change()
|
||||
|
||||
def _on_blue_change(self, value):
|
||||
if self._block_changes:
|
||||
return
|
||||
self.color.setBlue(value)
|
||||
self._on_change()
|
||||
|
||||
def _on_change(self):
|
||||
self.value_changed.emit()
|
||||
|
||||
def color_changed(self):
|
||||
if (
|
||||
self.input_red.value() == self.color.red()
|
||||
and self.input_green.value() == self.color.green()
|
||||
and self.input_blue.value() == self.color.blue()
|
||||
):
|
||||
return
|
||||
|
||||
self._block_changes = True
|
||||
|
||||
self.input_red.setValue(self.color.red())
|
||||
self.input_green.setValue(self.color.green())
|
||||
self.input_blue.setValue(self.color.blue())
|
||||
|
||||
self._block_changes = False
|
||||
|
||||
|
||||
class CMYKInputs(QtWidgets.QWidget):
|
||||
value_changed = QtCore.Signal()
|
||||
|
||||
def __init__(self, color, parent=None):
|
||||
super(CMYKInputs, self).__init__(parent)
|
||||
|
||||
self.color = color
|
||||
|
||||
self._block_changes = False
|
||||
|
||||
input_cyan = QtWidgets.QSpinBox(self)
|
||||
input_magenta = QtWidgets.QSpinBox(self)
|
||||
input_yellow = QtWidgets.QSpinBox(self)
|
||||
input_black = QtWidgets.QSpinBox(self)
|
||||
|
||||
input_cyan.setButtonSymbols(QtWidgets.QSpinBox.NoButtons)
|
||||
input_magenta.setButtonSymbols(QtWidgets.QSpinBox.NoButtons)
|
||||
input_yellow.setButtonSymbols(QtWidgets.QSpinBox.NoButtons)
|
||||
input_black.setButtonSymbols(QtWidgets.QSpinBox.NoButtons)
|
||||
|
||||
input_cyan.setMinimum(0)
|
||||
input_magenta.setMinimum(0)
|
||||
input_yellow.setMinimum(0)
|
||||
input_black.setMinimum(0)
|
||||
|
||||
input_cyan.setMaximum(255)
|
||||
input_magenta.setMaximum(255)
|
||||
input_yellow.setMaximum(255)
|
||||
input_black.setMaximum(255)
|
||||
|
||||
layout = QtWidgets.QHBoxLayout(self)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.addWidget(input_cyan, 1)
|
||||
layout.addWidget(input_magenta, 1)
|
||||
layout.addWidget(input_yellow, 1)
|
||||
layout.addWidget(input_black, 1)
|
||||
|
||||
input_cyan.valueChanged.connect(self._on_change)
|
||||
input_magenta.valueChanged.connect(self._on_change)
|
||||
input_yellow.valueChanged.connect(self._on_change)
|
||||
input_black.valueChanged.connect(self._on_change)
|
||||
|
||||
self.input_cyan = input_cyan
|
||||
self.input_magenta = input_magenta
|
||||
self.input_yellow = input_yellow
|
||||
self.input_black = input_black
|
||||
|
||||
def _on_change(self):
|
||||
if self._block_changes:
|
||||
return
|
||||
self.color.setCmyk(
|
||||
self.input_cyan.value(),
|
||||
self.input_magenta.value(),
|
||||
self.input_yellow.value(),
|
||||
self.input_black.value()
|
||||
)
|
||||
self.value_changed.emit()
|
||||
|
||||
def color_changed(self):
|
||||
if self._block_changes:
|
||||
return
|
||||
_cur_color = QtGui.QColor()
|
||||
_cur_color.setCmyk(
|
||||
self.input_cyan.value(),
|
||||
self.input_magenta.value(),
|
||||
self.input_yellow.value(),
|
||||
self.input_black.value()
|
||||
)
|
||||
if (
|
||||
_cur_color.red() == self.color.red()
|
||||
and _cur_color.green() == self.color.green()
|
||||
and _cur_color.blue() == self.color.blue()
|
||||
):
|
||||
return
|
||||
|
||||
c, m, y, k, _ = self.color.getCmyk()
|
||||
self._block_changes = True
|
||||
|
||||
self.input_cyan.setValue(c)
|
||||
self.input_magenta.setValue(m)
|
||||
self.input_yellow.setValue(y)
|
||||
self.input_black.setValue(k)
|
||||
|
||||
self._block_changes = False
|
||||
|
||||
|
||||
class HEXInputs(QtWidgets.QWidget):
|
||||
hex_regex = re.compile("^#(([0-9a-fA-F]{2}){3}|([0-9a-fA-F]){3})$")
|
||||
value_changed = QtCore.Signal()
|
||||
|
||||
def __init__(self, color, parent=None):
|
||||
super(HEXInputs, self).__init__(parent)
|
||||
self.color = color
|
||||
|
||||
input_field = QtWidgets.QLineEdit(self)
|
||||
|
||||
layout = QtWidgets.QHBoxLayout(self)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.addWidget(input_field, 1)
|
||||
|
||||
input_field.textChanged.connect(self._on_change)
|
||||
|
||||
self.input_field = input_field
|
||||
|
||||
def _on_change(self):
|
||||
if self._block_changes:
|
||||
return
|
||||
input_value = self.input_field.text()
|
||||
# TODO what if does not match?
|
||||
if self.hex_regex.match(input_value):
|
||||
self.color.setNamedColor(input_value)
|
||||
self.value_changed.emit()
|
||||
|
||||
def color_changed(self):
|
||||
input_value = self.input_field.text()
|
||||
if self.hex_regex.match(input_value):
|
||||
_cur_color = QtGui.QColor()
|
||||
_cur_color.setNamedColor(input_value)
|
||||
if (
|
||||
_cur_color.red() == self.color.red()
|
||||
and _cur_color.green() == self.color.green()
|
||||
and _cur_color.blue() == self.color.blue()
|
||||
):
|
||||
return
|
||||
self._block_changes = True
|
||||
|
||||
self.input_field.setText(self.color.name())
|
||||
|
||||
self._block_changes = False
|
||||
|
||||
|
||||
class HSVInputs(QtWidgets.QWidget):
|
||||
value_changed = QtCore.Signal()
|
||||
|
||||
def __init__(self, color, parent=None):
|
||||
super(HSVInputs, self).__init__(parent)
|
||||
|
||||
self._block_changes = False
|
||||
|
||||
self.color = color
|
||||
|
||||
input_hue = QtWidgets.QSpinBox(self)
|
||||
input_sat = QtWidgets.QSpinBox(self)
|
||||
input_val = QtWidgets.QSpinBox(self)
|
||||
|
||||
input_hue.setButtonSymbols(QtWidgets.QSpinBox.NoButtons)
|
||||
input_sat.setButtonSymbols(QtWidgets.QSpinBox.NoButtons)
|
||||
input_val.setButtonSymbols(QtWidgets.QSpinBox.NoButtons)
|
||||
|
||||
input_hue.setMinimum(0)
|
||||
input_sat.setMinimum(0)
|
||||
input_val.setMinimum(0)
|
||||
|
||||
input_hue.setMaximum(359)
|
||||
input_sat.setMaximum(255)
|
||||
input_val.setMaximum(255)
|
||||
|
||||
layout = QtWidgets.QHBoxLayout(self)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.addWidget(input_hue, 1)
|
||||
layout.addWidget(input_sat, 1)
|
||||
layout.addWidget(input_val, 1)
|
||||
|
||||
input_hue.valueChanged.connect(self._on_change)
|
||||
input_sat.valueChanged.connect(self._on_change)
|
||||
input_val.valueChanged.connect(self._on_change)
|
||||
|
||||
self.input_hue = input_hue
|
||||
self.input_sat = input_sat
|
||||
self.input_val = input_val
|
||||
|
||||
def _on_change(self):
|
||||
if self._block_changes:
|
||||
return
|
||||
self.color.setHsv(
|
||||
self.input_hue.value(),
|
||||
self.input_sat.value(),
|
||||
self.input_val.value()
|
||||
)
|
||||
self.value_changed.emit()
|
||||
|
||||
def color_changed(self):
|
||||
_cur_color = QtGui.QColor()
|
||||
_cur_color.setHsv(
|
||||
self.input_hue.value(),
|
||||
self.input_sat.value(),
|
||||
self.input_val.value()
|
||||
)
|
||||
if (
|
||||
_cur_color.red() == self.color.red()
|
||||
and _cur_color.green() == self.color.green()
|
||||
and _cur_color.blue() == self.color.blue()
|
||||
):
|
||||
return
|
||||
|
||||
self._block_changes = True
|
||||
h, s, v, _ = self.color.getHsv()
|
||||
|
||||
self.input_hue.setValue(h)
|
||||
self.input_sat.setValue(s)
|
||||
self.input_val.setValue(v)
|
||||
|
||||
self._block_changes = False
|
||||
|
||||
|
||||
class HSLInputs(QtWidgets.QWidget):
|
||||
value_changed = QtCore.Signal()
|
||||
|
||||
def __init__(self, color, parent=None):
|
||||
super(HSLInputs, self).__init__(parent)
|
||||
|
||||
self._block_changes = False
|
||||
|
||||
self.color = color
|
||||
|
||||
input_hue = QtWidgets.QSpinBox(self)
|
||||
input_sat = QtWidgets.QSpinBox(self)
|
||||
input_light = QtWidgets.QSpinBox(self)
|
||||
|
||||
input_hue.setButtonSymbols(QtWidgets.QSpinBox.NoButtons)
|
||||
input_sat.setButtonSymbols(QtWidgets.QSpinBox.NoButtons)
|
||||
input_light.setButtonSymbols(QtWidgets.QSpinBox.NoButtons)
|
||||
|
||||
input_hue.setMinimum(0)
|
||||
input_sat.setMinimum(0)
|
||||
input_light.setMinimum(0)
|
||||
|
||||
input_hue.setMaximum(359)
|
||||
input_sat.setMaximum(255)
|
||||
input_light.setMaximum(255)
|
||||
|
||||
layout = QtWidgets.QHBoxLayout(self)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.addWidget(input_hue, 1)
|
||||
layout.addWidget(input_sat, 1)
|
||||
layout.addWidget(input_light, 1)
|
||||
|
||||
input_hue.valueChanged.connect(self._on_change)
|
||||
input_sat.valueChanged.connect(self._on_change)
|
||||
input_light.valueChanged.connect(self._on_change)
|
||||
|
||||
self.input_hue = input_hue
|
||||
self.input_sat = input_sat
|
||||
self.input_light = input_light
|
||||
|
||||
def _on_change(self):
|
||||
if self._block_changes:
|
||||
return
|
||||
self.color.setHsl(
|
||||
self.input_hue.value(),
|
||||
self.input_sat.value(),
|
||||
self.input_light.value()
|
||||
)
|
||||
self.value_changed.emit()
|
||||
|
||||
def color_changed(self):
|
||||
_cur_color = QtGui.QColor()
|
||||
_cur_color.setHsl(
|
||||
self.input_hue.value(),
|
||||
self.input_sat.value(),
|
||||
self.input_light.value()
|
||||
)
|
||||
if (
|
||||
_cur_color.red() == self.color.red()
|
||||
and _cur_color.green() == self.color.green()
|
||||
and _cur_color.blue() == self.color.blue()
|
||||
):
|
||||
return
|
||||
|
||||
self._block_changes = True
|
||||
h, s, l, _ = self.color.getHsl()
|
||||
|
||||
self.input_hue.setValue(h)
|
||||
self.input_sat.setValue(s)
|
||||
self.input_light.setValue(l)
|
||||
|
||||
self._block_changes = False
|
||||
|
||||
|
||||
class ColorInputsWidget(QtWidgets.QWidget):
|
||||
color_changed = QtCore.Signal(QtGui.QColor)
|
||||
|
||||
def __init__(self, parent=None, **kwargs):
|
||||
super(ColorInputsWidget, self).__init__(parent)
|
||||
|
||||
color = QtGui.QColor()
|
||||
|
||||
input_fields = []
|
||||
|
||||
if kwargs.get("use_hex", True):
|
||||
input_fields.append(HEXInputs(color, self))
|
||||
|
||||
if kwargs.get("use_rgb", True):
|
||||
input_fields.append(RGBInputs(color, self))
|
||||
|
||||
if kwargs.get("use_hsl", True):
|
||||
input_fields.append(HSLInputs(color, self))
|
||||
|
||||
if kwargs.get("use_hsv", True):
|
||||
input_fields.append(HSVInputs(color, self))
|
||||
|
||||
if kwargs.get("use_cmyk", True):
|
||||
input_fields.append(CMYKInputs(color, self))
|
||||
|
||||
inputs_widget = QtWidgets.QWidget(self)
|
||||
inputs_layout = QtWidgets.QVBoxLayout(inputs_widget)
|
||||
|
||||
for input_field in input_fields:
|
||||
inputs_layout.addWidget(input_field)
|
||||
input_field.value_changed.connect(self._on_value_change)
|
||||
|
||||
layout = QtWidgets.QVBoxLayout(self)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.addWidget(inputs_widget, 0)
|
||||
spacer = QtWidgets.QWidget(self)
|
||||
layout.addWidget(spacer, 1)
|
||||
|
||||
self.input_fields = input_fields
|
||||
|
||||
self.color = color
|
||||
|
||||
def set_color(self, color):
|
||||
if (
|
||||
color.red() == self.color.red()
|
||||
and color.green() == self.color.green()
|
||||
and color.blue() == self.color.blue()
|
||||
):
|
||||
return
|
||||
self.color.setRed(color.red())
|
||||
self.color.setGreen(color.green())
|
||||
self.color.setBlue(color.blue())
|
||||
self._on_value_change()
|
||||
|
||||
def _on_value_change(self):
|
||||
for input_field in self.input_fields:
|
||||
input_field.color_changed()
|
||||
self.color_changed.emit(self.color)
|
||||
176
openpype/widgets/color_widgets/color_picker_widget.py
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
import os
|
||||
from Qt import QtWidgets, QtCore, QtGui
|
||||
|
||||
from .color_triangle import QtColorTriangle
|
||||
from .color_view import ColorViewer
|
||||
from .color_screen_pick import PickScreenColorWidget
|
||||
from .color_inputs import (
|
||||
AlphaSlider,
|
||||
AlphaInputs,
|
||||
HEXInputs,
|
||||
RGBInputs,
|
||||
HSLInputs,
|
||||
HSVInputs
|
||||
)
|
||||
|
||||
|
||||
class ColorPickerWidget(QtWidgets.QWidget):
|
||||
color_changed = QtCore.Signal(QtGui.QColor)
|
||||
|
||||
def __init__(self, color=None, parent=None):
|
||||
super(ColorPickerWidget, self).__init__(parent)
|
||||
|
||||
# Color triangle
|
||||
color_triangle = QtColorTriangle(self)
|
||||
|
||||
alpha_slider_proxy = QtWidgets.QWidget(self)
|
||||
alpha_slider = AlphaSlider(QtCore.Qt.Horizontal, alpha_slider_proxy)
|
||||
|
||||
alpha_slider_layout = QtWidgets.QHBoxLayout(alpha_slider_proxy)
|
||||
alpha_slider_layout.setContentsMargins(5, 5, 5, 5)
|
||||
alpha_slider_layout.addWidget(alpha_slider, 1)
|
||||
|
||||
# Eye picked widget
|
||||
pick_widget = PickScreenColorWidget()
|
||||
pick_widget.setMaximumHeight(50)
|
||||
|
||||
# Color pick button
|
||||
btn_pick_color = QtWidgets.QPushButton(self)
|
||||
icon_path = os.path.join(
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"eyedropper.png"
|
||||
)
|
||||
btn_pick_color.setIcon(QtGui.QIcon(icon_path))
|
||||
btn_pick_color.setToolTip("Pick a color")
|
||||
|
||||
# Color preview
|
||||
color_view = ColorViewer(self)
|
||||
color_view.setMaximumHeight(50)
|
||||
|
||||
alpha_inputs = AlphaInputs(self)
|
||||
|
||||
color_inputs_color = QtGui.QColor()
|
||||
col_inputs_by_label = [
|
||||
("HEX", HEXInputs(color_inputs_color, self)),
|
||||
("RGB", RGBInputs(color_inputs_color, self)),
|
||||
("HSL", HSLInputs(color_inputs_color, self)),
|
||||
("HSV", HSVInputs(color_inputs_color, self))
|
||||
]
|
||||
|
||||
layout = QtWidgets.QGridLayout(self)
|
||||
empty_col = 1
|
||||
label_col = empty_col + 1
|
||||
input_col = label_col + 1
|
||||
empty_widget = QtWidgets.QWidget(self)
|
||||
empty_widget.setFixedWidth(10)
|
||||
layout.addWidget(empty_widget, 0, empty_col)
|
||||
|
||||
row = 0
|
||||
layout.addWidget(btn_pick_color, row, label_col)
|
||||
layout.addWidget(color_view, row, input_col)
|
||||
row += 1
|
||||
|
||||
color_input_fields = []
|
||||
for label, input_field in col_inputs_by_label:
|
||||
layout.addWidget(QtWidgets.QLabel(label, self), row, label_col)
|
||||
layout.addWidget(input_field, row, input_col)
|
||||
input_field.value_changed.connect(
|
||||
self._on_color_input_value_change
|
||||
)
|
||||
color_input_fields.append(input_field)
|
||||
row += 1
|
||||
|
||||
layout.addWidget(color_triangle, 0, 0, row + 1, 1)
|
||||
layout.setRowStretch(row, 1)
|
||||
row += 1
|
||||
|
||||
layout.addWidget(alpha_slider_proxy, row, 0)
|
||||
|
||||
layout.addWidget(QtWidgets.QLabel("Alpha", self), row, label_col)
|
||||
layout.addWidget(alpha_inputs, row, input_col)
|
||||
row += 1
|
||||
layout.setRowStretch(row, 1)
|
||||
|
||||
color_view.set_color(color_triangle.cur_color)
|
||||
|
||||
color_triangle.color_changed.connect(self.triangle_color_changed)
|
||||
alpha_slider.valueChanged.connect(self._on_alpha_slider_change)
|
||||
pick_widget.color_selected.connect(self.on_color_change)
|
||||
alpha_inputs.alpha_changed.connect(self._on_alpha_inputs_changed)
|
||||
btn_pick_color.released.connect(self.pick_color)
|
||||
|
||||
self.color_input_fields = color_input_fields
|
||||
self.color_inputs_color = color_inputs_color
|
||||
|
||||
self.pick_widget = pick_widget
|
||||
|
||||
self.color_triangle = color_triangle
|
||||
self.alpha_slider = alpha_slider
|
||||
|
||||
self.color_view = color_view
|
||||
self.alpha_inputs = alpha_inputs
|
||||
self.btn_pick_color = btn_pick_color
|
||||
|
||||
self._minimum_size_set = False
|
||||
|
||||
if color:
|
||||
self.set_color(color)
|
||||
self.alpha_changed(color.alpha())
|
||||
|
||||
def showEvent(self, event):
|
||||
super(ColorPickerWidget, self).showEvent(event)
|
||||
if self._minimum_size_set:
|
||||
return
|
||||
|
||||
triangle_size = max(int(self.width() / 5 * 3), 180)
|
||||
self.color_triangle.setMinimumWidth(triangle_size)
|
||||
self.color_triangle.setMinimumHeight(triangle_size)
|
||||
self._minimum_size_set = True
|
||||
|
||||
def color(self):
|
||||
return self.color_view.color()
|
||||
|
||||
def set_color(self, color):
|
||||
self.alpha_inputs.set_alpha(color.alpha())
|
||||
self.on_color_change(color)
|
||||
|
||||
def pick_color(self):
|
||||
self.pick_widget.pick_color()
|
||||
|
||||
def triangle_color_changed(self, color):
|
||||
self.color_view.set_color(color)
|
||||
if self.color_inputs_color != color:
|
||||
self.color_inputs_color.setRgb(
|
||||
color.red(), color.green(), color.blue()
|
||||
)
|
||||
for color_input in self.color_input_fields:
|
||||
color_input.color_changed()
|
||||
|
||||
def on_color_change(self, color):
|
||||
self.color_view.set_color(color)
|
||||
self.color_triangle.set_color(color)
|
||||
if self.color_inputs_color != color:
|
||||
self.color_inputs_color.setRgb(
|
||||
color.red(), color.green(), color.blue()
|
||||
)
|
||||
for color_input in self.color_input_fields:
|
||||
color_input.color_changed()
|
||||
|
||||
def _on_color_input_value_change(self):
|
||||
for input_field in self.color_input_fields:
|
||||
input_field.color_changed()
|
||||
self.on_color_change(QtGui.QColor(self.color_inputs_color))
|
||||
|
||||
def alpha_changed(self, value):
|
||||
self.color_view.set_alpha(value)
|
||||
if self.alpha_slider.value() != value:
|
||||
self.alpha_slider.setValue(value)
|
||||
|
||||
if self.alpha_inputs.alpha_value != value:
|
||||
self.alpha_inputs.set_alpha(value)
|
||||
|
||||
def _on_alpha_inputs_changed(self, value):
|
||||
self.alpha_changed(value)
|
||||
|
||||
def _on_alpha_slider_change(self, value):
|
||||
self.alpha_changed(value)
|
||||
248
openpype/widgets/color_widgets/color_screen_pick.py
Normal file
|
|
@ -0,0 +1,248 @@
|
|||
import Qt
|
||||
from Qt import QtWidgets, QtCore, QtGui
|
||||
|
||||
|
||||
class PickScreenColorWidget(QtWidgets.QWidget):
|
||||
color_selected = QtCore.Signal(QtGui.QColor)
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super(PickScreenColorWidget, self).__init__(parent)
|
||||
self.labels = []
|
||||
self.magnification = 2
|
||||
|
||||
self._min_magnification = 1
|
||||
self._max_magnification = 10
|
||||
|
||||
def add_magnification_delta(self, delta):
|
||||
_delta = abs(delta / 1000)
|
||||
if delta > 0:
|
||||
self.magnification += _delta
|
||||
else:
|
||||
self.magnification -= _delta
|
||||
|
||||
if self.magnification > self._max_magnification:
|
||||
self.magnification = self._max_magnification
|
||||
elif self.magnification < self._min_magnification:
|
||||
self.magnification = self._min_magnification
|
||||
|
||||
def pick_color(self):
|
||||
if self.labels:
|
||||
if self.labels[0].isVisible():
|
||||
return
|
||||
self.labels = []
|
||||
|
||||
for screen in QtWidgets.QApplication.screens():
|
||||
label = PickLabel(self)
|
||||
label.pick_color(screen)
|
||||
label.color_selected.connect(self.on_color_select)
|
||||
label.close_session.connect(self.end_pick_session)
|
||||
self.labels.append(label)
|
||||
|
||||
def end_pick_session(self):
|
||||
for label in self.labels:
|
||||
label.close()
|
||||
self.labels = []
|
||||
|
||||
def on_color_select(self, color):
|
||||
self.color_selected.emit(color)
|
||||
self.end_pick_session()
|
||||
|
||||
|
||||
class PickLabel(QtWidgets.QLabel):
|
||||
color_selected = QtCore.Signal(QtGui.QColor)
|
||||
close_session = QtCore.Signal()
|
||||
|
||||
def __init__(self, pick_widget):
|
||||
super(PickLabel, self).__init__()
|
||||
self.setMouseTracking(True)
|
||||
|
||||
self.pick_widget = pick_widget
|
||||
|
||||
self.radius_pen = QtGui.QPen(QtGui.QColor(27, 27, 27), 2)
|
||||
self.text_pen = QtGui.QPen(QtGui.QColor(127, 127, 127), 4)
|
||||
self.text_bg = QtGui.QBrush(QtGui.QColor(27, 27, 27))
|
||||
self._mouse_over = False
|
||||
|
||||
self.radius = 100
|
||||
self.radius_ratio = 11
|
||||
|
||||
@property
|
||||
def magnification(self):
|
||||
return self.pick_widget.magnification
|
||||
|
||||
def pick_color(self, screen_obj):
|
||||
self.show()
|
||||
self.windowHandle().setScreen(screen_obj)
|
||||
geo = screen_obj.geometry()
|
||||
args = (
|
||||
QtWidgets.QApplication.desktop().winId(),
|
||||
geo.x(), geo.y(), geo.width(), geo.height()
|
||||
)
|
||||
if Qt.__binding__ in ("PyQt4", "PySide"):
|
||||
pix = QtGui.QPixmap.grabWindow(*args)
|
||||
else:
|
||||
pix = screen_obj.grabWindow(*args)
|
||||
|
||||
if pix.width() > pix.height():
|
||||
size = pix.height()
|
||||
else:
|
||||
size = pix.width()
|
||||
|
||||
self.radius = int(size / self.radius_ratio)
|
||||
|
||||
self.setPixmap(pix)
|
||||
self.showFullScreen()
|
||||
|
||||
def wheelEvent(self, event):
|
||||
y_delta = event.angleDelta().y()
|
||||
self.pick_widget.add_magnification_delta(y_delta)
|
||||
self.update()
|
||||
|
||||
def enterEvent(self, event):
|
||||
self._mouse_over = True
|
||||
super().enterEvent(event)
|
||||
|
||||
def leaveEvent(self, event):
|
||||
self._mouse_over = False
|
||||
super().leaveEvent(event)
|
||||
self.update()
|
||||
|
||||
def mouseMoveEvent(self, event):
|
||||
self.update()
|
||||
|
||||
def paintEvent(self, event):
|
||||
super().paintEvent(event)
|
||||
if not self._mouse_over:
|
||||
return
|
||||
|
||||
mouse_pos_to_widet = self.mapFromGlobal(QtGui.QCursor.pos())
|
||||
|
||||
magnified_half_size = self.radius / self.magnification
|
||||
magnified_size = magnified_half_size * 2
|
||||
|
||||
zoom_x_1 = mouse_pos_to_widet.x() - magnified_half_size
|
||||
zoom_x_2 = mouse_pos_to_widet.x() + magnified_half_size
|
||||
zoom_y_1 = mouse_pos_to_widet.y() - magnified_half_size
|
||||
zoom_y_2 = mouse_pos_to_widet.y() + magnified_half_size
|
||||
pix_width = magnified_size
|
||||
pix_height = magnified_size
|
||||
draw_pos_x = 0
|
||||
draw_pos_y = 0
|
||||
if zoom_x_1 < 0:
|
||||
draw_pos_x = abs(zoom_x_1)
|
||||
pix_width -= draw_pos_x
|
||||
zoom_x_1 = 1
|
||||
elif zoom_x_2 > self.pixmap().width():
|
||||
pix_width -= zoom_x_2 - self.pixmap().width()
|
||||
|
||||
if zoom_y_1 < 0:
|
||||
draw_pos_y = abs(zoom_y_1)
|
||||
pix_height -= draw_pos_y
|
||||
zoom_y_1 = 1
|
||||
elif zoom_y_2 > self.pixmap().height():
|
||||
pix_height -= zoom_y_2 - self.pixmap().height()
|
||||
|
||||
new_pix = QtGui.QPixmap(magnified_size, magnified_size)
|
||||
new_pix.fill(QtCore.Qt.transparent)
|
||||
new_pix_painter = QtGui.QPainter(new_pix)
|
||||
new_pix_painter.drawPixmap(
|
||||
QtCore.QRect(draw_pos_x, draw_pos_y, pix_width, pix_height),
|
||||
self.pixmap().copy(zoom_x_1, zoom_y_1, pix_width, pix_height)
|
||||
)
|
||||
new_pix_painter.end()
|
||||
|
||||
painter = QtGui.QPainter(self)
|
||||
|
||||
ellipse_rect = QtCore.QRect(
|
||||
mouse_pos_to_widet.x() - self.radius,
|
||||
mouse_pos_to_widet.y() - self.radius,
|
||||
self.radius * 2,
|
||||
self.radius * 2
|
||||
)
|
||||
ellipse_rect_f = QtCore.QRectF(ellipse_rect)
|
||||
path = QtGui.QPainterPath()
|
||||
path.addEllipse(ellipse_rect_f)
|
||||
painter.setClipPath(path)
|
||||
|
||||
new_pix_rect = QtCore.QRect(
|
||||
mouse_pos_to_widet.x() - self.radius + 1,
|
||||
mouse_pos_to_widet.y() - self.radius + 1,
|
||||
new_pix.width() * self.magnification,
|
||||
new_pix.height() * self.magnification
|
||||
)
|
||||
|
||||
painter.drawPixmap(new_pix_rect, new_pix)
|
||||
|
||||
painter.setClipping(False)
|
||||
|
||||
painter.setRenderHint(QtGui.QPainter.Antialiasing)
|
||||
|
||||
painter.setPen(self.radius_pen)
|
||||
painter.drawEllipse(ellipse_rect_f)
|
||||
|
||||
image = self.pixmap().toImage()
|
||||
if image.valid(mouse_pos_to_widet):
|
||||
color = QtGui.QColor(image.pixel(mouse_pos_to_widet))
|
||||
else:
|
||||
color = QtGui.QColor()
|
||||
|
||||
color_text = "Red: {} - Green: {} - Blue: {}".format(
|
||||
color.red(), color.green(), color.blue()
|
||||
)
|
||||
font = painter.font()
|
||||
font.setPointSize(self.radius / 10)
|
||||
painter.setFont(font)
|
||||
|
||||
text_rect_height = int(painter.fontMetrics().height() + 10)
|
||||
text_rect = QtCore.QRect(
|
||||
ellipse_rect.x(),
|
||||
ellipse_rect.bottom(),
|
||||
ellipse_rect.width(),
|
||||
text_rect_height
|
||||
)
|
||||
if text_rect.bottom() > self.pixmap().height():
|
||||
text_rect.moveBottomLeft(ellipse_rect.topLeft())
|
||||
|
||||
rect_radius = text_rect_height / 2
|
||||
path = QtGui.QPainterPath()
|
||||
path.addRoundedRect(
|
||||
QtCore.QRectF(text_rect),
|
||||
rect_radius,
|
||||
rect_radius
|
||||
)
|
||||
painter.fillPath(path, self.text_bg)
|
||||
|
||||
painter.setPen(self.text_pen)
|
||||
painter.drawText(
|
||||
text_rect,
|
||||
QtCore.Qt.AlignLeft | QtCore.Qt.AlignCenter,
|
||||
color_text
|
||||
)
|
||||
|
||||
color_rect_x = ellipse_rect.x() - text_rect_height
|
||||
if color_rect_x < 0:
|
||||
color_rect_x += (text_rect_height + ellipse_rect.width())
|
||||
|
||||
color_rect = QtCore.QRect(
|
||||
color_rect_x,
|
||||
ellipse_rect.y(),
|
||||
text_rect_height,
|
||||
ellipse_rect.height()
|
||||
)
|
||||
path = QtGui.QPainterPath()
|
||||
path.addRoundedRect(
|
||||
QtCore.QRectF(color_rect),
|
||||
rect_radius,
|
||||
rect_radius
|
||||
)
|
||||
painter.fillPath(path, color)
|
||||
painter.drawRoundedRect(color_rect, rect_radius, rect_radius)
|
||||
painter.end()
|
||||
|
||||
def mouseReleaseEvent(self, event):
|
||||
color = QtGui.QColor(self.pixmap().toImage().pixel(event.pos()))
|
||||
self.color_selected.emit(color)
|
||||
|
||||
def keyPressEvent(self, event):
|
||||
if event.key() == QtCore.Qt.Key_Escape:
|
||||
self.close_session.emit()
|
||||
1431
openpype/widgets/color_widgets/color_triangle.py
Normal file
83
openpype/widgets/color_widgets/color_view.py
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
from Qt import QtWidgets, QtCore, QtGui
|
||||
|
||||
|
||||
def draw_checkerboard_tile(piece_size=None, color_1=None, color_2=None):
|
||||
if piece_size is None:
|
||||
piece_size = 7
|
||||
|
||||
if color_1 is None:
|
||||
color_1 = QtGui.QColor(188, 188, 188)
|
||||
|
||||
if color_2 is None:
|
||||
color_2 = QtGui.QColor(90, 90, 90)
|
||||
|
||||
pix = QtGui.QPixmap(piece_size * 2, piece_size * 2)
|
||||
pix_painter = QtGui.QPainter(pix)
|
||||
|
||||
rect = QtCore.QRect(
|
||||
0, 0, piece_size, piece_size
|
||||
)
|
||||
pix_painter.fillRect(rect, color_1)
|
||||
rect.moveTo(piece_size, piece_size)
|
||||
pix_painter.fillRect(rect, color_1)
|
||||
rect.moveTo(piece_size, 0)
|
||||
pix_painter.fillRect(rect, color_2)
|
||||
rect.moveTo(0, piece_size)
|
||||
pix_painter.fillRect(rect, color_2)
|
||||
pix_painter.end()
|
||||
|
||||
return pix
|
||||
|
||||
|
||||
class ColorViewer(QtWidgets.QWidget):
|
||||
def __init__(self, parent=None):
|
||||
super(ColorViewer, self).__init__(parent)
|
||||
|
||||
self.setMinimumSize(10, 10)
|
||||
|
||||
self.alpha = 255
|
||||
self.actual_pen = QtGui.QPen()
|
||||
self.actual_color = QtGui.QColor()
|
||||
self._checkerboard = None
|
||||
|
||||
def checkerboard(self):
|
||||
if not self._checkerboard:
|
||||
self._checkerboard = draw_checkerboard_tile(4)
|
||||
return self._checkerboard
|
||||
|
||||
def color(self):
|
||||
return self.actual_color
|
||||
|
||||
def set_color(self, color):
|
||||
if color == self.actual_color:
|
||||
return
|
||||
|
||||
# Create copy of entered color
|
||||
self.actual_color = QtGui.QColor(color)
|
||||
# Set alpha by current alpha value
|
||||
self.actual_color.setAlpha(self.alpha)
|
||||
# Repaint
|
||||
self.update()
|
||||
|
||||
def set_alpha(self, alpha):
|
||||
if alpha == self.alpha:
|
||||
return
|
||||
# Change alpha of current color
|
||||
self.actual_color.setAlpha(alpha)
|
||||
# Store the value
|
||||
self.alpha = alpha
|
||||
# Repaint
|
||||
self.update()
|
||||
|
||||
def paintEvent(self, event):
|
||||
clip_rect = event.rect()
|
||||
rect = clip_rect.adjusted(0, 0, -1, -1)
|
||||
|
||||
painter = QtGui.QPainter(self)
|
||||
painter.setClipRect(clip_rect)
|
||||
painter.drawTiledPixmap(rect, self.checkerboard())
|
||||
painter.setBrush(self.actual_color)
|
||||
pen = QtGui.QPen(QtGui.QColor(255, 255, 255, 67))
|
||||
painter.setPen(pen)
|
||||
painter.drawRect(rect)
|
||||
painter.end()
|
||||
BIN
openpype/widgets/color_widgets/eyedropper.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
|
|
@ -12,6 +12,14 @@
|
|||
|
||||
PS> .\build.ps1
|
||||
|
||||
.EXAMPLE
|
||||
|
||||
To build without automatical submodule update:
|
||||
PS> .\build.ps1 --no-submodule-update
|
||||
|
||||
.LINK
|
||||
https://openpype.io/docs
|
||||
|
||||
#>
|
||||
|
||||
$arguments=$ARGS
|
||||
|
|
@ -82,17 +90,17 @@ $art = @"
|
|||
|
||||
. . .. . ..
|
||||
_oOOP3OPP3Op_. .
|
||||
.PPpo~· ·· ~2p. ·· ···· · ·
|
||||
·Ppo · .pPO3Op.· · O:· · · ·
|
||||
.3Pp · oP3'· 'P33· · 4 ·· · · · ·· · · ·
|
||||
·~OP 3PO· .Op3 : · ·· _____ _____ _____
|
||||
·P3O · oP3oP3O3P' · · · · / /·/ /·/ /
|
||||
O3:· O3p~ · ·:· · ·/____/·/____/ /____/
|
||||
'P · 3p3· oP3~· ·.P:· · · ·· · · ·· · · ·
|
||||
· ': · Po' ·Opo'· .3O· . o[ by Pype Club ]]]==- - - · ·
|
||||
· '_ .. · . _OP3·· · ·https://openpype.io·· ·
|
||||
~P3·OPPPO3OP~ · ·· ·
|
||||
· ' '· · ·· · · · ·· ·
|
||||
.PPpo~. .. ~2p. .. .... . .
|
||||
.Ppo . .pPO3Op.. . O:. . . .
|
||||
.3Pp . oP3'. 'P33. . 4 .. . . . .. . . .
|
||||
.~OP 3PO. .Op3 : . .. _____ _____ _____
|
||||
.P3O . oP3oP3O3P' . . . . / /./ /./ /
|
||||
O3:. O3p~ . .:. . ./____/./____/ /____/
|
||||
'P . 3p3. oP3~. ..P:. . . .. . . .. . . .
|
||||
. ': . Po' .Opo'. .3O. . o[ by Pype Club ]]]==- - - . .
|
||||
. '_ .. . . _OP3.. . .https://openpype.io.. .
|
||||
~P3.OPPPO3OP~ . .. .
|
||||
. ' '. . .. . . . .. .
|
||||
|
||||
"@
|
||||
|
||||
|
|
|
|||
|
|
@ -1,16 +1,14 @@
|
|||
<#
|
||||
.SYNOPSIS
|
||||
Helper script to build OpenPype.
|
||||
Helper script to build OpenPype Installer.
|
||||
|
||||
.DESCRIPTION
|
||||
This script will detect Python installation, and build OpenPype to `build`
|
||||
directory using existing virtual environment created by Poetry (or
|
||||
by running `/tools/create_venv.ps1`). It will then shuffle dependencies in
|
||||
build folder to optimize for different Python versions (2/3) in Python host.
|
||||
This script will use already built OpenPype (in `build` directory) and
|
||||
create Windows installer from it using Inno Setup (https://jrsoftware.org/)
|
||||
|
||||
.EXAMPLE
|
||||
|
||||
PS> .\build.ps1
|
||||
PS> .\build_win_installer.ps1
|
||||
|
||||
#>
|
||||
|
||||
|
|
@ -76,11 +74,19 @@ function Install-Poetry() {
|
|||
|
||||
$art = @"
|
||||
|
||||
▒█▀▀▀█ █▀▀█ █▀▀ █▀▀▄ ▒█▀▀█ █░░█ █▀▀█ █▀▀ ▀█▀ ▀█▀ ▀█▀
|
||||
▒█░░▒█ █░░█ █▀▀ █░░█ ▒█▄▄█ █▄▄█ █░░█ █▀▀ ▒█░ ▒█░ ▒█░
|
||||
▒█▄▄▄█ █▀▀▀ ▀▀▀ ▀░░▀ ▒█░░░ ▄▄▄█ █▀▀▀ ▀▀▀ ▄█▄ ▄█▄ ▄█▄
|
||||
.---= [ by Pype Club ] =---.
|
||||
https://openpype.io
|
||||
. . .. . ..
|
||||
_oOOP3OPP3Op_. .
|
||||
.PPpo~. .. ~2p. .. .... . .
|
||||
.Ppo . .pPO3Op.. . O:. . . .
|
||||
.3Pp . oP3'. 'P33. . 4 .. . . . .. . . .
|
||||
.~OP 3PO. .Op3 : . .. _____ _____ _____
|
||||
.P3O . oP3oP3O3P' . . . . / /./ /./ /
|
||||
O3:. O3p~ . .:. . ./____/./____/ /____/
|
||||
'P . 3p3. oP3~. ..P:. . . .. . . .. . . .
|
||||
. ': . Po' .Opo'. .3O. . o[ by Pype Club ]]]==- - - . .
|
||||
. '_ .. . . _OP3.. . .https://openpype.io.. .
|
||||
~P3.OPPPO3OP~ . .. .
|
||||
. ' '. . .. . . . .. .
|
||||
|
||||
"@
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,11 @@
|
|||
|
||||
PS> .\create_env.ps1
|
||||
|
||||
.EXAMPLE
|
||||
|
||||
Print verbose information from Poetry:
|
||||
PS> .\create_env.ps1 --verbose
|
||||
|
||||
#>
|
||||
|
||||
$arguments=$ARGS
|
||||
|
|
@ -98,17 +103,17 @@ $art = @"
|
|||
|
||||
. . .. . ..
|
||||
_oOOP3OPP3Op_. .
|
||||
.PPpo~· ·· ~2p. ·· ···· · ·
|
||||
·Ppo · .pPO3Op.· · O:· · · ·
|
||||
.3Pp · oP3'· 'P33· · 4 ·· · · · ·· · · ·
|
||||
·~OP 3PO· .Op3 : · ·· _____ _____ _____
|
||||
·P3O · oP3oP3O3P' · · · · / /·/ /·/ /
|
||||
O3:· O3p~ · ·:· · ·/____/·/____/ /____/
|
||||
'P · 3p3· oP3~· ·.P:· · · ·· · · ·· · · ·
|
||||
· ': · Po' ·Opo'· .3O· . o[ by Pype Club ]]]==- - - · ·
|
||||
· '_ .. · . _OP3·· · ·https://openpype.io·· ·
|
||||
~P3·OPPPO3OP~ · ·· ·
|
||||
· ' '· · ·· · · · ·· ·
|
||||
.PPpo~. .. ~2p. .. .... . .
|
||||
.Ppo . .pPO3Op.. . O:. . . .
|
||||
.3Pp . oP3'. 'P33. . 4 .. . . . .. . . .
|
||||
.~OP 3PO. .Op3 : . .. _____ _____ _____
|
||||
.P3O . oP3oP3O3P' . . . . / /./ /./ /
|
||||
O3:. O3p~ . .:. . ./____/./____/ /____/
|
||||
'P . 3p3. oP3~. ..P:. . . .. . . .. . . .
|
||||
. ': . Po' .Opo'. .3O. . o[ by Pype Club ]]]==- - - . .
|
||||
. '_ .. . . _OP3.. . .https://openpype.io.. .
|
||||
~P3.OPPPO3OP~ . .. .
|
||||
. ' '. . .. . . . .. .
|
||||
|
||||
|
||||
"@
|
||||
|
|
|
|||
|
|
@ -4,14 +4,19 @@
|
|||
|
||||
.DESCRIPTION
|
||||
This script will detect Python installation and run OpenPype to create
|
||||
zip. It needs mongodb running. I will create zip from current source code
|
||||
version and copy it top `%LOCALAPPDATA%/pypeclub/pype` if `--path` or `-p`
|
||||
zip. It will create zip from current source code
|
||||
version and copy it top `%LOCALAPPDATA%/pypeclub/openpype` if `--path` or `-p`
|
||||
argument is not used.
|
||||
|
||||
.EXAMPLE
|
||||
|
||||
PS> .\create_zip.ps1
|
||||
|
||||
.EXAMPLE
|
||||
|
||||
To put generated zip to C:\OpenPype directory:
|
||||
PS> .\create_zip.ps1 --path C:\OpenPype
|
||||
|
||||
#>
|
||||
|
||||
function Exit-WithCode($exitcode) {
|
||||
|
|
@ -52,17 +57,17 @@ $art = @"
|
|||
|
||||
. . .. . ..
|
||||
_oOOP3OPP3Op_. .
|
||||
.PPpo~· ·· ~2p. ·· ···· · ·
|
||||
·Ppo · .pPO3Op.· · O:· · · ·
|
||||
.3Pp · oP3'· 'P33· · 4 ·· · · · ·· · · ·
|
||||
·~OP 3PO· .Op3 : · ·· _____ _____ _____
|
||||
·P3O · oP3oP3O3P' · · · · / /·/ /·/ /
|
||||
O3:· O3p~ · ·:· · ·/____/·/____/ /____/
|
||||
'P · 3p3· oP3~· ·.P:· · · ·· · · ·· · · ·
|
||||
· ': · Po' ·Opo'· .3O· . o[ by Pype Club ]]]==- - - · ·
|
||||
· '_ .. · . _OP3·· · ·https://openpype.io·· ·
|
||||
~P3·OPPPO3OP~ · ·· ·
|
||||
· ' '· · ·· · · · ·· ·
|
||||
.PPpo~. .. ~2p. .. .... . .
|
||||
.Ppo . .pPO3Op.. . O:. . . .
|
||||
.3Pp . oP3'. 'P33. . 4 .. . . . .. . . .
|
||||
.~OP 3PO. .Op3 : . .. _____ _____ _____
|
||||
.P3O . oP3oP3O3P' . . . . / /./ /./ /
|
||||
O3:. O3p~ . .:. . ./____/./____/ /____/
|
||||
'P . 3p3. oP3~. ..P:. . . .. . . .. . . .
|
||||
. ': . Po' .Opo'. .3O. . o[ by Pype Club ]]]==- - - . .
|
||||
. '_ .. . . _OP3.. . .https://openpype.io.. .
|
||||
~P3.OPPPO3OP~ . .. .
|
||||
. ' '. . .. . . . .. .
|
||||
|
||||
"@
|
||||
|
||||
|
|
|
|||
|
|
@ -32,17 +32,17 @@ $art = @"
|
|||
|
||||
. . .. . ..
|
||||
_oOOP3OPP3Op_. .
|
||||
.PPpo~· ·· ~2p. ·· ···· · ·
|
||||
·Ppo · .pPO3Op.· · O:· · · ·
|
||||
.3Pp · oP3'· 'P33· · 4 ·· · · · ·· · · ·
|
||||
·~OP 3PO· .Op3 : · ·· _____ _____ _____
|
||||
·P3O · oP3oP3O3P' · · · · / /·/ /·/ /
|
||||
O3:· O3p~ · ·:· · ·/____/·/____/ /____/
|
||||
'P · 3p3· oP3~· ·.P:· · · ·· · · ·· · · ·
|
||||
· ': · Po' ·Opo'· .3O· . o[ by Pype Club ]]]==- - - · ·
|
||||
· '_ .. · . _OP3·· · ·https://openpype.io·· ·
|
||||
~P3·OPPPO3OP~ · ·· ·
|
||||
· ' '· · ·· · · · ·· ·
|
||||
.PPpo~. .. ~2p. .. .... . .
|
||||
.Ppo . .pPO3Op.. . O:. . . .
|
||||
.3Pp . oP3'. 'P33. . 4 .. . . . .. . . .
|
||||
.~OP 3PO. .Op3 : . .. _____ _____ _____
|
||||
.P3O . oP3oP3O3P' . . . . / /./ /./ /
|
||||
O3:. O3p~ . .:. . ./____/./____/ /____/
|
||||
'P . 3p3. oP3~. ..P:. . . .. . . .. . . .
|
||||
. ': . Po' .Opo'. .3O. . o[ by Pype Club ]]]==- - - . .
|
||||
. '_ .. . . _OP3.. . .https://openpype.io.. .
|
||||
~P3.OPPPO3OP~ . .. .
|
||||
. ' '. . .. . . . .. .
|
||||
|
||||
"@
|
||||
|
||||
|
|
|
|||
|
|
@ -1,23 +1,38 @@
|
|||
<#
|
||||
.SYNOPSIS
|
||||
Helper script to run mongodb.
|
||||
Helper script to run Docusaurus for easy editing of OpenPype documentation.
|
||||
|
||||
.DESCRIPTION
|
||||
This script will detect mongodb, add it to the PATH and launch it on specified port and db location.
|
||||
This script is using `yarn` package manager to run Docusaurus. If you don't
|
||||
have `yarn`, install Node.js (https://nodejs.org/) and then run:
|
||||
|
||||
npm install -g yarn
|
||||
|
||||
It take some time to run this script. If all is successful you should see
|
||||
new browser window with OpenPype documentation. All changes is markdown files
|
||||
under .\website should be immediately seen in browser.
|
||||
|
||||
.EXAMPLE
|
||||
|
||||
PS> .\run_mongo.ps1
|
||||
PS> .\run_documentation.ps1
|
||||
|
||||
#>
|
||||
|
||||
$art = @"
|
||||
|
||||
▒█▀▀▀█ █▀▀█ █▀▀ █▀▀▄ ▒█▀▀█ █░░█ █▀▀█ █▀▀ ▀█▀ ▀█▀ ▀█▀
|
||||
▒█░░▒█ █░░█ █▀▀ █░░█ ▒█▄▄█ █▄▄█ █░░█ █▀▀ ▒█░ ▒█░ ▒█░
|
||||
▒█▄▄▄█ █▀▀▀ ▀▀▀ ▀░░▀ ▒█░░░ ▄▄▄█ █▀▀▀ ▀▀▀ ▄█▄ ▄█▄ ▄█▄
|
||||
.---= [ by Pype Club ] =---.
|
||||
https://openpype.io
|
||||
. . .. . ..
|
||||
_oOOP3OPP3Op_. .
|
||||
.PPpo~. .. ~2p. .. .... . .
|
||||
.Ppo . .pPO3Op.. . O:. . . .
|
||||
.3Pp . oP3'. 'P33. . 4 .. . . . .. . . .
|
||||
.~OP 3PO. .Op3 : . .. _____ _____ _____
|
||||
.P3O . oP3oP3O3P' . . . . / /./ /./ /
|
||||
O3:. O3p~ . .:. . ./____/./____/ /____/
|
||||
'P . 3p3. oP3~. ..P:. . . .. . . .. . . .
|
||||
. ': . Po' .Opo'. .3O. . o[ by Pype Club ]]]==- - - . .
|
||||
. '_ .. . . _OP3.. . .https://openpype.io.. .
|
||||
~P3.OPPPO3OP~ . .. .
|
||||
. ' '. . .. . . . .. .
|
||||
|
||||
"@
|
||||
|
||||
|
|
@ -26,7 +41,6 @@ Write-Host $art -ForegroundColor DarkGreen
|
|||
$script_dir = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
|
||||
$openpype_root = (Get-Item $script_dir).parent.FullName
|
||||
|
||||
cd $openpype_root/website
|
||||
|
||||
yarn run start
|
||||
Set-Location $openpype_root/website
|
||||
|
||||
& yarn run start
|
||||
|
|
|
|||
|
|
@ -15,17 +15,17 @@ $art = @"
|
|||
|
||||
. . .. . ..
|
||||
_oOOP3OPP3Op_. .
|
||||
.PPpo~· ·· ~2p. ·· ···· · ·
|
||||
·Ppo · .pPO3Op.· · O:· · · ·
|
||||
.3Pp · oP3'· 'P33· · 4 ·· · · · ·· · · ·
|
||||
·~OP 3PO· .Op3 : · ·· _____ _____ _____
|
||||
·P3O · oP3oP3O3P' · · · · / /·/ /·/ /
|
||||
O3:· O3p~ · ·:· · ·/____/·/____/ /____/
|
||||
'P · 3p3· oP3~· ·.P:· · · ·· · · ·· · · ·
|
||||
· ': · Po' ·Opo'· .3O· . o[ by Pype Club ]]]==- - - · ·
|
||||
· '_ .. · . _OP3·· · ·https://openpype.io·· ·
|
||||
~P3·OPPPO3OP~ · ·· ·
|
||||
· ' '· · ·· · · · ·· ·
|
||||
.PPpo~. .. ~2p. .. .... . .
|
||||
.Ppo . .pPO3Op.. . O:. . . .
|
||||
.3Pp . oP3'. 'P33. . 4 .. . . . .. . . .
|
||||
.~OP 3PO. .Op3 : . .. _____ _____ _____
|
||||
.P3O . oP3oP3O3P' . . . . / /./ /./ /
|
||||
O3:. O3p~ . .:. . ./____/./____/ /____/
|
||||
'P . 3p3. oP3~. ..P:. . . .. . . .. . . .
|
||||
. ': . Po' .Opo'. .3O. . o[ by Pype Club ]]]==- - - . .
|
||||
. '_ .. . . _OP3.. . .https://openpype.io.. .
|
||||
~P3.OPPPO3OP~ . .. .
|
||||
. ' '. . .. . . . .. .
|
||||
|
||||
"@
|
||||
|
||||
|
|
|
|||
|
|
@ -1,18 +1,60 @@
|
|||
<#
|
||||
.SYNOPSIS
|
||||
Helper script OpenPype Tray.
|
||||
Helper script to run Project Manager.
|
||||
|
||||
.DESCRIPTION
|
||||
|
||||
|
||||
.EXAMPLE
|
||||
|
||||
PS> .\run_tray.ps1
|
||||
PS> .\run_project_manager.ps1
|
||||
|
||||
#>
|
||||
|
||||
$art = @"
|
||||
|
||||
. . .. . ..
|
||||
_oOOP3OPP3Op_. .
|
||||
.PPpo~. .. ~2p. .. .... . .
|
||||
.Ppo . .pPO3Op.. . O:. . . .
|
||||
.3Pp . oP3'. 'P33. . 4 .. . . . .. . . .
|
||||
.~OP 3PO. .Op3 : . .. _____ _____ _____
|
||||
.P3O . oP3oP3O3P' . . . . / /./ /./ /
|
||||
O3:. O3p~ . .:. . ./____/./____/ /____/
|
||||
'P . 3p3. oP3~. ..P:. . . .. . . .. . . .
|
||||
. ': . Po' .Opo'. .3O. . o[ by Pype Club ]]]==- - - . .
|
||||
. '_ .. . . _OP3.. . .https://openpype.io.. .
|
||||
~P3.OPPPO3OP~ . .. .
|
||||
. ' '. . .. . . . .. .
|
||||
|
||||
"@
|
||||
|
||||
Write-Host $art -ForegroundColor DarkGreen
|
||||
|
||||
$current_dir = Get-Location
|
||||
$script_dir = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
|
||||
$openpype_root = (Get-Item $script_dir).parent.FullName
|
||||
|
||||
$env:_INSIDE_OPENPYPE_TOOL = "1"
|
||||
|
||||
# make sure Poetry is in PATH
|
||||
if (-not (Test-Path 'env:POETRY_HOME')) {
|
||||
$env:POETRY_HOME = "$openpype_root\.poetry"
|
||||
}
|
||||
$env:PATH = "$($env:PATH);$($env:POETRY_HOME)\bin"
|
||||
|
||||
Set-Location -Path $openpype_root
|
||||
|
||||
Write-Host ">>> " -NoNewline -ForegroundColor Green
|
||||
Write-Host "Reading Poetry ... " -NoNewline
|
||||
if (-not (Test-Path -PathType Container -Path "$openpype_root\.poetry\bin")) {
|
||||
Write-Host "NOT FOUND" -ForegroundColor Yellow
|
||||
Write-Host "*** " -NoNewline -ForegroundColor Yellow
|
||||
Write-Host "We need to install Poetry create virtual env first ..."
|
||||
& "$openpype_root\tools\create_env.ps1"
|
||||
} else {
|
||||
Write-Host "OK" -ForegroundColor Green
|
||||
}
|
||||
|
||||
& poetry run python "$($openpype_root)\start.py" projectmanager
|
||||
Set-Location -Path $current_dir
|
||||
|
|
|
|||
103
tools/run_projectmanager.sh
Executable file
|
|
@ -0,0 +1,103 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# Run OpenPype Settings GUI
|
||||
|
||||
|
||||
art () {
|
||||
cat <<-EOF
|
||||
|
||||
. . .. . ..
|
||||
_oOOP3OPP3Op_. .
|
||||
.PPpo~· ·· ~2p. ·· ···· · ·
|
||||
·Ppo · .pPO3Op.· · O:· · · ·
|
||||
.3Pp · oP3'· 'P33· · 4 ·· · · · ·· · · ·
|
||||
·~OP 3PO· .Op3 : · ·· _____ _____ _____
|
||||
·P3O · oP3oP3O3P' · · · · / /·/ /·/ /
|
||||
O3:· O3p~ · ·:· · ·/____/·/____/ /____/
|
||||
'P · 3p3· oP3~· ·.P:· · · ·· · · ·· · · ·
|
||||
· ': · Po' ·Opo'· .3O· . o[ by Pype Club ]]]==- - - · ·
|
||||
· '_ .. · . _OP3·· · ·https://openpype.io·· ·
|
||||
~P3·OPPPO3OP~ · ·· ·
|
||||
· ' '· · ·· · · · ·· ·
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
# Colors for terminal
|
||||
|
||||
RST='\033[0m' # Text Reset
|
||||
|
||||
# Regular Colors
|
||||
Black='\033[0;30m' # Black
|
||||
Red='\033[0;31m' # Red
|
||||
Green='\033[0;32m' # Green
|
||||
Yellow='\033[0;33m' # Yellow
|
||||
Blue='\033[0;34m' # Blue
|
||||
Purple='\033[0;35m' # Purple
|
||||
Cyan='\033[0;36m' # Cyan
|
||||
White='\033[0;37m' # White
|
||||
|
||||
# Bold
|
||||
BBlack='\033[1;30m' # Black
|
||||
BRed='\033[1;31m' # Red
|
||||
BGreen='\033[1;32m' # Green
|
||||
BYellow='\033[1;33m' # Yellow
|
||||
BBlue='\033[1;34m' # Blue
|
||||
BPurple='\033[1;35m' # Purple
|
||||
BCyan='\033[1;36m' # Cyan
|
||||
BWhite='\033[1;37m' # White
|
||||
|
||||
# Bold High Intensity
|
||||
BIBlack='\033[1;90m' # Black
|
||||
BIRed='\033[1;91m' # Red
|
||||
BIGreen='\033[1;92m' # Green
|
||||
BIYellow='\033[1;93m' # Yellow
|
||||
BIBlue='\033[1;94m' # Blue
|
||||
BIPurple='\033[1;95m' # Purple
|
||||
BICyan='\033[1;96m' # Cyan
|
||||
BIWhite='\033[1;97m' # White
|
||||
|
||||
|
||||
##############################################################################
|
||||
# Return absolute path
|
||||
# Globals:
|
||||
# None
|
||||
# Arguments:
|
||||
# Path to resolve
|
||||
# Returns:
|
||||
# None
|
||||
###############################################################################
|
||||
realpath () {
|
||||
echo $(cd $(dirname "$1"); pwd)/$(basename "$1")
|
||||
}
|
||||
|
||||
# Main
|
||||
main () {
|
||||
|
||||
# Directories
|
||||
openpype_root=$(realpath $(dirname $(dirname "${BASH_SOURCE[0]}")))
|
||||
|
||||
_inside_openpype_tool="1"
|
||||
|
||||
# make sure Poetry is in PATH
|
||||
if [[ -z $POETRY_HOME ]]; then
|
||||
export POETRY_HOME="$openpype_root/.poetry"
|
||||
fi
|
||||
export PATH="$POETRY_HOME/bin:$PATH"
|
||||
|
||||
pushd "$openpype_root" > /dev/null || return > /dev/null
|
||||
|
||||
echo -e "${BIGreen}>>>${RST} Reading Poetry ... \c"
|
||||
if [ -f "$POETRY_HOME/bin/poetry" ]; then
|
||||
echo -e "${BIGreen}OK${RST}"
|
||||
else
|
||||
echo -e "${BIYellow}NOT FOUND${RST}"
|
||||
echo -e "${BIYellow}***${RST} We need to install Poetry and virtual env ..."
|
||||
. "$openpype_root/tools/create_env.sh" || { echo -e "${BIRed}!!!${RST} Poetry installation failed"; return; }
|
||||
fi
|
||||
|
||||
echo -e "${BIGreen}>>>${RST} Generating zip from current sources ..."
|
||||
poetry run python "$openpype_root/start.py" projectmanager
|
||||
}
|
||||
|
||||
main
|
||||
|
|
@ -34,17 +34,17 @@ $art = @"
|
|||
|
||||
. . .. . ..
|
||||
_oOOP3OPP3Op_. .
|
||||
.PPpo~· ·· ~2p. ·· ···· · ·
|
||||
·Ppo · .pPO3Op.· · O:· · · ·
|
||||
.3Pp · oP3'· 'P33· · 4 ·· · · · ·· · · ·
|
||||
·~OP 3PO· .Op3 : · ·· _____ _____ _____
|
||||
·P3O · oP3oP3O3P' · · · · / /·/ /·/ /
|
||||
O3:· O3p~ · ·:· · ·/____/·/____/ /____/
|
||||
'P · 3p3· oP3~· ·.P:· · · ·· · · ·· · · ·
|
||||
· ': · Po' ·Opo'· .3O· . o[ by Pype Club ]]]==- - - · ·
|
||||
· '_ .. · . _OP3·· · ·https://openpype.io·· ·
|
||||
~P3·OPPPO3OP~ · ·· ·
|
||||
· ' '· · ·· · · · ·· ·
|
||||
.PPpo~. .. ~2p. .. .... . .
|
||||
.Ppo . .pPO3Op.. . O:. . . .
|
||||
.3Pp . oP3'. 'P33. . 4 .. . . . .. . . .
|
||||
.~OP 3PO. .Op3 : . .. _____ _____ _____
|
||||
.P3O . oP3oP3O3P' . . . . / /./ /./ /
|
||||
O3:. O3p~ . .:. . ./____/./____/ /____/
|
||||
'P . 3p3. oP3~. ..P:. . . .. . . .. . . .
|
||||
. ': . Po' .Opo'. .3O. . o[ by Pype Club ]]]==- - - . .
|
||||
. '_ .. . . _OP3.. . .https://openpype.io.. .
|
||||
~P3.OPPPO3OP~ . .. .
|
||||
. ' '. . .. . . . .. .
|
||||
|
||||
"@
|
||||
|
||||
|
|
|
|||
82
website/docs/artist_install.md
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
---
|
||||
id: artist_install
|
||||
title: Installation
|
||||
sidebar_label: Installation
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
OpenPype comes in packages for Windows (10 or Server), Mac OS X (Mojave or higher), and Linux distribution (Centos, Ubuntu), and you can install them on your machine the same way as you are used to.
|
||||
|
||||
:::important
|
||||
To install OpenPype you will need administrator permissions.
|
||||
:::
|
||||
|
||||
:::note pick your platform
|
||||
<Tabs
|
||||
defaultValue='win'
|
||||
values={[
|
||||
{label: 'Windows', value: 'win'},
|
||||
{label: 'Linux', value: 'linux'},
|
||||
{label: 'Mac OS X', value: 'mac'},
|
||||
]}>
|
||||
|
||||
<TabItem value='win'>
|
||||
|
||||
For installation on Windows, download and run the executable file `OpenPype-3.0.0.exe`.
|
||||
During the installation process, you can change the destination location path of the application,
|
||||
|
||||

|
||||
|
||||
and create an icon on the desktop.
|
||||
|
||||

|
||||
|
||||
</TabItem>
|
||||
|
||||
|
||||
<TabItem value='linux'>
|
||||
|
||||
For installation on your Linux distribution, download and unzip `OpenPype-3.0.0.zip`. A new folder `OpenPype-3.0.0` will be created.
|
||||
Inside this folder find and run `openpype_gui`,
|
||||
|
||||

|
||||
|
||||
</TabItem>
|
||||
|
||||
|
||||
<TabItem value='mac'>
|
||||
|
||||
For installation on Mac OS X, download and run dmg image file `OpenPype-3.0.0.dmg`.
|
||||
|
||||
Drag the OpenPype icon into the Application folder.
|
||||
|
||||

|
||||
|
||||
After the installation, you can find OpenPype among the other Applications.
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
:::
|
||||
|
||||
## Run OpenPype
|
||||
|
||||
To run OpenPype click on the icon or find executable file (e.g. `C:\Program Files (x86)\OpenPype\openpype_gui.exe`) in the application location.
|
||||
On the very first run of OpenPype the user will be asked for OpenPype Mongo URL.
|
||||
This piece of information will be provided by the administrator or project manager who set up the studio.
|
||||
|
||||

|
||||
|
||||
Once the Mongo URL address is entered, press `Start`, and OpenPype will be initiated.
|
||||
OpenPype will also remember the connection for the next launch, so it is a one-time process.
|
||||
|
||||
:::note
|
||||
If the launch was successful, the artist should see a turquoise OpenPype logo in their
|
||||
tray menu. Keep in mind that on Windows this icon might be hidden by default, in which case, the artist can simply drag the icon down to the tray.
|
||||
|
||||

|
||||
:::
|
||||
BIN
website/docs/assets/artist_systray.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
website/docs/assets/install_01.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
website/docs/assets/install_02.png
Normal file
|
After Width: | Height: | Size: 9.1 KiB |
BIN
website/docs/assets/install_03.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
website/docs/assets/install_04.png
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
website/docs/assets/install_05.png
Normal file
|
After Width: | Height: | Size: 7.9 KiB |
|
|
@ -9,6 +9,7 @@ module.exports = {
|
|||
"artist_concepts",
|
||||
"artist_publish",
|
||||
"artist_tools",
|
||||
"artist_install"
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
|
|||