mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +01:00
Merge remote-tracking branch 'origin/develop' into feature/royalrender-integration
This commit is contained in:
commit
96a6fb9d4a
101 changed files with 6317 additions and 1819 deletions
53
CHANGELOG.md
53
CHANGELOG.md
|
|
@ -1,8 +1,36 @@
|
|||
# Changelog
|
||||
|
||||
## [3.6.0-nightly.1](https://github.com/pypeclub/OpenPype/tree/HEAD)
|
||||
|
||||
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.5.0...HEAD)
|
||||
|
||||
**🚀 Enhancements**
|
||||
|
||||
- Tools: Experimental tools [\#2167](https://github.com/pypeclub/OpenPype/pull/2167)
|
||||
- Loader: Refactor and use OpenPype stylesheets [\#2166](https://github.com/pypeclub/OpenPype/pull/2166)
|
||||
- Add loader for linked smart objects in photoshop [\#2149](https://github.com/pypeclub/OpenPype/pull/2149)
|
||||
- Burnins: DNxHD profiles handling [\#2142](https://github.com/pypeclub/OpenPype/pull/2142)
|
||||
- Tools: Single access point for host tools [\#2139](https://github.com/pypeclub/OpenPype/pull/2139)
|
||||
|
||||
**🐛 Bug fixes**
|
||||
|
||||
- MacOS: Launching of applications may cause Permissions error [\#2175](https://github.com/pypeclub/OpenPype/pull/2175)
|
||||
- Blender: Fix 'Deselect All' with object not in 'Object Mode' [\#2163](https://github.com/pypeclub/OpenPype/pull/2163)
|
||||
- Tools: Stylesheets are applied after tool show [\#2161](https://github.com/pypeclub/OpenPype/pull/2161)
|
||||
- Maya: Collect render - fix UNC path support 🐛 [\#2158](https://github.com/pypeclub/OpenPype/pull/2158)
|
||||
- Maya: Fix hotbox broken by scriptsmenu [\#2151](https://github.com/pypeclub/OpenPype/pull/2151)
|
||||
- Ftrack: Ignore save warnings exception in Prepare project action [\#2150](https://github.com/pypeclub/OpenPype/pull/2150)
|
||||
- Loader thumbnails with smooth edges [\#2147](https://github.com/pypeclub/OpenPype/pull/2147)
|
||||
- Added validator for source files for Standalone Publisher [\#2138](https://github.com/pypeclub/OpenPype/pull/2138)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Bump pillow from 8.2.0 to 8.3.2 [\#2162](https://github.com/pypeclub/OpenPype/pull/2162)
|
||||
- Bump axios from 0.21.1 to 0.21.4 in /website [\#2059](https://github.com/pypeclub/OpenPype/pull/2059)
|
||||
|
||||
## [3.5.0](https://github.com/pypeclub/OpenPype/tree/3.5.0) (2021-10-17)
|
||||
|
||||
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.4.1...3.5.0)
|
||||
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/CI/3.5.0-nightly.8...3.5.0)
|
||||
|
||||
**Deprecated:**
|
||||
|
||||
|
|
@ -66,10 +94,6 @@
|
|||
|
||||
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/CI/3.4.1-nightly.1...3.4.1)
|
||||
|
||||
**🆕 New features**
|
||||
|
||||
- Settings: Flag project as deactivated and hide from tools' view [\#2008](https://github.com/pypeclub/OpenPype/pull/2008)
|
||||
|
||||
**🚀 Enhancements**
|
||||
|
||||
- General: Startup validations [\#2054](https://github.com/pypeclub/OpenPype/pull/2054)
|
||||
|
|
@ -82,8 +106,6 @@
|
|||
- Loader & Library loader: Use tools from OpenPype [\#2038](https://github.com/pypeclub/OpenPype/pull/2038)
|
||||
- Adding predefined project folders creation in PM [\#2030](https://github.com/pypeclub/OpenPype/pull/2030)
|
||||
- WebserverModule: Removed interface of webserver module [\#2028](https://github.com/pypeclub/OpenPype/pull/2028)
|
||||
- TimersManager: Removed interface of timers manager [\#2024](https://github.com/pypeclub/OpenPype/pull/2024)
|
||||
- Feature Maya import asset from scene inventory [\#2018](https://github.com/pypeclub/OpenPype/pull/2018)
|
||||
|
||||
**🐛 Bug fixes**
|
||||
|
||||
|
|
@ -101,21 +123,10 @@
|
|||
|
||||
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/CI/3.4.0-nightly.6...3.4.0)
|
||||
|
||||
**🆕 New features**
|
||||
|
||||
- Nuke: Compatibility with Nuke 13 [\#2003](https://github.com/pypeclub/OpenPype/pull/2003)
|
||||
|
||||
**🚀 Enhancements**
|
||||
|
||||
- Added possibility to configure of synchronization of workfile version… [\#2041](https://github.com/pypeclub/OpenPype/pull/2041)
|
||||
- General: Task types in profiles [\#2036](https://github.com/pypeclub/OpenPype/pull/2036)
|
||||
- Console interpreter: Handle invalid sizes on initialization [\#2022](https://github.com/pypeclub/OpenPype/pull/2022)
|
||||
- Ftrack: Show OpenPype versions in event server status [\#2019](https://github.com/pypeclub/OpenPype/pull/2019)
|
||||
- General: Staging icon [\#2017](https://github.com/pypeclub/OpenPype/pull/2017)
|
||||
- Ftrack: Sync to avalon actions have jobs [\#2015](https://github.com/pypeclub/OpenPype/pull/2015)
|
||||
- Modules: Connect method is not required [\#2009](https://github.com/pypeclub/OpenPype/pull/2009)
|
||||
- Settings UI: Number with configurable steps [\#2001](https://github.com/pypeclub/OpenPype/pull/2001)
|
||||
- Moving project folder structure creation out of ftrack module \#1989 [\#1996](https://github.com/pypeclub/OpenPype/pull/1996)
|
||||
|
||||
**🐛 Bug fixes**
|
||||
|
||||
|
|
@ -124,12 +135,6 @@
|
|||
- Nuke: typo on a button [\#2034](https://github.com/pypeclub/OpenPype/pull/2034)
|
||||
- Hiero: Fix "none" named tags [\#2033](https://github.com/pypeclub/OpenPype/pull/2033)
|
||||
- FFmpeg: Subprocess arguments as list [\#2032](https://github.com/pypeclub/OpenPype/pull/2032)
|
||||
- General: Fix Python 2 breaking line [\#2016](https://github.com/pypeclub/OpenPype/pull/2016)
|
||||
- Bugfix/webpublisher task type [\#2006](https://github.com/pypeclub/OpenPype/pull/2006)
|
||||
|
||||
### 📖 Documentation
|
||||
|
||||
- Documentation: Ftrack launch argsuments update [\#2014](https://github.com/pypeclub/OpenPype/pull/2014)
|
||||
|
||||
## [3.3.1](https://github.com/pypeclub/OpenPype/tree/3.3.1) (2021-08-20)
|
||||
|
||||
|
|
|
|||
|
|
@ -158,6 +158,25 @@ def publish(debug, paths, targets):
|
|||
PypeCommands.publish(list(paths), targets)
|
||||
|
||||
|
||||
@main.command()
|
||||
@click.argument("path")
|
||||
@click.option("-d", "--debug", is_flag=True, help="Print debug messages")
|
||||
@click.option("-h", "--host", help="Host")
|
||||
@click.option("-u", "--user", help="User email address")
|
||||
@click.option("-p", "--project", help="Project")
|
||||
@click.option("-t", "--targets", help="Targets", default=None,
|
||||
multiple=True)
|
||||
def remotepublishfromapp(debug, project, path, host, targets=None, user=None):
|
||||
"""Start CLI publishing.
|
||||
|
||||
Publish collects json from paths provided as an argument.
|
||||
More than one path is allowed.
|
||||
"""
|
||||
if debug:
|
||||
os.environ['OPENPYPE_DEBUG'] = '3'
|
||||
PypeCommands.remotepublishfromapp(project, path, host, user,
|
||||
targets=targets)
|
||||
|
||||
@main.command()
|
||||
@click.argument("path")
|
||||
@click.option("-d", "--debug", is_flag=True, help="Print debug messages")
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ class LaunchFoundryAppsWindows(PreLaunchHook):
|
|||
|
||||
# Should be as last hook because must change launch arguments to string
|
||||
order = 1000
|
||||
app_groups = ["nuke", "nukex", "hiero", "nukestudio"]
|
||||
app_groups = ["nuke", "nukex", "hiero", "nukestudio", "photoshop"]
|
||||
platforms = ["windows"]
|
||||
|
||||
def execute(self):
|
||||
|
|
|
|||
|
|
@ -95,6 +95,30 @@ def get_local_collection_with_name(name):
|
|||
return None
|
||||
|
||||
|
||||
def deselect_all():
|
||||
"""Deselect all objects in the scene.
|
||||
|
||||
Blender gives context error if trying to deselect object that it isn't
|
||||
in object mode.
|
||||
"""
|
||||
modes = []
|
||||
active = bpy.context.view_layer.objects.active
|
||||
|
||||
for obj in bpy.data.objects:
|
||||
if obj.mode != 'OBJECT':
|
||||
modes.append((obj, obj.mode))
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
|
||||
for p in modes:
|
||||
bpy.context.view_layer.objects.active = p[0]
|
||||
bpy.ops.object.mode_set(mode=p[1])
|
||||
|
||||
bpy.context.view_layer.objects.active = active
|
||||
|
||||
|
||||
class Creator(PypeCreatorMixin, blender.Creator):
|
||||
pass
|
||||
|
||||
|
|
|
|||
|
|
@ -3,11 +3,12 @@
|
|||
import bpy
|
||||
|
||||
from avalon import api
|
||||
from avalon.blender import lib
|
||||
import openpype.hosts.blender.api.plugin
|
||||
from avalon.blender import lib, ops
|
||||
from avalon.blender.pipeline import AVALON_INSTANCES
|
||||
from openpype.hosts.blender.api import plugin
|
||||
|
||||
|
||||
class CreateCamera(openpype.hosts.blender.api.plugin.Creator):
|
||||
class CreateCamera(plugin.Creator):
|
||||
"""Polygonal static geometry"""
|
||||
|
||||
name = "cameraMain"
|
||||
|
|
@ -16,17 +17,46 @@ class CreateCamera(openpype.hosts.blender.api.plugin.Creator):
|
|||
icon = "video-camera"
|
||||
|
||||
def process(self):
|
||||
""" Run the creator on Blender main thread"""
|
||||
mti = ops.MainThreadItem(self._process)
|
||||
ops.execute_in_main_thread(mti)
|
||||
|
||||
def _process(self):
|
||||
# Get Instance Containter or create it if it does not exist
|
||||
instances = bpy.data.collections.get(AVALON_INSTANCES)
|
||||
if not instances:
|
||||
instances = bpy.data.collections.new(name=AVALON_INSTANCES)
|
||||
bpy.context.scene.collection.children.link(instances)
|
||||
|
||||
# Create instance object
|
||||
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)
|
||||
name = plugin.asset_name(asset, subset)
|
||||
|
||||
camera = bpy.data.cameras.new(subset)
|
||||
camera_obj = bpy.data.objects.new(subset, camera)
|
||||
|
||||
instances.objects.link(camera_obj)
|
||||
|
||||
asset_group = bpy.data.objects.new(name=name, object_data=None)
|
||||
asset_group.empty_display_type = 'SINGLE_ARROW'
|
||||
instances.objects.link(asset_group)
|
||||
self.data['task'] = api.Session.get('AVALON_TASK')
|
||||
lib.imprint(collection, self.data)
|
||||
print(f"self.data: {self.data}")
|
||||
lib.imprint(asset_group, self.data)
|
||||
|
||||
if (self.options or {}).get("useSelection"):
|
||||
for obj in lib.get_selection():
|
||||
collection.objects.link(obj)
|
||||
bpy.context.view_layer.objects.active = asset_group
|
||||
selected = lib.get_selection()
|
||||
for obj in selected:
|
||||
obj.select_set(True)
|
||||
selected.append(asset_group)
|
||||
bpy.ops.object.parent_set(keep_transform=True)
|
||||
else:
|
||||
plugin.deselect_all()
|
||||
camera_obj.select_set(True)
|
||||
asset_group.select_set(True)
|
||||
bpy.context.view_layer.objects.active = asset_group
|
||||
bpy.ops.object.parent_set(keep_transform=True)
|
||||
|
||||
return collection
|
||||
return asset_group
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ class CacheModelLoader(plugin.AssetLoader):
|
|||
bpy.data.objects.remove(empty)
|
||||
|
||||
def _process(self, libpath, asset_group, group_name):
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
plugin.deselect_all()
|
||||
|
||||
collection = bpy.context.view_layer.active_layer_collection.collection
|
||||
|
||||
|
|
@ -109,7 +109,7 @@ class CacheModelLoader(plugin.AssetLoader):
|
|||
avalon_info = obj[AVALON_PROPERTY]
|
||||
avalon_info.update({"container_name": group_name})
|
||||
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
plugin.deselect_all()
|
||||
|
||||
return objects
|
||||
|
||||
|
|
|
|||
|
|
@ -1,247 +0,0 @@
|
|||
"""Load a camera asset in Blender."""
|
||||
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from pprint import pformat
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
from avalon import api, blender
|
||||
import bpy
|
||||
import openpype.hosts.blender.api.plugin
|
||||
|
||||
logger = logging.getLogger("openpype").getChild("blender").getChild("load_camera")
|
||||
|
||||
|
||||
class BlendCameraLoader(openpype.hosts.blender.api.plugin.AssetLoader):
|
||||
"""Load a camera from a .blend file.
|
||||
|
||||
Warning:
|
||||
Loading the same asset more then once is not properly supported at the
|
||||
moment.
|
||||
"""
|
||||
|
||||
families = ["camera"]
|
||||
representations = ["blend"]
|
||||
|
||||
label = "Link Camera"
|
||||
icon = "code-fork"
|
||||
color = "orange"
|
||||
|
||||
def _remove(self, objects, lib_container):
|
||||
for obj in list(objects):
|
||||
bpy.data.cameras.remove(obj.data)
|
||||
|
||||
bpy.data.collections.remove(bpy.data.collections[lib_container])
|
||||
|
||||
def _process(self, libpath, lib_container, container_name, actions):
|
||||
|
||||
relative = bpy.context.preferences.filepaths.use_relative_paths
|
||||
with bpy.data.libraries.load(
|
||||
libpath, link=True, relative=relative
|
||||
) as (_, data_to):
|
||||
data_to.collections = [lib_container]
|
||||
|
||||
scene = bpy.context.scene
|
||||
|
||||
scene.collection.children.link(bpy.data.collections[lib_container])
|
||||
|
||||
camera_container = scene.collection.children[lib_container].make_local()
|
||||
|
||||
objects_list = []
|
||||
|
||||
for obj in camera_container.objects:
|
||||
local_obj = obj.make_local()
|
||||
local_obj.data.make_local()
|
||||
|
||||
if not local_obj.get(blender.pipeline.AVALON_PROPERTY):
|
||||
local_obj[blender.pipeline.AVALON_PROPERTY] = dict()
|
||||
|
||||
avalon_info = local_obj[blender.pipeline.AVALON_PROPERTY]
|
||||
avalon_info.update({"container_name": container_name})
|
||||
|
||||
if actions[0] is not None:
|
||||
if local_obj.animation_data is None:
|
||||
local_obj.animation_data_create()
|
||||
local_obj.animation_data.action = actions[0]
|
||||
|
||||
if actions[1] is not None:
|
||||
if local_obj.data.animation_data is None:
|
||||
local_obj.data.animation_data_create()
|
||||
local_obj.data.animation_data.action = actions[1]
|
||||
|
||||
objects_list.append(local_obj)
|
||||
|
||||
camera_container.pop(blender.pipeline.AVALON_PROPERTY)
|
||||
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
|
||||
return objects_list
|
||||
|
||||
def process_asset(
|
||||
self, context: dict, name: str, namespace: Optional[str] = None,
|
||||
options: Optional[Dict] = None
|
||||
) -> Optional[List]:
|
||||
"""
|
||||
Arguments:
|
||||
name: Use pre-defined name
|
||||
namespace: Use pre-defined namespace
|
||||
context: Full parenthood of representation to load
|
||||
options: Additional settings dictionary
|
||||
"""
|
||||
|
||||
libpath = self.fname
|
||||
asset = context["asset"]["name"]
|
||||
subset = context["subset"]["name"]
|
||||
lib_container = openpype.hosts.blender.api.plugin.asset_name(asset, subset)
|
||||
container_name = openpype.hosts.blender.api.plugin.asset_name(
|
||||
asset, subset, namespace
|
||||
)
|
||||
|
||||
container = bpy.data.collections.new(lib_container)
|
||||
container.name = container_name
|
||||
blender.pipeline.containerise_existing(
|
||||
container,
|
||||
name,
|
||||
namespace,
|
||||
context,
|
||||
self.__class__.__name__,
|
||||
)
|
||||
|
||||
container_metadata = container.get(
|
||||
blender.pipeline.AVALON_PROPERTY)
|
||||
|
||||
container_metadata["libpath"] = libpath
|
||||
container_metadata["lib_container"] = lib_container
|
||||
|
||||
objects_list = self._process(
|
||||
libpath, lib_container, container_name, (None, None))
|
||||
|
||||
# Save the list of objects in the metadata container
|
||||
container_metadata["objects"] = objects_list
|
||||
|
||||
nodes = list(container.objects)
|
||||
nodes.append(container)
|
||||
self[:] = nodes
|
||||
return nodes
|
||||
|
||||
def update(self, container: Dict, representation: Dict):
|
||||
"""Update the loaded asset.
|
||||
|
||||
This will remove all objects of the current collection, load the new
|
||||
ones and add them to the collection.
|
||||
If the objects of the collection are used in another collection they
|
||||
will not be removed, only unlinked. Normally this should not be the
|
||||
case though.
|
||||
|
||||
Warning:
|
||||
No nested collections are supported at the moment!
|
||||
"""
|
||||
|
||||
collection = bpy.data.collections.get(
|
||||
container["objectName"]
|
||||
)
|
||||
|
||||
libpath = Path(api.get_representation_path(representation))
|
||||
extension = libpath.suffix.lower()
|
||||
|
||||
logger.info(
|
||||
"Container: %s\nRepresentation: %s",
|
||||
pformat(container, indent=2),
|
||||
pformat(representation, indent=2),
|
||||
)
|
||||
|
||||
assert collection, (
|
||||
f"The asset is not loaded: {container['objectName']}"
|
||||
)
|
||||
assert not (collection.children), (
|
||||
"Nested collections are not supported."
|
||||
)
|
||||
assert libpath, (
|
||||
"No existing library file found for {container['objectName']}"
|
||||
)
|
||||
assert libpath.is_file(), (
|
||||
f"The file doesn't exist: {libpath}"
|
||||
)
|
||||
assert extension in openpype.hosts.blender.api.plugin.VALID_EXTENSIONS, (
|
||||
f"Unsupported file: {libpath}"
|
||||
)
|
||||
|
||||
collection_metadata = collection.get(
|
||||
blender.pipeline.AVALON_PROPERTY)
|
||||
collection_libpath = collection_metadata["libpath"]
|
||||
objects = collection_metadata["objects"]
|
||||
lib_container = collection_metadata["lib_container"]
|
||||
|
||||
normalized_collection_libpath = (
|
||||
str(Path(bpy.path.abspath(collection_libpath)).resolve())
|
||||
)
|
||||
normalized_libpath = (
|
||||
str(Path(bpy.path.abspath(str(libpath))).resolve())
|
||||
)
|
||||
logger.debug(
|
||||
"normalized_collection_libpath:\n %s\nnormalized_libpath:\n %s",
|
||||
normalized_collection_libpath,
|
||||
normalized_libpath,
|
||||
)
|
||||
if normalized_collection_libpath == normalized_libpath:
|
||||
logger.info("Library already loaded, not updating...")
|
||||
return
|
||||
|
||||
camera = objects[0]
|
||||
|
||||
camera_action = None
|
||||
camera_data_action = None
|
||||
|
||||
if camera.animation_data and camera.animation_data.action:
|
||||
camera_action = camera.animation_data.action
|
||||
|
||||
if camera.data.animation_data and camera.data.animation_data.action:
|
||||
camera_data_action = camera.data.animation_data.action
|
||||
|
||||
actions = (camera_action, camera_data_action)
|
||||
|
||||
self._remove(objects, lib_container)
|
||||
|
||||
objects_list = self._process(
|
||||
str(libpath), lib_container, collection.name, actions)
|
||||
|
||||
# Save the list of objects in the metadata container
|
||||
collection_metadata["objects"] = objects_list
|
||||
collection_metadata["libpath"] = str(libpath)
|
||||
collection_metadata["representation"] = str(representation["_id"])
|
||||
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
|
||||
def remove(self, container: Dict) -> bool:
|
||||
"""Remove an existing container from a Blender scene.
|
||||
|
||||
Arguments:
|
||||
container (openpype:container-1.0): Container to remove,
|
||||
from `host.ls()`.
|
||||
|
||||
Returns:
|
||||
bool: Whether the container was deleted.
|
||||
|
||||
Warning:
|
||||
No nested collections are supported at the moment!
|
||||
"""
|
||||
|
||||
collection = bpy.data.collections.get(
|
||||
container["objectName"]
|
||||
)
|
||||
if not collection:
|
||||
return False
|
||||
assert not (collection.children), (
|
||||
"Nested collections are not supported."
|
||||
)
|
||||
|
||||
collection_metadata = collection.get(
|
||||
blender.pipeline.AVALON_PROPERTY)
|
||||
objects = collection_metadata["objects"]
|
||||
lib_container = collection_metadata["lib_container"]
|
||||
|
||||
self._remove(objects, lib_container)
|
||||
|
||||
bpy.data.collections.remove(collection)
|
||||
|
||||
return True
|
||||
252
openpype/hosts/blender/plugins/load/load_camera_blend.py
Normal file
252
openpype/hosts/blender/plugins/load/load_camera_blend.py
Normal file
|
|
@ -0,0 +1,252 @@
|
|||
"""Load a camera asset in Blender."""
|
||||
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from pprint import pformat
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
import bpy
|
||||
|
||||
from avalon import api
|
||||
from avalon.blender.pipeline import AVALON_CONTAINERS
|
||||
from avalon.blender.pipeline import AVALON_CONTAINER_ID
|
||||
from avalon.blender.pipeline import AVALON_PROPERTY
|
||||
from openpype.hosts.blender.api import plugin
|
||||
|
||||
logger = logging.getLogger("openpype").getChild(
|
||||
"blender").getChild("load_camera")
|
||||
|
||||
|
||||
class BlendCameraLoader(plugin.AssetLoader):
|
||||
"""Load a camera from a .blend file.
|
||||
|
||||
Warning:
|
||||
Loading the same asset more then once is not properly supported at the
|
||||
moment.
|
||||
"""
|
||||
|
||||
families = ["camera"]
|
||||
representations = ["blend"]
|
||||
|
||||
label = "Link Camera (Blend)"
|
||||
icon = "code-fork"
|
||||
color = "orange"
|
||||
|
||||
def _remove(self, asset_group):
|
||||
objects = list(asset_group.children)
|
||||
|
||||
for obj in objects:
|
||||
if obj.type == 'CAMERA':
|
||||
bpy.data.cameras.remove(obj.data)
|
||||
|
||||
def _process(self, libpath, asset_group, group_name):
|
||||
with bpy.data.libraries.load(
|
||||
libpath, link=True, relative=False
|
||||
) as (data_from, data_to):
|
||||
data_to.objects = data_from.objects
|
||||
|
||||
parent = bpy.context.scene.collection
|
||||
|
||||
empties = [obj for obj in data_to.objects if obj.type == 'EMPTY']
|
||||
|
||||
container = None
|
||||
|
||||
for empty in empties:
|
||||
if empty.get(AVALON_PROPERTY):
|
||||
container = empty
|
||||
break
|
||||
|
||||
assert container, "No asset group found"
|
||||
|
||||
# Children must be linked before parents,
|
||||
# otherwise the hierarchy will break
|
||||
objects = []
|
||||
nodes = list(container.children)
|
||||
|
||||
for obj in nodes:
|
||||
obj.parent = asset_group
|
||||
|
||||
for obj in nodes:
|
||||
objects.append(obj)
|
||||
nodes.extend(list(obj.children))
|
||||
|
||||
objects.reverse()
|
||||
|
||||
for obj in objects:
|
||||
parent.objects.link(obj)
|
||||
|
||||
for obj in objects:
|
||||
local_obj = plugin.prepare_data(obj, group_name)
|
||||
|
||||
if local_obj.type != 'EMPTY':
|
||||
plugin.prepare_data(local_obj.data, group_name)
|
||||
|
||||
if not local_obj.get(AVALON_PROPERTY):
|
||||
local_obj[AVALON_PROPERTY] = dict()
|
||||
|
||||
avalon_info = local_obj[AVALON_PROPERTY]
|
||||
avalon_info.update({"container_name": group_name})
|
||||
|
||||
objects.reverse()
|
||||
|
||||
bpy.data.orphans_purge(do_local_ids=False)
|
||||
|
||||
plugin.deselect_all()
|
||||
|
||||
return objects
|
||||
|
||||
def process_asset(
|
||||
self, context: dict, name: str, namespace: Optional[str] = None,
|
||||
options: Optional[Dict] = None
|
||||
) -> Optional[List]:
|
||||
"""
|
||||
Arguments:
|
||||
name: Use pre-defined name
|
||||
namespace: Use pre-defined namespace
|
||||
context: Full parenthood of representation to load
|
||||
options: Additional settings dictionary
|
||||
"""
|
||||
libpath = self.fname
|
||||
asset = context["asset"]["name"]
|
||||
subset = context["subset"]["name"]
|
||||
|
||||
asset_name = plugin.asset_name(asset, subset)
|
||||
unique_number = plugin.get_unique_number(asset, subset)
|
||||
group_name = plugin.asset_name(asset, subset, unique_number)
|
||||
namespace = namespace or f"{asset}_{unique_number}"
|
||||
|
||||
avalon_container = bpy.data.collections.get(AVALON_CONTAINERS)
|
||||
if not avalon_container:
|
||||
avalon_container = bpy.data.collections.new(name=AVALON_CONTAINERS)
|
||||
bpy.context.scene.collection.children.link(avalon_container)
|
||||
|
||||
asset_group = bpy.data.objects.new(group_name, object_data=None)
|
||||
asset_group.empty_display_type = 'SINGLE_ARROW'
|
||||
avalon_container.objects.link(asset_group)
|
||||
|
||||
objects = self._process(libpath, asset_group, group_name)
|
||||
|
||||
bpy.context.scene.collection.objects.link(asset_group)
|
||||
|
||||
asset_group[AVALON_PROPERTY] = {
|
||||
"schema": "openpype:container-2.0",
|
||||
"id": AVALON_CONTAINER_ID,
|
||||
"name": name,
|
||||
"namespace": namespace or '',
|
||||
"loader": str(self.__class__.__name__),
|
||||
"representation": str(context["representation"]["_id"]),
|
||||
"libpath": libpath,
|
||||
"asset_name": asset_name,
|
||||
"parent": str(context["representation"]["parent"]),
|
||||
"family": context["representation"]["context"]["family"],
|
||||
"objectName": group_name
|
||||
}
|
||||
|
||||
self[:] = objects
|
||||
return objects
|
||||
|
||||
def exec_update(self, container: Dict, representation: Dict):
|
||||
"""Update the loaded asset.
|
||||
|
||||
This will remove all children of the asset group, load the new ones
|
||||
and add them as children of the group.
|
||||
"""
|
||||
object_name = container["objectName"]
|
||||
asset_group = bpy.data.objects.get(object_name)
|
||||
libpath = Path(api.get_representation_path(representation))
|
||||
extension = libpath.suffix.lower()
|
||||
|
||||
self.log.info(
|
||||
"Container: %s\nRepresentation: %s",
|
||||
pformat(container, indent=2),
|
||||
pformat(representation, indent=2),
|
||||
)
|
||||
|
||||
assert asset_group, (
|
||||
f"The asset is not loaded: {container['objectName']}"
|
||||
)
|
||||
assert libpath, (
|
||||
"No existing library file found for {container['objectName']}"
|
||||
)
|
||||
assert libpath.is_file(), (
|
||||
f"The file doesn't exist: {libpath}"
|
||||
)
|
||||
assert extension in plugin.VALID_EXTENSIONS, (
|
||||
f"Unsupported file: {libpath}"
|
||||
)
|
||||
|
||||
metadata = asset_group.get(AVALON_PROPERTY)
|
||||
group_libpath = metadata["libpath"]
|
||||
|
||||
normalized_group_libpath = (
|
||||
str(Path(bpy.path.abspath(group_libpath)).resolve())
|
||||
)
|
||||
normalized_libpath = (
|
||||
str(Path(bpy.path.abspath(str(libpath))).resolve())
|
||||
)
|
||||
self.log.debug(
|
||||
"normalized_group_libpath:\n %s\nnormalized_libpath:\n %s",
|
||||
normalized_group_libpath,
|
||||
normalized_libpath,
|
||||
)
|
||||
if normalized_group_libpath == normalized_libpath:
|
||||
self.log.info("Library already loaded, not updating...")
|
||||
return
|
||||
|
||||
# Check how many assets use the same library
|
||||
count = 0
|
||||
for obj in bpy.data.collections.get(AVALON_CONTAINERS).objects:
|
||||
if obj.get(AVALON_PROPERTY).get('libpath') == group_libpath:
|
||||
count += 1
|
||||
|
||||
mat = asset_group.matrix_basis.copy()
|
||||
|
||||
self._remove(asset_group)
|
||||
|
||||
# If it is the last object to use that library, remove it
|
||||
if count == 1:
|
||||
library = bpy.data.libraries.get(bpy.path.basename(group_libpath))
|
||||
if library:
|
||||
bpy.data.libraries.remove(library)
|
||||
|
||||
self._process(str(libpath), asset_group, object_name)
|
||||
|
||||
asset_group.matrix_basis = mat
|
||||
|
||||
metadata["libpath"] = str(libpath)
|
||||
metadata["representation"] = str(representation["_id"])
|
||||
metadata["parent"] = str(representation["parent"])
|
||||
|
||||
def exec_remove(self, container: Dict) -> bool:
|
||||
"""Remove an existing container from a Blender scene.
|
||||
|
||||
Arguments:
|
||||
container (openpype:container-1.0): Container to remove,
|
||||
from `host.ls()`.
|
||||
|
||||
Returns:
|
||||
bool: Whether the container was deleted.
|
||||
"""
|
||||
object_name = container["objectName"]
|
||||
asset_group = bpy.data.objects.get(object_name)
|
||||
libpath = asset_group.get(AVALON_PROPERTY).get('libpath')
|
||||
|
||||
# Check how many assets use the same library
|
||||
count = 0
|
||||
for obj in bpy.data.collections.get(AVALON_CONTAINERS).objects:
|
||||
if obj.get(AVALON_PROPERTY).get('libpath') == libpath:
|
||||
count += 1
|
||||
|
||||
if not asset_group:
|
||||
return False
|
||||
|
||||
self._remove(asset_group)
|
||||
|
||||
bpy.data.objects.remove(asset_group)
|
||||
|
||||
# If it is the last object to use that library, remove it
|
||||
if count == 1:
|
||||
library = bpy.data.libraries.get(bpy.path.basename(libpath))
|
||||
bpy.data.libraries.remove(library)
|
||||
|
||||
return True
|
||||
218
openpype/hosts/blender/plugins/load/load_camera_fbx.py
Normal file
218
openpype/hosts/blender/plugins/load/load_camera_fbx.py
Normal file
|
|
@ -0,0 +1,218 @@
|
|||
"""Load an asset in Blender from an Alembic file."""
|
||||
|
||||
from pathlib import Path
|
||||
from pprint import pformat
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
import bpy
|
||||
|
||||
from avalon import api
|
||||
from avalon.blender import lib
|
||||
from avalon.blender.pipeline import AVALON_CONTAINERS
|
||||
from avalon.blender.pipeline import AVALON_CONTAINER_ID
|
||||
from avalon.blender.pipeline import AVALON_PROPERTY
|
||||
from openpype.hosts.blender.api import plugin
|
||||
|
||||
|
||||
class FbxCameraLoader(plugin.AssetLoader):
|
||||
"""Load a camera from FBX.
|
||||
|
||||
Stores the imported asset in an empty named after the asset.
|
||||
"""
|
||||
|
||||
families = ["camera"]
|
||||
representations = ["fbx"]
|
||||
|
||||
label = "Load Camera (FBX)"
|
||||
icon = "code-fork"
|
||||
color = "orange"
|
||||
|
||||
def _remove(self, asset_group):
|
||||
objects = list(asset_group.children)
|
||||
|
||||
for obj in objects:
|
||||
if obj.type == 'CAMERA':
|
||||
bpy.data.cameras.remove(obj.data)
|
||||
elif obj.type == 'EMPTY':
|
||||
objects.extend(obj.children)
|
||||
bpy.data.objects.remove(obj)
|
||||
|
||||
def _process(self, libpath, asset_group, group_name):
|
||||
plugin.deselect_all()
|
||||
|
||||
collection = bpy.context.view_layer.active_layer_collection.collection
|
||||
|
||||
bpy.ops.import_scene.fbx(filepath=libpath)
|
||||
|
||||
parent = bpy.context.scene.collection
|
||||
|
||||
objects = lib.get_selection()
|
||||
|
||||
for obj in objects:
|
||||
obj.parent = asset_group
|
||||
|
||||
for obj in objects:
|
||||
parent.objects.link(obj)
|
||||
collection.objects.unlink(obj)
|
||||
|
||||
for obj in objects:
|
||||
name = obj.name
|
||||
obj.name = f"{group_name}:{name}"
|
||||
if obj.type != 'EMPTY':
|
||||
name_data = obj.data.name
|
||||
obj.data.name = f"{group_name}:{name_data}"
|
||||
|
||||
if not obj.get(AVALON_PROPERTY):
|
||||
obj[AVALON_PROPERTY] = dict()
|
||||
|
||||
avalon_info = obj[AVALON_PROPERTY]
|
||||
avalon_info.update({"container_name": group_name})
|
||||
|
||||
plugin.deselect_all()
|
||||
|
||||
return objects
|
||||
|
||||
def process_asset(
|
||||
self, context: dict, name: str, namespace: Optional[str] = None,
|
||||
options: Optional[Dict] = None
|
||||
) -> Optional[List]:
|
||||
"""
|
||||
Arguments:
|
||||
name: Use pre-defined name
|
||||
namespace: Use pre-defined namespace
|
||||
context: Full parenthood of representation to load
|
||||
options: Additional settings dictionary
|
||||
"""
|
||||
libpath = self.fname
|
||||
asset = context["asset"]["name"]
|
||||
subset = context["subset"]["name"]
|
||||
|
||||
asset_name = plugin.asset_name(asset, subset)
|
||||
unique_number = plugin.get_unique_number(asset, subset)
|
||||
group_name = plugin.asset_name(asset, subset, unique_number)
|
||||
namespace = namespace or f"{asset}_{unique_number}"
|
||||
|
||||
avalon_container = bpy.data.collections.get(AVALON_CONTAINERS)
|
||||
if not avalon_container:
|
||||
avalon_container = bpy.data.collections.new(name=AVALON_CONTAINERS)
|
||||
bpy.context.scene.collection.children.link(avalon_container)
|
||||
|
||||
asset_group = bpy.data.objects.new(group_name, object_data=None)
|
||||
avalon_container.objects.link(asset_group)
|
||||
|
||||
objects = self._process(libpath, asset_group, group_name)
|
||||
|
||||
objects = []
|
||||
nodes = list(asset_group.children)
|
||||
|
||||
for obj in nodes:
|
||||
objects.append(obj)
|
||||
nodes.extend(list(obj.children))
|
||||
|
||||
bpy.context.scene.collection.objects.link(asset_group)
|
||||
|
||||
asset_group[AVALON_PROPERTY] = {
|
||||
"schema": "openpype:container-2.0",
|
||||
"id": AVALON_CONTAINER_ID,
|
||||
"name": name,
|
||||
"namespace": namespace or '',
|
||||
"loader": str(self.__class__.__name__),
|
||||
"representation": str(context["representation"]["_id"]),
|
||||
"libpath": libpath,
|
||||
"asset_name": asset_name,
|
||||
"parent": str(context["representation"]["parent"]),
|
||||
"family": context["representation"]["context"]["family"],
|
||||
"objectName": group_name
|
||||
}
|
||||
|
||||
self[:] = objects
|
||||
return objects
|
||||
|
||||
def exec_update(self, container: Dict, representation: Dict):
|
||||
"""Update the loaded asset.
|
||||
|
||||
This will remove all objects of the current collection, load the new
|
||||
ones and add them to the collection.
|
||||
If the objects of the collection are used in another collection they
|
||||
will not be removed, only unlinked. Normally this should not be the
|
||||
case though.
|
||||
|
||||
Warning:
|
||||
No nested collections are supported at the moment!
|
||||
"""
|
||||
object_name = container["objectName"]
|
||||
asset_group = bpy.data.objects.get(object_name)
|
||||
libpath = Path(api.get_representation_path(representation))
|
||||
extension = libpath.suffix.lower()
|
||||
|
||||
self.log.info(
|
||||
"Container: %s\nRepresentation: %s",
|
||||
pformat(container, indent=2),
|
||||
pformat(representation, indent=2),
|
||||
)
|
||||
|
||||
assert asset_group, (
|
||||
f"The asset is not loaded: {container['objectName']}"
|
||||
)
|
||||
assert libpath, (
|
||||
"No existing library file found for {container['objectName']}"
|
||||
)
|
||||
assert libpath.is_file(), (
|
||||
f"The file doesn't exist: {libpath}"
|
||||
)
|
||||
assert extension in plugin.VALID_EXTENSIONS, (
|
||||
f"Unsupported file: {libpath}"
|
||||
)
|
||||
|
||||
metadata = asset_group.get(AVALON_PROPERTY)
|
||||
group_libpath = metadata["libpath"]
|
||||
|
||||
normalized_group_libpath = (
|
||||
str(Path(bpy.path.abspath(group_libpath)).resolve())
|
||||
)
|
||||
normalized_libpath = (
|
||||
str(Path(bpy.path.abspath(str(libpath))).resolve())
|
||||
)
|
||||
self.log.debug(
|
||||
"normalized_group_libpath:\n %s\nnormalized_libpath:\n %s",
|
||||
normalized_group_libpath,
|
||||
normalized_libpath,
|
||||
)
|
||||
if normalized_group_libpath == normalized_libpath:
|
||||
self.log.info("Library already loaded, not updating...")
|
||||
return
|
||||
|
||||
mat = asset_group.matrix_basis.copy()
|
||||
|
||||
self._remove(asset_group)
|
||||
self._process(str(libpath), asset_group, object_name)
|
||||
|
||||
asset_group.matrix_basis = mat
|
||||
|
||||
metadata["libpath"] = str(libpath)
|
||||
metadata["representation"] = str(representation["_id"])
|
||||
|
||||
def exec_remove(self, container: Dict) -> bool:
|
||||
"""Remove an existing container from a Blender scene.
|
||||
|
||||
Arguments:
|
||||
container (openpype:container-1.0): Container to remove,
|
||||
from `host.ls()`.
|
||||
|
||||
Returns:
|
||||
bool: Whether the container was deleted.
|
||||
|
||||
Warning:
|
||||
No nested collections are supported at the moment!
|
||||
"""
|
||||
object_name = container["objectName"]
|
||||
asset_group = bpy.data.objects.get(object_name)
|
||||
|
||||
if not asset_group:
|
||||
return False
|
||||
|
||||
self._remove(asset_group)
|
||||
|
||||
bpy.data.objects.remove(asset_group)
|
||||
|
||||
return True
|
||||
|
|
@ -46,7 +46,7 @@ class FbxModelLoader(plugin.AssetLoader):
|
|||
bpy.data.objects.remove(obj)
|
||||
|
||||
def _process(self, libpath, asset_group, group_name, action):
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
plugin.deselect_all()
|
||||
|
||||
collection = bpy.context.view_layer.active_layer_collection.collection
|
||||
|
||||
|
|
@ -112,7 +112,7 @@ class FbxModelLoader(plugin.AssetLoader):
|
|||
avalon_info = obj[AVALON_PROPERTY]
|
||||
avalon_info.update({"container_name": group_name})
|
||||
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
plugin.deselect_all()
|
||||
|
||||
return objects
|
||||
|
||||
|
|
|
|||
|
|
@ -150,7 +150,7 @@ class BlendLayoutLoader(plugin.AssetLoader):
|
|||
|
||||
bpy.data.orphans_purge(do_local_ids=False)
|
||||
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
plugin.deselect_all()
|
||||
|
||||
return objects
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ from avalon.blender.pipeline import AVALON_CONTAINERS
|
|||
from avalon.blender.pipeline import AVALON_CONTAINER_ID
|
||||
from avalon.blender.pipeline import AVALON_PROPERTY
|
||||
from avalon.blender.pipeline import AVALON_INSTANCES
|
||||
from openpype import lib
|
||||
from openpype.hosts.blender.api import plugin
|
||||
|
||||
|
||||
|
|
@ -59,7 +60,7 @@ class JsonLayoutLoader(plugin.AssetLoader):
|
|||
return None
|
||||
|
||||
def _process(self, libpath, asset, asset_group, actions):
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
plugin.deselect_all()
|
||||
|
||||
with open(libpath, "r") as fp:
|
||||
data = json.load(fp)
|
||||
|
|
@ -103,6 +104,21 @@ class JsonLayoutLoader(plugin.AssetLoader):
|
|||
options=options
|
||||
)
|
||||
|
||||
# Create the camera asset and the camera instance
|
||||
creator_plugin = lib.get_creator_by_name("CreateCamera")
|
||||
if not creator_plugin:
|
||||
raise ValueError("Creator plugin \"CreateCamera\" was "
|
||||
"not found.")
|
||||
|
||||
api.create(
|
||||
creator_plugin,
|
||||
name="camera",
|
||||
# name=f"{unique_number}_{subset}_animation",
|
||||
asset=asset,
|
||||
options={"useSelection": False}
|
||||
# data={"dependencies": str(context["representation"]["_id"])}
|
||||
)
|
||||
|
||||
def process_asset(self,
|
||||
context: dict,
|
||||
name: str,
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ class BlendModelLoader(plugin.AssetLoader):
|
|||
|
||||
bpy.data.orphans_purge(do_local_ids=False)
|
||||
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
plugin.deselect_all()
|
||||
|
||||
return objects
|
||||
|
||||
|
|
@ -126,7 +126,7 @@ class BlendModelLoader(plugin.AssetLoader):
|
|||
asset_group.empty_display_type = 'SINGLE_ARROW'
|
||||
avalon_container.objects.link(asset_group)
|
||||
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
plugin.deselect_all()
|
||||
|
||||
if options is not None:
|
||||
parent = options.get('parent')
|
||||
|
|
@ -158,7 +158,7 @@ class BlendModelLoader(plugin.AssetLoader):
|
|||
|
||||
bpy.ops.object.parent_set(keep_transform=True)
|
||||
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
plugin.deselect_all()
|
||||
|
||||
objects = self._process(libpath, asset_group, group_name)
|
||||
|
||||
|
|
|
|||
|
|
@ -156,7 +156,7 @@ class BlendRigLoader(plugin.AssetLoader):
|
|||
while bpy.data.orphans_purge(do_local_ids=False):
|
||||
pass
|
||||
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
plugin.deselect_all()
|
||||
|
||||
return objects
|
||||
|
||||
|
|
@ -191,7 +191,7 @@ class BlendRigLoader(plugin.AssetLoader):
|
|||
|
||||
action = None
|
||||
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
plugin.deselect_all()
|
||||
|
||||
create_animation = False
|
||||
|
||||
|
|
@ -227,7 +227,7 @@ class BlendRigLoader(plugin.AssetLoader):
|
|||
|
||||
bpy.ops.object.parent_set(keep_transform=True)
|
||||
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
plugin.deselect_all()
|
||||
|
||||
objects = self._process(libpath, asset_group, group_name, action)
|
||||
|
||||
|
|
@ -250,7 +250,7 @@ class BlendRigLoader(plugin.AssetLoader):
|
|||
data={"dependencies": str(context["representation"]["_id"])}
|
||||
)
|
||||
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
plugin.deselect_all()
|
||||
|
||||
bpy.context.scene.collection.objects.link(asset_group)
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ class ExtractABC(api.Extractor):
|
|||
# Perform extraction
|
||||
self.log.info("Performing extraction..")
|
||||
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
plugin.deselect_all()
|
||||
|
||||
selected = []
|
||||
asset_group = None
|
||||
|
|
@ -50,7 +50,7 @@ class ExtractABC(api.Extractor):
|
|||
flatten=False
|
||||
)
|
||||
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
plugin.deselect_all()
|
||||
|
||||
if "representations" not in instance.data:
|
||||
instance.data["representations"] = []
|
||||
|
|
|
|||
73
openpype/hosts/blender/plugins/publish/extract_camera.py
Normal file
73
openpype/hosts/blender/plugins/publish/extract_camera.py
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
import os
|
||||
|
||||
from openpype import api
|
||||
from openpype.hosts.blender.api import plugin
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
class ExtractCamera(api.Extractor):
|
||||
"""Extract as the camera as FBX."""
|
||||
|
||||
label = "Extract Camera"
|
||||
hosts = ["blender"]
|
||||
families = ["camera"]
|
||||
optional = True
|
||||
|
||||
def process(self, instance):
|
||||
# Define extract output file path
|
||||
stagingdir = self.staging_dir(instance)
|
||||
filename = f"{instance.name}.fbx"
|
||||
filepath = os.path.join(stagingdir, filename)
|
||||
|
||||
# Perform extraction
|
||||
self.log.info("Performing extraction..")
|
||||
|
||||
plugin.deselect_all()
|
||||
|
||||
selected = []
|
||||
|
||||
camera = None
|
||||
|
||||
for obj in instance:
|
||||
if obj.type == "CAMERA":
|
||||
obj.select_set(True)
|
||||
selected.append(obj)
|
||||
camera = obj
|
||||
break
|
||||
|
||||
assert camera, "No camera found"
|
||||
|
||||
context = plugin.create_blender_context(
|
||||
active=camera, selected=selected)
|
||||
|
||||
scale_length = bpy.context.scene.unit_settings.scale_length
|
||||
bpy.context.scene.unit_settings.scale_length = 0.01
|
||||
|
||||
# We export the fbx
|
||||
bpy.ops.export_scene.fbx(
|
||||
context,
|
||||
filepath=filepath,
|
||||
use_active_collection=False,
|
||||
use_selection=True,
|
||||
object_types={'CAMERA'},
|
||||
bake_anim_simplify_factor=0.0
|
||||
)
|
||||
|
||||
bpy.context.scene.unit_settings.scale_length = scale_length
|
||||
|
||||
plugin.deselect_all()
|
||||
|
||||
if "representations" not in instance.data:
|
||||
instance.data["representations"] = []
|
||||
|
||||
representation = {
|
||||
'name': 'fbx',
|
||||
'ext': 'fbx',
|
||||
'files': filename,
|
||||
"stagingDir": stagingdir,
|
||||
}
|
||||
instance.data["representations"].append(representation)
|
||||
|
||||
self.log.info("Extracted instance '%s' to: %s",
|
||||
instance.name, representation)
|
||||
|
|
@ -24,7 +24,7 @@ class ExtractFBX(api.Extractor):
|
|||
# Perform extraction
|
||||
self.log.info("Performing extraction..")
|
||||
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
plugin.deselect_all()
|
||||
|
||||
selected = []
|
||||
asset_group = None
|
||||
|
|
@ -60,7 +60,7 @@ class ExtractFBX(api.Extractor):
|
|||
add_leaf_bones=False
|
||||
)
|
||||
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
plugin.deselect_all()
|
||||
|
||||
for mat in new_materials:
|
||||
bpy.data.materials.remove(mat)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,48 @@
|
|||
from typing import List
|
||||
|
||||
import mathutils
|
||||
|
||||
import pyblish.api
|
||||
import openpype.hosts.blender.api.action
|
||||
|
||||
|
||||
class ValidateCameraZeroKeyframe(pyblish.api.InstancePlugin):
|
||||
"""Camera must have a keyframe at frame 0.
|
||||
|
||||
Unreal shifts the first keyframe to frame 0. Forcing the camera to have
|
||||
a keyframe at frame 0 will ensure that the animation will be the same
|
||||
in Unreal and Blender.
|
||||
"""
|
||||
|
||||
order = openpype.api.ValidateContentsOrder
|
||||
hosts = ["blender"]
|
||||
families = ["camera"]
|
||||
category = "geometry"
|
||||
version = (0, 1, 0)
|
||||
label = "Zero Keyframe"
|
||||
actions = [openpype.hosts.blender.api.action.SelectInvalidAction]
|
||||
|
||||
_identity = mathutils.Matrix()
|
||||
|
||||
@classmethod
|
||||
def get_invalid(cls, instance) -> List:
|
||||
invalid = []
|
||||
for obj in [obj for obj in instance]:
|
||||
if obj.type == "CAMERA":
|
||||
if obj.animation_data and obj.animation_data.action:
|
||||
action = obj.animation_data.action
|
||||
frames_set = set()
|
||||
for fcu in action.fcurves:
|
||||
for kp in fcu.keyframe_points:
|
||||
frames_set.add(kp.co[0])
|
||||
frames = list(frames_set)
|
||||
frames.sort()
|
||||
if frames[0] != 0.0:
|
||||
invalid.append(obj)
|
||||
return invalid
|
||||
|
||||
def process(self, instance):
|
||||
invalid = self.get_invalid(instance)
|
||||
if invalid:
|
||||
raise RuntimeError(
|
||||
f"Object found in instance is not in Object Mode: {invalid}")
|
||||
105
openpype/hosts/flame/__init__.py
Normal file
105
openpype/hosts/flame/__init__.py
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
from .api.utils import (
|
||||
setup
|
||||
)
|
||||
|
||||
from .api.pipeline import (
|
||||
install,
|
||||
uninstall,
|
||||
ls,
|
||||
containerise,
|
||||
update_container,
|
||||
maintained_selection,
|
||||
remove_instance,
|
||||
list_instances,
|
||||
imprint
|
||||
)
|
||||
|
||||
from .api.lib import (
|
||||
FlameAppFramework,
|
||||
maintain_current_timeline,
|
||||
get_project_manager,
|
||||
get_current_project,
|
||||
get_current_timeline,
|
||||
create_bin,
|
||||
)
|
||||
|
||||
from .api.menu import (
|
||||
FlameMenuProjectConnect,
|
||||
FlameMenuTimeline
|
||||
)
|
||||
|
||||
from .api.workio import (
|
||||
open_file,
|
||||
save_file,
|
||||
current_file,
|
||||
has_unsaved_changes,
|
||||
file_extensions,
|
||||
work_root
|
||||
)
|
||||
|
||||
import os
|
||||
|
||||
HOST_DIR = os.path.dirname(
|
||||
os.path.abspath(__file__)
|
||||
)
|
||||
API_DIR = os.path.join(HOST_DIR, "api")
|
||||
PLUGINS_DIR = os.path.join(HOST_DIR, "plugins")
|
||||
PUBLISH_PATH = os.path.join(PLUGINS_DIR, "publish")
|
||||
LOAD_PATH = os.path.join(PLUGINS_DIR, "load")
|
||||
CREATE_PATH = os.path.join(PLUGINS_DIR, "create")
|
||||
INVENTORY_PATH = os.path.join(PLUGINS_DIR, "inventory")
|
||||
|
||||
app_framework = None
|
||||
apps = []
|
||||
|
||||
|
||||
__all__ = [
|
||||
"HOST_DIR",
|
||||
"API_DIR",
|
||||
"PLUGINS_DIR",
|
||||
"PUBLISH_PATH",
|
||||
"LOAD_PATH",
|
||||
"CREATE_PATH",
|
||||
"INVENTORY_PATH",
|
||||
"INVENTORY_PATH",
|
||||
|
||||
"app_framework",
|
||||
"apps",
|
||||
|
||||
# pipeline
|
||||
"install",
|
||||
"uninstall",
|
||||
"ls",
|
||||
"containerise",
|
||||
"update_container",
|
||||
"reload_pipeline",
|
||||
"maintained_selection",
|
||||
"remove_instance",
|
||||
"list_instances",
|
||||
"imprint",
|
||||
|
||||
# utils
|
||||
"setup",
|
||||
|
||||
# lib
|
||||
"FlameAppFramework",
|
||||
"maintain_current_timeline",
|
||||
"get_project_manager",
|
||||
"get_current_project",
|
||||
"get_current_timeline",
|
||||
"create_bin",
|
||||
|
||||
# menu
|
||||
"FlameMenuProjectConnect",
|
||||
"FlameMenuTimeline",
|
||||
|
||||
# plugin
|
||||
|
||||
# workio
|
||||
"open_file",
|
||||
"save_file",
|
||||
"current_file",
|
||||
"has_unsaved_changes",
|
||||
"file_extensions",
|
||||
"work_root"
|
||||
]
|
||||
3
openpype/hosts/flame/api/__init__.py
Normal file
3
openpype/hosts/flame/api/__init__.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
"""
|
||||
OpenPype Autodesk Flame api
|
||||
"""
|
||||
276
openpype/hosts/flame/api/lib.py
Normal file
276
openpype/hosts/flame/api/lib.py
Normal file
|
|
@ -0,0 +1,276 @@
|
|||
import sys
|
||||
import os
|
||||
import pickle
|
||||
import contextlib
|
||||
from pprint import pformat
|
||||
|
||||
from openpype.api import Logger
|
||||
|
||||
log = Logger().get_logger(__name__)
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def io_preferences_file(klass, filepath, write=False):
|
||||
try:
|
||||
flag = "w" if write else "r"
|
||||
yield open(filepath, flag)
|
||||
|
||||
except IOError as _error:
|
||||
klass.log.info("Unable to work with preferences `{}`: {}".format(
|
||||
filepath, _error))
|
||||
|
||||
|
||||
class FlameAppFramework(object):
|
||||
# flameAppFramework class takes care of preferences
|
||||
|
||||
class prefs_dict(dict):
|
||||
|
||||
def __init__(self, master, name, **kwargs):
|
||||
self.name = name
|
||||
self.master = master
|
||||
if not self.master.get(self.name):
|
||||
self.master[self.name] = {}
|
||||
self.master[self.name].__init__()
|
||||
|
||||
def __getitem__(self, k):
|
||||
return self.master[self.name].__getitem__(k)
|
||||
|
||||
def __setitem__(self, k, v):
|
||||
return self.master[self.name].__setitem__(k, v)
|
||||
|
||||
def __delitem__(self, k):
|
||||
return self.master[self.name].__delitem__(k)
|
||||
|
||||
def get(self, k, default=None):
|
||||
return self.master[self.name].get(k, default)
|
||||
|
||||
def setdefault(self, k, default=None):
|
||||
return self.master[self.name].setdefault(k, default)
|
||||
|
||||
def pop(self, k, v=object()):
|
||||
if v is object():
|
||||
return self.master[self.name].pop(k)
|
||||
return self.master[self.name].pop(k, v)
|
||||
|
||||
def update(self, mapping=(), **kwargs):
|
||||
self.master[self.name].update(mapping, **kwargs)
|
||||
|
||||
def __contains__(self, k):
|
||||
return self.master[self.name].__contains__(k)
|
||||
|
||||
def copy(self): # don"t delegate w/ super - dict.copy() -> dict :(
|
||||
return type(self)(self)
|
||||
|
||||
def keys(self):
|
||||
return self.master[self.name].keys()
|
||||
|
||||
@classmethod
|
||||
def fromkeys(cls, keys, v=None):
|
||||
return cls.master[cls.name].fromkeys(keys, v)
|
||||
|
||||
def __repr__(self):
|
||||
return "{0}({1})".format(
|
||||
type(self).__name__, self.master[self.name].__repr__())
|
||||
|
||||
def master_keys(self):
|
||||
return self.master.keys()
|
||||
|
||||
def __init__(self):
|
||||
self.name = self.__class__.__name__
|
||||
self.bundle_name = "OpenPypeFlame"
|
||||
# self.prefs scope is limited to flame project and user
|
||||
self.prefs = {}
|
||||
self.prefs_user = {}
|
||||
self.prefs_global = {}
|
||||
self.log = log
|
||||
|
||||
try:
|
||||
import flame
|
||||
self.flame = flame
|
||||
self.flame_project_name = self.flame.project.current_project.name
|
||||
self.flame_user_name = flame.users.current_user.name
|
||||
except Exception:
|
||||
self.flame = None
|
||||
self.flame_project_name = None
|
||||
self.flame_user_name = None
|
||||
|
||||
import socket
|
||||
self.hostname = socket.gethostname()
|
||||
|
||||
if sys.platform == "darwin":
|
||||
self.prefs_folder = os.path.join(
|
||||
os.path.expanduser("~"),
|
||||
"Library",
|
||||
"Caches",
|
||||
"OpenPype",
|
||||
self.bundle_name
|
||||
)
|
||||
elif sys.platform.startswith("linux"):
|
||||
self.prefs_folder = os.path.join(
|
||||
os.path.expanduser("~"),
|
||||
".OpenPype",
|
||||
self.bundle_name)
|
||||
|
||||
self.prefs_folder = os.path.join(
|
||||
self.prefs_folder,
|
||||
self.hostname,
|
||||
)
|
||||
|
||||
self.log.info("[{}] waking up".format(self.__class__.__name__))
|
||||
self.load_prefs()
|
||||
|
||||
# menu auto-refresh defaults
|
||||
|
||||
if not self.prefs_global.get("menu_auto_refresh"):
|
||||
self.prefs_global["menu_auto_refresh"] = {
|
||||
"media_panel": True,
|
||||
"batch": True,
|
||||
"main_menu": True,
|
||||
"timeline_menu": True
|
||||
}
|
||||
|
||||
self.apps = []
|
||||
|
||||
def get_pref_file_paths(self):
|
||||
|
||||
prefix = self.prefs_folder + os.path.sep + self.bundle_name
|
||||
prefs_file_path = "_".join([
|
||||
prefix, self.flame_user_name,
|
||||
self.flame_project_name]) + ".prefs"
|
||||
prefs_user_file_path = "_".join([
|
||||
prefix, self.flame_user_name]) + ".prefs"
|
||||
prefs_global_file_path = prefix + ".prefs"
|
||||
|
||||
return (prefs_file_path, prefs_user_file_path, prefs_global_file_path)
|
||||
|
||||
def load_prefs(self):
|
||||
|
||||
(proj_pref_path, user_pref_path,
|
||||
glob_pref_path) = self.get_pref_file_paths()
|
||||
|
||||
with io_preferences_file(self, proj_pref_path) as prefs_file:
|
||||
self.prefs = pickle.load(prefs_file)
|
||||
self.log.info(
|
||||
"Project - preferences contents:\n{}".format(
|
||||
pformat(self.prefs)
|
||||
))
|
||||
|
||||
with io_preferences_file(self, user_pref_path) as prefs_file:
|
||||
self.prefs_user = pickle.load(prefs_file)
|
||||
self.log.info(
|
||||
"User - preferences contents:\n{}".format(
|
||||
pformat(self.prefs_user)
|
||||
))
|
||||
|
||||
with io_preferences_file(self, glob_pref_path) as prefs_file:
|
||||
self.prefs_global = pickle.load(prefs_file)
|
||||
self.log.info(
|
||||
"Global - preferences contents:\n{}".format(
|
||||
pformat(self.prefs_global)
|
||||
))
|
||||
|
||||
return True
|
||||
|
||||
def save_prefs(self):
|
||||
# make sure the preference folder is available
|
||||
if not os.path.isdir(self.prefs_folder):
|
||||
try:
|
||||
os.makedirs(self.prefs_folder)
|
||||
except Exception:
|
||||
self.log.info("Unable to create folder {}".format(
|
||||
self.prefs_folder))
|
||||
return False
|
||||
|
||||
# get all pref file paths
|
||||
(proj_pref_path, user_pref_path,
|
||||
glob_pref_path) = self.get_pref_file_paths()
|
||||
|
||||
with io_preferences_file(self, proj_pref_path, True) as prefs_file:
|
||||
pickle.dump(self.prefs, prefs_file)
|
||||
self.log.info(
|
||||
"Project - preferences contents:\n{}".format(
|
||||
pformat(self.prefs)
|
||||
))
|
||||
|
||||
with io_preferences_file(self, user_pref_path, True) as prefs_file:
|
||||
pickle.dump(self.prefs_user, prefs_file)
|
||||
self.log.info(
|
||||
"User - preferences contents:\n{}".format(
|
||||
pformat(self.prefs_user)
|
||||
))
|
||||
|
||||
with io_preferences_file(self, glob_pref_path, True) as prefs_file:
|
||||
pickle.dump(self.prefs_global, prefs_file)
|
||||
self.log.info(
|
||||
"Global - preferences contents:\n{}".format(
|
||||
pformat(self.prefs_global)
|
||||
))
|
||||
|
||||
return True
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def maintain_current_timeline(to_timeline, from_timeline=None):
|
||||
"""Maintain current timeline selection during context
|
||||
|
||||
Attributes:
|
||||
from_timeline (resolve.Timeline)[optional]:
|
||||
Example:
|
||||
>>> print(from_timeline.GetName())
|
||||
timeline1
|
||||
>>> print(to_timeline.GetName())
|
||||
timeline2
|
||||
|
||||
>>> with maintain_current_timeline(to_timeline):
|
||||
... print(get_current_timeline().GetName())
|
||||
timeline2
|
||||
|
||||
>>> print(get_current_timeline().GetName())
|
||||
timeline1
|
||||
"""
|
||||
# todo: this is still Resolve's implementation
|
||||
project = get_current_project()
|
||||
working_timeline = from_timeline or project.GetCurrentTimeline()
|
||||
|
||||
# swith to the input timeline
|
||||
project.SetCurrentTimeline(to_timeline)
|
||||
|
||||
try:
|
||||
# do a work
|
||||
yield
|
||||
finally:
|
||||
# put the original working timeline to context
|
||||
project.SetCurrentTimeline(working_timeline)
|
||||
|
||||
|
||||
def get_project_manager():
|
||||
# TODO: get_project_manager
|
||||
return
|
||||
|
||||
|
||||
def get_media_storage():
|
||||
# TODO: get_media_storage
|
||||
return
|
||||
|
||||
|
||||
def get_current_project():
|
||||
# TODO: get_current_project
|
||||
return
|
||||
|
||||
|
||||
def get_current_timeline(new=False):
|
||||
# TODO: get_current_timeline
|
||||
return
|
||||
|
||||
|
||||
def create_bin(name, root=None):
|
||||
# TODO: create_bin
|
||||
return
|
||||
|
||||
|
||||
def rescan_hooks():
|
||||
import flame
|
||||
try:
|
||||
flame.execute_shortcut('Rescan Python Hooks')
|
||||
except Exception:
|
||||
pass
|
||||
208
openpype/hosts/flame/api/menu.py
Normal file
208
openpype/hosts/flame/api/menu.py
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
import os
|
||||
from Qt import QtWidgets
|
||||
from copy import deepcopy
|
||||
|
||||
from openpype.tools.utils.host_tools import HostToolsHelper
|
||||
|
||||
|
||||
menu_group_name = 'OpenPype'
|
||||
|
||||
default_flame_export_presets = {
|
||||
'Publish': {
|
||||
'PresetVisibility': 2,
|
||||
'PresetType': 0,
|
||||
'PresetFile': 'OpenEXR/OpenEXR (16-bit fp PIZ).xml'
|
||||
},
|
||||
'Preview': {
|
||||
'PresetVisibility': 3,
|
||||
'PresetType': 2,
|
||||
'PresetFile': 'Generate Preview.xml'
|
||||
},
|
||||
'Thumbnail': {
|
||||
'PresetVisibility': 3,
|
||||
'PresetType': 0,
|
||||
'PresetFile': 'Generate Thumbnail.xml'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class _FlameMenuApp(object):
|
||||
def __init__(self, framework):
|
||||
self.name = self.__class__.__name__
|
||||
self.framework = framework
|
||||
self.log = framework.log
|
||||
self.menu_group_name = menu_group_name
|
||||
self.dynamic_menu_data = {}
|
||||
|
||||
# flame module is only avaliable when a
|
||||
# flame project is loaded and initialized
|
||||
self.flame = None
|
||||
try:
|
||||
import flame
|
||||
self.flame = flame
|
||||
except ImportError:
|
||||
self.flame = None
|
||||
|
||||
self.flame_project_name = flame.project.current_project.name
|
||||
self.prefs = self.framework.prefs_dict(self.framework.prefs, self.name)
|
||||
self.prefs_user = self.framework.prefs_dict(
|
||||
self.framework.prefs_user, self.name)
|
||||
self.prefs_global = self.framework.prefs_dict(
|
||||
self.framework.prefs_global, self.name)
|
||||
|
||||
self.mbox = QtWidgets.QMessageBox()
|
||||
|
||||
self.menu = {
|
||||
"actions": [{
|
||||
'name': os.getenv("AVALON_PROJECT", "project"),
|
||||
'isEnabled': False
|
||||
}],
|
||||
"name": self.menu_group_name
|
||||
}
|
||||
self.tools_helper = HostToolsHelper()
|
||||
|
||||
def __getattr__(self, name):
|
||||
def method(*args, **kwargs):
|
||||
print('calling %s' % name)
|
||||
return method
|
||||
|
||||
def rescan(self, *args, **kwargs):
|
||||
if not self.flame:
|
||||
try:
|
||||
import flame
|
||||
self.flame = flame
|
||||
except ImportError:
|
||||
self.flame = None
|
||||
|
||||
if self.flame:
|
||||
self.flame.execute_shortcut('Rescan Python Hooks')
|
||||
self.log.info('Rescan Python Hooks')
|
||||
|
||||
|
||||
class FlameMenuProjectConnect(_FlameMenuApp):
|
||||
|
||||
# flameMenuProjectconnect app takes care of the preferences dialog as well
|
||||
|
||||
def __init__(self, framework):
|
||||
_FlameMenuApp.__init__(self, framework)
|
||||
|
||||
def __getattr__(self, name):
|
||||
def method(*args, **kwargs):
|
||||
project = self.dynamic_menu_data.get(name)
|
||||
if project:
|
||||
self.link_project(project)
|
||||
return method
|
||||
|
||||
def build_menu(self):
|
||||
if not self.flame:
|
||||
return []
|
||||
|
||||
flame_project_name = self.flame_project_name
|
||||
self.log.info("______ {} ______".format(flame_project_name))
|
||||
|
||||
menu = deepcopy(self.menu)
|
||||
|
||||
menu['actions'].append({
|
||||
"name": "Workfiles ...",
|
||||
"execute": lambda x: self.tools_helper.show_workfiles()
|
||||
})
|
||||
menu['actions'].append({
|
||||
"name": "Create ...",
|
||||
"execute": lambda x: self.tools_helper.show_creator()
|
||||
})
|
||||
menu['actions'].append({
|
||||
"name": "Publish ...",
|
||||
"execute": lambda x: self.tools_helper.show_publish()
|
||||
})
|
||||
menu['actions'].append({
|
||||
"name": "Load ...",
|
||||
"execute": lambda x: self.tools_helper.show_loader()
|
||||
})
|
||||
menu['actions'].append({
|
||||
"name": "Manage ...",
|
||||
"execute": lambda x: self.tools_helper.show_scene_inventory()
|
||||
})
|
||||
menu['actions'].append({
|
||||
"name": "Library ...",
|
||||
"execute": lambda x: self.tools_helper.show_library_loader()
|
||||
})
|
||||
return menu
|
||||
|
||||
def get_projects(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def refresh(self, *args, **kwargs):
|
||||
self.rescan()
|
||||
|
||||
def rescan(self, *args, **kwargs):
|
||||
if not self.flame:
|
||||
try:
|
||||
import flame
|
||||
self.flame = flame
|
||||
except ImportError:
|
||||
self.flame = None
|
||||
|
||||
if self.flame:
|
||||
self.flame.execute_shortcut('Rescan Python Hooks')
|
||||
self.log.info('Rescan Python Hooks')
|
||||
|
||||
|
||||
class FlameMenuTimeline(_FlameMenuApp):
|
||||
|
||||
# flameMenuProjectconnect app takes care of the preferences dialog as well
|
||||
|
||||
def __init__(self, framework):
|
||||
_FlameMenuApp.__init__(self, framework)
|
||||
|
||||
def __getattr__(self, name):
|
||||
def method(*args, **kwargs):
|
||||
project = self.dynamic_menu_data.get(name)
|
||||
if project:
|
||||
self.link_project(project)
|
||||
return method
|
||||
|
||||
def build_menu(self):
|
||||
if not self.flame:
|
||||
return []
|
||||
|
||||
flame_project_name = self.flame_project_name
|
||||
self.log.info("______ {} ______".format(flame_project_name))
|
||||
|
||||
menu = deepcopy(self.menu)
|
||||
|
||||
menu['actions'].append({
|
||||
"name": "Create ...",
|
||||
"execute": lambda x: self.tools_helper.show_creator()
|
||||
})
|
||||
menu['actions'].append({
|
||||
"name": "Publish ...",
|
||||
"execute": lambda x: self.tools_helper.show_publish()
|
||||
})
|
||||
menu['actions'].append({
|
||||
"name": "Load ...",
|
||||
"execute": lambda x: self.tools_helper.show_loader()
|
||||
})
|
||||
menu['actions'].append({
|
||||
"name": "Manage ...",
|
||||
"execute": lambda x: self.tools_helper.show_scene_inventory()
|
||||
})
|
||||
|
||||
return menu
|
||||
|
||||
def get_projects(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def refresh(self, *args, **kwargs):
|
||||
self.rescan()
|
||||
|
||||
def rescan(self, *args, **kwargs):
|
||||
if not self.flame:
|
||||
try:
|
||||
import flame
|
||||
self.flame = flame
|
||||
except ImportError:
|
||||
self.flame = None
|
||||
|
||||
if self.flame:
|
||||
self.flame.execute_shortcut('Rescan Python Hooks')
|
||||
self.log.info('Rescan Python Hooks')
|
||||
155
openpype/hosts/flame/api/pipeline.py
Normal file
155
openpype/hosts/flame/api/pipeline.py
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
"""
|
||||
Basic avalon integration
|
||||
"""
|
||||
import contextlib
|
||||
from avalon import api as avalon
|
||||
from pyblish import api as pyblish
|
||||
from openpype.api import Logger
|
||||
|
||||
AVALON_CONTAINERS = "AVALON_CONTAINERS"
|
||||
|
||||
log = Logger().get_logger(__name__)
|
||||
|
||||
|
||||
def install():
|
||||
from .. import (
|
||||
PUBLISH_PATH,
|
||||
LOAD_PATH,
|
||||
CREATE_PATH,
|
||||
INVENTORY_PATH
|
||||
)
|
||||
# TODO: install
|
||||
|
||||
# Disable all families except for the ones we explicitly want to see
|
||||
family_states = [
|
||||
"imagesequence",
|
||||
"render2d",
|
||||
"plate",
|
||||
"render",
|
||||
"mov",
|
||||
"clip"
|
||||
]
|
||||
avalon.data["familiesStateDefault"] = False
|
||||
avalon.data["familiesStateToggled"] = family_states
|
||||
|
||||
log.info("openpype.hosts.flame installed")
|
||||
|
||||
pyblish.register_host("flame")
|
||||
pyblish.register_plugin_path(PUBLISH_PATH)
|
||||
log.info("Registering Flame plug-ins..")
|
||||
|
||||
avalon.register_plugin_path(avalon.Loader, LOAD_PATH)
|
||||
avalon.register_plugin_path(avalon.Creator, CREATE_PATH)
|
||||
avalon.register_plugin_path(avalon.InventoryAction, INVENTORY_PATH)
|
||||
|
||||
# register callback for switching publishable
|
||||
pyblish.register_callback("instanceToggled", on_pyblish_instance_toggled)
|
||||
|
||||
|
||||
def uninstall():
|
||||
from .. import (
|
||||
PUBLISH_PATH,
|
||||
LOAD_PATH,
|
||||
CREATE_PATH,
|
||||
INVENTORY_PATH
|
||||
)
|
||||
|
||||
# TODO: uninstall
|
||||
pyblish.deregister_host("flame")
|
||||
pyblish.deregister_plugin_path(PUBLISH_PATH)
|
||||
log.info("Deregistering DaVinci Resovle plug-ins..")
|
||||
|
||||
avalon.deregister_plugin_path(avalon.Loader, LOAD_PATH)
|
||||
avalon.deregister_plugin_path(avalon.Creator, CREATE_PATH)
|
||||
avalon.deregister_plugin_path(avalon.InventoryAction, INVENTORY_PATH)
|
||||
|
||||
# register callback for switching publishable
|
||||
pyblish.deregister_callback("instanceToggled", on_pyblish_instance_toggled)
|
||||
|
||||
|
||||
def containerise(tl_segment,
|
||||
name,
|
||||
namespace,
|
||||
context,
|
||||
loader=None,
|
||||
data=None):
|
||||
# TODO: containerise
|
||||
pass
|
||||
|
||||
|
||||
def ls():
|
||||
"""List available containers.
|
||||
"""
|
||||
# TODO: ls
|
||||
pass
|
||||
|
||||
|
||||
def parse_container(tl_segment, validate=True):
|
||||
"""Return container data from timeline_item's openpype tag.
|
||||
"""
|
||||
# TODO: parse_container
|
||||
pass
|
||||
|
||||
|
||||
def update_container(tl_segment, data=None):
|
||||
"""Update container data to input timeline_item's openpype tag.
|
||||
"""
|
||||
# TODO: update_container
|
||||
pass
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def maintained_selection():
|
||||
"""Maintain selection during context
|
||||
|
||||
Example:
|
||||
>>> with maintained_selection():
|
||||
... node['selected'].setValue(True)
|
||||
>>> print(node['selected'].value())
|
||||
False
|
||||
"""
|
||||
# TODO: maintained_selection + remove undo steps
|
||||
|
||||
try:
|
||||
# do the operation
|
||||
yield
|
||||
finally:
|
||||
pass
|
||||
|
||||
|
||||
def reset_selection():
|
||||
"""Deselect all selected nodes
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
def on_pyblish_instance_toggled(instance, old_value, new_value):
|
||||
"""Toggle node passthrough states on instance toggles."""
|
||||
|
||||
log.info("instance toggle: {}, old_value: {}, new_value:{} ".format(
|
||||
instance, old_value, new_value))
|
||||
|
||||
# from openpype.hosts.resolve import (
|
||||
# set_publish_attribute
|
||||
# )
|
||||
|
||||
# # Whether instances should be passthrough based on new value
|
||||
# timeline_item = instance.data["item"]
|
||||
# set_publish_attribute(timeline_item, new_value)
|
||||
|
||||
|
||||
def remove_instance(instance):
|
||||
"""Remove instance marker from track item."""
|
||||
# TODO: remove_instance
|
||||
pass
|
||||
|
||||
|
||||
def list_instances():
|
||||
"""List all created instances from current workfile."""
|
||||
# TODO: list_instances
|
||||
pass
|
||||
|
||||
|
||||
def imprint(item, data=None):
|
||||
# TODO: imprint
|
||||
pass
|
||||
3
openpype/hosts/flame/api/plugin.py
Normal file
3
openpype/hosts/flame/api/plugin.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# Creator plugin functions
|
||||
# Publishing plugin functions
|
||||
# Loader plugin functions
|
||||
108
openpype/hosts/flame/api/utils.py
Normal file
108
openpype/hosts/flame/api/utils.py
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
"""
|
||||
Flame utils for syncing scripts
|
||||
"""
|
||||
|
||||
import os
|
||||
import shutil
|
||||
from openpype.api import Logger
|
||||
log = Logger().get_logger(__name__)
|
||||
|
||||
|
||||
def _sync_utility_scripts(env=None):
|
||||
""" Synchronizing basic utlility scripts for flame.
|
||||
|
||||
To be able to run start OpenPype within Flame we have to copy
|
||||
all utility_scripts and additional FLAME_SCRIPT_DIR into
|
||||
`/opt/Autodesk/shared/python`. This will be always synchronizing those
|
||||
folders.
|
||||
"""
|
||||
from .. import HOST_DIR
|
||||
|
||||
env = env or os.environ
|
||||
|
||||
# initiate inputs
|
||||
scripts = {}
|
||||
fsd_env = env.get("FLAME_SCRIPT_DIRS", "")
|
||||
flame_shared_dir = "/opt/Autodesk/shared/python"
|
||||
|
||||
fsd_paths = [os.path.join(
|
||||
HOST_DIR,
|
||||
"utility_scripts"
|
||||
)]
|
||||
|
||||
# collect script dirs
|
||||
log.info("FLAME_SCRIPT_DIRS: `{fsd_env}`".format(**locals()))
|
||||
log.info("fsd_paths: `{fsd_paths}`".format(**locals()))
|
||||
|
||||
# add application environment setting for FLAME_SCRIPT_DIR
|
||||
# to script path search
|
||||
for _dirpath in fsd_env.split(os.pathsep):
|
||||
if not os.path.isdir(_dirpath):
|
||||
log.warning("Path is not a valid dir: `{_dirpath}`".format(
|
||||
**locals()))
|
||||
continue
|
||||
fsd_paths.append(_dirpath)
|
||||
|
||||
# collect scripts from dirs
|
||||
for path in fsd_paths:
|
||||
scripts.update({path: os.listdir(path)})
|
||||
|
||||
remove_black_list = []
|
||||
for _k, s_list in scripts.items():
|
||||
remove_black_list += s_list
|
||||
|
||||
log.info("remove_black_list: `{remove_black_list}`".format(**locals()))
|
||||
log.info("Additional Flame script paths: `{fsd_paths}`".format(**locals()))
|
||||
log.info("Flame Scripts: `{scripts}`".format(**locals()))
|
||||
|
||||
# make sure no script file is in folder
|
||||
if next(iter(os.listdir(flame_shared_dir)), None):
|
||||
for _itm in os.listdir(flame_shared_dir):
|
||||
skip = False
|
||||
|
||||
# skip all scripts and folders which are not maintained
|
||||
if _itm not in remove_black_list:
|
||||
skip = True
|
||||
|
||||
# do not skyp if pyc in extension
|
||||
if not os.path.isdir(_itm) and "pyc" in os.path.splitext(_itm)[-1]:
|
||||
skip = False
|
||||
|
||||
# continue if skip in true
|
||||
if skip:
|
||||
continue
|
||||
|
||||
path = os.path.join(flame_shared_dir, _itm)
|
||||
log.info("Removing `{path}`...".format(**locals()))
|
||||
if os.path.isdir(path):
|
||||
shutil.rmtree(path, onerror=None)
|
||||
else:
|
||||
os.remove(path)
|
||||
|
||||
# copy scripts into Resolve's utility scripts dir
|
||||
for dirpath, scriptlist in scripts.items():
|
||||
# directory and scripts list
|
||||
for _script in scriptlist:
|
||||
# script in script list
|
||||
src = os.path.join(dirpath, _script)
|
||||
dst = os.path.join(flame_shared_dir, _script)
|
||||
log.info("Copying `{src}` to `{dst}`...".format(**locals()))
|
||||
if os.path.isdir(src):
|
||||
shutil.copytree(
|
||||
src, dst, symlinks=False,
|
||||
ignore=None, ignore_dangling_symlinks=False
|
||||
)
|
||||
else:
|
||||
shutil.copy2(src, dst)
|
||||
|
||||
|
||||
def setup(env=None):
|
||||
""" Wrapper installer started from
|
||||
`flame/hooks/pre_flame_setup.py`
|
||||
"""
|
||||
env = env or os.environ
|
||||
|
||||
# synchronize resolve utility scripts
|
||||
_sync_utility_scripts(env)
|
||||
|
||||
log.info("Flame OpenPype wrapper has been installed")
|
||||
37
openpype/hosts/flame/api/workio.py
Normal file
37
openpype/hosts/flame/api/workio.py
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
"""Host API required Work Files tool"""
|
||||
|
||||
import os
|
||||
from openpype.api import Logger
|
||||
# from .. import (
|
||||
# get_project_manager,
|
||||
# get_current_project
|
||||
# )
|
||||
|
||||
|
||||
log = Logger().get_logger(__name__)
|
||||
|
||||
exported_projet_ext = ".otoc"
|
||||
|
||||
|
||||
def file_extensions():
|
||||
return [exported_projet_ext]
|
||||
|
||||
|
||||
def has_unsaved_changes():
|
||||
pass
|
||||
|
||||
|
||||
def save_file(filepath):
|
||||
pass
|
||||
|
||||
|
||||
def open_file(filepath):
|
||||
pass
|
||||
|
||||
|
||||
def current_file():
|
||||
pass
|
||||
|
||||
|
||||
def work_root(session):
|
||||
return os.path.normpath(session["AVALON_WORKDIR"]).replace("\\", "/")
|
||||
132
openpype/hosts/flame/hooks/pre_flame_setup.py
Normal file
132
openpype/hosts/flame/hooks/pre_flame_setup.py
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
import os
|
||||
import json
|
||||
import tempfile
|
||||
import contextlib
|
||||
from openpype.lib import (
|
||||
PreLaunchHook, get_openpype_username)
|
||||
from openpype.hosts import flame as opflame
|
||||
import openpype
|
||||
from pprint import pformat
|
||||
|
||||
|
||||
class FlamePrelaunch(PreLaunchHook):
|
||||
""" Flame prelaunch hook
|
||||
|
||||
Will make sure flame_script_dirs are coppied to user's folder defined
|
||||
in environment var FLAME_SCRIPT_DIR.
|
||||
"""
|
||||
app_groups = ["flame"]
|
||||
|
||||
# todo: replace version number with avalon launch app version
|
||||
flame_python_exe = "/opt/Autodesk/python/2021/bin/python2.7"
|
||||
|
||||
wtc_script_path = os.path.join(
|
||||
opflame.HOST_DIR, "scripts", "wiretap_com.py")
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.signature = "( {} )".format(self.__class__.__name__)
|
||||
|
||||
def execute(self):
|
||||
"""Hook entry method."""
|
||||
project_doc = self.data["project_doc"]
|
||||
user_name = get_openpype_username()
|
||||
|
||||
self.log.debug("Collected user \"{}\"".format(user_name))
|
||||
self.log.info(pformat(project_doc))
|
||||
_db_p_data = project_doc["data"]
|
||||
width = _db_p_data["resolutionWidth"]
|
||||
height = _db_p_data["resolutionHeight"]
|
||||
fps = int(_db_p_data["fps"])
|
||||
|
||||
project_data = {
|
||||
"Name": project_doc["name"],
|
||||
"Nickname": _db_p_data["code"],
|
||||
"Description": "Created by OpenPype",
|
||||
"SetupDir": project_doc["name"],
|
||||
"FrameWidth": int(width),
|
||||
"FrameHeight": int(height),
|
||||
"AspectRatio": float((width / height) * _db_p_data["pixelAspect"]),
|
||||
"FrameRate": "{} fps".format(fps),
|
||||
"FrameDepth": "16-bit fp",
|
||||
"FieldDominance": "PROGRESSIVE"
|
||||
}
|
||||
|
||||
data_to_script = {
|
||||
# from settings
|
||||
"host_name": "localhost",
|
||||
"volume_name": "stonefs",
|
||||
"group_name": "staff",
|
||||
"color_policy": "ACES 1.1",
|
||||
|
||||
# from project
|
||||
"project_name": project_doc["name"],
|
||||
"user_name": user_name,
|
||||
"project_data": project_data
|
||||
}
|
||||
app_arguments = self._get_launch_arguments(data_to_script)
|
||||
|
||||
self.log.info(pformat(dict(self.launch_context.env)))
|
||||
|
||||
opflame.setup(self.launch_context.env)
|
||||
|
||||
self.launch_context.launch_args.extend(app_arguments)
|
||||
|
||||
def _get_launch_arguments(self, script_data):
|
||||
# Dump data to string
|
||||
dumped_script_data = json.dumps(script_data)
|
||||
|
||||
with make_temp_file(dumped_script_data) as tmp_json_path:
|
||||
# Prepare subprocess arguments
|
||||
args = [
|
||||
self.flame_python_exe,
|
||||
self.wtc_script_path,
|
||||
tmp_json_path
|
||||
]
|
||||
self.log.info("Executing: {}".format(" ".join(args)))
|
||||
|
||||
process_kwargs = {
|
||||
"logger": self.log,
|
||||
"env": {}
|
||||
}
|
||||
|
||||
openpype.api.run_subprocess(args, **process_kwargs)
|
||||
|
||||
# process returned json file to pass launch args
|
||||
return_json_data = open(tmp_json_path).read()
|
||||
returned_data = json.loads(return_json_data)
|
||||
app_args = returned_data.get("app_args")
|
||||
self.log.info("____ app_args: `{}`".format(app_args))
|
||||
|
||||
if not app_args:
|
||||
RuntimeError("App arguments were not solved")
|
||||
|
||||
return app_args
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def make_temp_file(data):
|
||||
try:
|
||||
# Store dumped json to temporary file
|
||||
temporary_json_file = tempfile.NamedTemporaryFile(
|
||||
mode="w", suffix=".json", delete=False
|
||||
)
|
||||
temporary_json_file.write(data)
|
||||
temporary_json_file.close()
|
||||
temporary_json_filepath = temporary_json_file.name.replace(
|
||||
"\\", "/"
|
||||
)
|
||||
|
||||
yield temporary_json_filepath
|
||||
|
||||
except IOError as _error:
|
||||
raise IOError(
|
||||
"Not able to create temp json file: {}".format(
|
||||
_error
|
||||
)
|
||||
)
|
||||
|
||||
finally:
|
||||
# Remove the temporary json
|
||||
os.remove(temporary_json_filepath)
|
||||
490
openpype/hosts/flame/scripts/wiretap_com.py
Normal file
490
openpype/hosts/flame/scripts/wiretap_com.py
Normal file
|
|
@ -0,0 +1,490 @@
|
|||
#!/usr/bin/env python2.7
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import absolute_import
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
import json
|
||||
import xml.dom.minidom as minidom
|
||||
from copy import deepcopy
|
||||
import datetime
|
||||
|
||||
try:
|
||||
from libwiretapPythonClientAPI import (
|
||||
WireTapClientInit)
|
||||
except ImportError:
|
||||
flame_python_path = "/opt/Autodesk/flame_2021/python"
|
||||
flame_exe_path = (
|
||||
"/opt/Autodesk/flame_2021/bin/flame.app"
|
||||
"/Contents/MacOS/startApp")
|
||||
|
||||
sys.path.append(flame_python_path)
|
||||
|
||||
from libwiretapPythonClientAPI import (
|
||||
WireTapClientInit,
|
||||
WireTapClientUninit,
|
||||
WireTapNodeHandle,
|
||||
WireTapServerHandle,
|
||||
WireTapInt,
|
||||
WireTapStr
|
||||
)
|
||||
|
||||
|
||||
class WireTapCom(object):
|
||||
"""
|
||||
Comunicator class wrapper for talking to WireTap db.
|
||||
|
||||
This way we are able to set new project with settings and
|
||||
correct colorspace policy. Also we are able to create new user
|
||||
or get actuall user with similar name (users are usually cloning
|
||||
their profiles and adding date stamp into suffix).
|
||||
"""
|
||||
|
||||
def __init__(self, host_name=None, volume_name=None, group_name=None):
|
||||
"""Initialisation of WireTap communication class
|
||||
|
||||
Args:
|
||||
host_name (str, optional): Name of host server. Defaults to None.
|
||||
volume_name (str, optional): Name of volume. Defaults to None.
|
||||
group_name (str, optional): Name of user group. Defaults to None.
|
||||
"""
|
||||
# set main attributes of server
|
||||
# if there are none set the default installation
|
||||
self.host_name = host_name or "localhost"
|
||||
self.volume_name = volume_name or "stonefs"
|
||||
self.group_name = group_name or "staff"
|
||||
|
||||
# initialize WireTap client
|
||||
WireTapClientInit()
|
||||
|
||||
# add the server to shared variable
|
||||
self._server = WireTapServerHandle("{}:IFFFS".format(self.host_name))
|
||||
print("WireTap connected at '{}'...".format(
|
||||
self.host_name))
|
||||
|
||||
def close(self):
|
||||
self._server = None
|
||||
WireTapClientUninit()
|
||||
print("WireTap closed...")
|
||||
|
||||
def get_launch_args(
|
||||
self, project_name, project_data, user_name, *args, **kwargs):
|
||||
"""Forming launch arguments for OpenPype launcher.
|
||||
|
||||
Args:
|
||||
project_name (str): name of project
|
||||
project_data (dict): Flame compatible project data
|
||||
user_name (str): name of user
|
||||
|
||||
Returns:
|
||||
list: arguments
|
||||
"""
|
||||
|
||||
workspace_name = kwargs.get("workspace_name")
|
||||
color_policy = kwargs.get("color_policy")
|
||||
|
||||
self._project_prep(project_name)
|
||||
self._set_project_settings(project_name, project_data)
|
||||
self._set_project_colorspace(project_name, color_policy)
|
||||
user_name = self._user_prep(user_name)
|
||||
|
||||
if workspace_name is None:
|
||||
# default workspace
|
||||
print("Using a default workspace")
|
||||
return [
|
||||
"--start-project={}".format(project_name),
|
||||
"--start-user={}".format(user_name),
|
||||
"--create-workspace"
|
||||
]
|
||||
|
||||
else:
|
||||
print(
|
||||
"Using a custom workspace '{}'".format(workspace_name))
|
||||
|
||||
self._workspace_prep(project_name, workspace_name)
|
||||
return [
|
||||
"--start-project={}".format(project_name),
|
||||
"--start-user={}".format(user_name),
|
||||
"--create-workspace",
|
||||
"--start-workspace={}".format(workspace_name)
|
||||
]
|
||||
|
||||
def _workspace_prep(self, project_name, workspace_name):
|
||||
"""Preparing a workspace
|
||||
|
||||
In case it doesn not exists it will create one
|
||||
|
||||
Args:
|
||||
project_name (str): project name
|
||||
workspace_name (str): workspace name
|
||||
|
||||
Raises:
|
||||
AttributeError: unable to create workspace
|
||||
"""
|
||||
workspace_exists = self._child_is_in_parent_path(
|
||||
"/projects/{}".format(project_name), workspace_name, "WORKSPACE"
|
||||
)
|
||||
if not workspace_exists:
|
||||
project = WireTapNodeHandle(
|
||||
self._server, "/projects/{}".format(project_name))
|
||||
|
||||
workspace_node = WireTapNodeHandle()
|
||||
created_workspace = project.createNode(
|
||||
workspace_name, "WORKSPACE", workspace_node)
|
||||
|
||||
if not created_workspace:
|
||||
raise AttributeError(
|
||||
"Cannot create workspace `{}` in "
|
||||
"project `{}`: `{}`".format(
|
||||
workspace_name, project_name, project.lastError())
|
||||
)
|
||||
|
||||
print(
|
||||
"Workspace `{}` is successfully created".format(workspace_name))
|
||||
|
||||
def _project_prep(self, project_name):
|
||||
"""Preparing a project
|
||||
|
||||
In case it doesn not exists it will create one
|
||||
|
||||
Args:
|
||||
project_name (str): project name
|
||||
|
||||
Raises:
|
||||
AttributeError: unable to create project
|
||||
"""
|
||||
# test if projeft exists
|
||||
project_exists = self._child_is_in_parent_path(
|
||||
"/projects", project_name, "PROJECT")
|
||||
|
||||
if not project_exists:
|
||||
volumes = self._get_all_volumes()
|
||||
|
||||
if len(volumes) == 0:
|
||||
raise AttributeError(
|
||||
"Not able to create new project. No Volumes existing"
|
||||
)
|
||||
|
||||
# check if volumes exists
|
||||
if self.volume_name not in volumes:
|
||||
raise AttributeError(
|
||||
("Volume '{}' does not exist '{}'").format(
|
||||
self.volume_name, volumes)
|
||||
)
|
||||
|
||||
# form cmd arguments
|
||||
project_create_cmd = [
|
||||
os.path.join(
|
||||
"/opt/Autodesk/",
|
||||
"wiretap",
|
||||
"tools",
|
||||
"2021",
|
||||
"wiretap_create_node",
|
||||
),
|
||||
'-n',
|
||||
os.path.join("/volumes", self.volume_name),
|
||||
'-d',
|
||||
project_name,
|
||||
'-g',
|
||||
]
|
||||
|
||||
project_create_cmd.append(self.group_name)
|
||||
|
||||
print(project_create_cmd)
|
||||
|
||||
exit_code = subprocess.call(
|
||||
project_create_cmd,
|
||||
cwd=os.path.expanduser('~'))
|
||||
|
||||
if exit_code != 0:
|
||||
RuntimeError("Cannot create project in flame db")
|
||||
|
||||
print(
|
||||
"A new project '{}' is created.".format(project_name))
|
||||
|
||||
def _get_all_volumes(self):
|
||||
"""Request all available volumens from WireTap
|
||||
|
||||
Returns:
|
||||
list: all available volumes in server
|
||||
|
||||
Rises:
|
||||
AttributeError: unable to get any volumes childs from server
|
||||
"""
|
||||
root = WireTapNodeHandle(self._server, "/volumes")
|
||||
children_num = WireTapInt(0)
|
||||
|
||||
get_children_num = root.getNumChildren(children_num)
|
||||
if not get_children_num:
|
||||
raise AttributeError(
|
||||
"Cannot get number of volumes: {}".format(root.lastError())
|
||||
)
|
||||
|
||||
volumes = []
|
||||
|
||||
# go trough all children and get volume names
|
||||
child_obj = WireTapNodeHandle()
|
||||
for child_idx in range(children_num):
|
||||
|
||||
# get a child
|
||||
if not root.getChild(child_idx, child_obj):
|
||||
raise AttributeError(
|
||||
"Unable to get child: {}".format(root.lastError()))
|
||||
|
||||
node_name = WireTapStr()
|
||||
get_children_name = child_obj.getDisplayName(node_name)
|
||||
|
||||
if not get_children_name:
|
||||
raise AttributeError(
|
||||
"Unable to get child name: {}".format(
|
||||
child_obj.lastError())
|
||||
)
|
||||
|
||||
volumes.append(node_name.c_str())
|
||||
|
||||
return volumes
|
||||
|
||||
def _user_prep(self, user_name):
|
||||
"""Ensuring user does exists in user's stack
|
||||
|
||||
Args:
|
||||
user_name (str): name of a user
|
||||
|
||||
Raises:
|
||||
AttributeError: unable to create user
|
||||
"""
|
||||
|
||||
# get all used usernames in db
|
||||
used_names = self._get_usernames()
|
||||
print(">> used_names: {}".format(used_names))
|
||||
|
||||
# filter only those which are sharing input user name
|
||||
filtered_users = [user for user in used_names if user_name in user]
|
||||
|
||||
if filtered_users:
|
||||
# todo: need to find lastly created following regex patern for
|
||||
# date used in name
|
||||
return filtered_users.pop()
|
||||
|
||||
# create new user name with date in suffix
|
||||
now = datetime.datetime.now() # current date and time
|
||||
date = now.strftime("%Y%m%d")
|
||||
new_user_name = "{}_{}".format(user_name, date)
|
||||
print(new_user_name)
|
||||
|
||||
if not self._child_is_in_parent_path("/users", new_user_name, "USER"):
|
||||
# Create the new user
|
||||
users = WireTapNodeHandle(self._server, "/users")
|
||||
|
||||
user_node = WireTapNodeHandle()
|
||||
created_user = users.createNode(new_user_name, "USER", user_node)
|
||||
if not created_user:
|
||||
raise AttributeError(
|
||||
"User {} cannot be created: {}".format(
|
||||
new_user_name, users.lastError())
|
||||
)
|
||||
|
||||
print("User `{}` is created".format(new_user_name))
|
||||
return new_user_name
|
||||
|
||||
def _get_usernames(self):
|
||||
"""Requesting all available users from WireTap
|
||||
|
||||
Returns:
|
||||
list: all available user names
|
||||
|
||||
Raises:
|
||||
AttributeError: there are no users in server
|
||||
"""
|
||||
root = WireTapNodeHandle(self._server, "/users")
|
||||
children_num = WireTapInt(0)
|
||||
|
||||
get_children_num = root.getNumChildren(children_num)
|
||||
if not get_children_num:
|
||||
raise AttributeError(
|
||||
"Cannot get number of volumes: {}".format(root.lastError())
|
||||
)
|
||||
|
||||
usernames = []
|
||||
|
||||
# go trough all children and get volume names
|
||||
child_obj = WireTapNodeHandle()
|
||||
for child_idx in range(children_num):
|
||||
|
||||
# get a child
|
||||
if not root.getChild(child_idx, child_obj):
|
||||
raise AttributeError(
|
||||
"Unable to get child: {}".format(root.lastError()))
|
||||
|
||||
node_name = WireTapStr()
|
||||
get_children_name = child_obj.getDisplayName(node_name)
|
||||
|
||||
if not get_children_name:
|
||||
raise AttributeError(
|
||||
"Unable to get child name: {}".format(
|
||||
child_obj.lastError())
|
||||
)
|
||||
|
||||
usernames.append(node_name.c_str())
|
||||
|
||||
return usernames
|
||||
|
||||
def _child_is_in_parent_path(self, parent_path, child_name, child_type):
|
||||
"""Checking if a given child is in parent path.
|
||||
|
||||
Args:
|
||||
parent_path (str): db path to parent
|
||||
child_name (str): name of child
|
||||
child_type (str): type of child
|
||||
|
||||
Raises:
|
||||
AttributeError: Not able to get number of children
|
||||
AttributeError: Not able to get children form parent
|
||||
AttributeError: Not able to get children name
|
||||
AttributeError: Not able to get children type
|
||||
|
||||
Returns:
|
||||
bool: True if child is in parent path
|
||||
"""
|
||||
parent = WireTapNodeHandle(self._server, parent_path)
|
||||
|
||||
# iterate number of children
|
||||
children_num = WireTapInt(0)
|
||||
requested = parent.getNumChildren(children_num)
|
||||
if not requested:
|
||||
raise AttributeError((
|
||||
"Error: Cannot request number of "
|
||||
"childrens from the node {}. Make sure your "
|
||||
"wiretap service is running: {}").format(
|
||||
parent_path, parent.lastError())
|
||||
)
|
||||
|
||||
# iterate children
|
||||
child_obj = WireTapNodeHandle()
|
||||
for child_idx in range(children_num):
|
||||
if not parent.getChild(child_idx, child_obj):
|
||||
raise AttributeError(
|
||||
"Cannot get child: {}".format(
|
||||
parent.lastError()))
|
||||
|
||||
node_name = WireTapStr()
|
||||
node_type = WireTapStr()
|
||||
|
||||
if not child_obj.getDisplayName(node_name):
|
||||
raise AttributeError(
|
||||
"Unable to get child name: %s" % child_obj.lastError()
|
||||
)
|
||||
if not child_obj.getNodeTypeStr(node_type):
|
||||
raise AttributeError(
|
||||
"Unable to obtain child type: %s" % child_obj.lastError()
|
||||
)
|
||||
|
||||
if (node_name.c_str() == child_name) and (
|
||||
node_type.c_str() == child_type):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def _set_project_settings(self, project_name, project_data):
|
||||
"""Setting project attributes.
|
||||
|
||||
Args:
|
||||
project_name (str): name of project
|
||||
project_data (dict): data with project attributes
|
||||
(flame compatible)
|
||||
|
||||
Raises:
|
||||
AttributeError: Not able to set project attributes
|
||||
"""
|
||||
# generated xml from project_data dict
|
||||
_xml = "<Project>"
|
||||
for key, value in project_data.items():
|
||||
_xml += "<{}>{}</{}>".format(key, value, key)
|
||||
_xml += "</Project>"
|
||||
|
||||
pretty_xml = minidom.parseString(_xml).toprettyxml()
|
||||
print("__ xml: {}".format(pretty_xml))
|
||||
|
||||
# set project data to wiretap
|
||||
project_node = WireTapNodeHandle(
|
||||
self._server, "/projects/{}".format(project_name))
|
||||
|
||||
if not project_node.setMetaData("XML", _xml):
|
||||
raise AttributeError(
|
||||
"Not able to set project attributes {}. Error: {}".format(
|
||||
project_name, project_node.lastError())
|
||||
)
|
||||
|
||||
print("Project settings successfully set.")
|
||||
|
||||
def _set_project_colorspace(self, project_name, color_policy):
|
||||
"""Set project's colorspace policy.
|
||||
|
||||
Args:
|
||||
project_name (str): name of project
|
||||
color_policy (str): name of policy
|
||||
|
||||
Raises:
|
||||
RuntimeError: Not able to set colorspace policy
|
||||
"""
|
||||
color_policy = color_policy or "Legacy"
|
||||
project_colorspace_cmd = [
|
||||
os.path.join(
|
||||
"/opt/Autodesk/",
|
||||
"wiretap",
|
||||
"tools",
|
||||
"2021",
|
||||
"wiretap_duplicate_node",
|
||||
),
|
||||
"-s",
|
||||
"/syncolor/policies/Autodesk/{}".format(color_policy),
|
||||
"-n",
|
||||
"/projects/{}/syncolor".format(project_name)
|
||||
]
|
||||
|
||||
print(project_colorspace_cmd)
|
||||
|
||||
exit_code = subprocess.call(
|
||||
project_colorspace_cmd,
|
||||
cwd=os.path.expanduser('~'))
|
||||
|
||||
if exit_code != 0:
|
||||
RuntimeError("Cannot set colorspace {} on project {}".format(
|
||||
color_policy, project_name
|
||||
))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# get json exchange data
|
||||
json_path = sys.argv[-1]
|
||||
json_data = open(json_path).read()
|
||||
in_data = json.loads(json_data)
|
||||
out_data = deepcopy(in_data)
|
||||
|
||||
# get main server attributes
|
||||
host_name = in_data.pop("host_name")
|
||||
volume_name = in_data.pop("volume_name")
|
||||
group_name = in_data.pop("group_name")
|
||||
|
||||
# initialize class
|
||||
wiretap_handler = WireTapCom(host_name, volume_name, group_name)
|
||||
|
||||
try:
|
||||
app_args = wiretap_handler.get_launch_args(
|
||||
project_name=in_data.pop("project_name"),
|
||||
project_data=in_data.pop("project_data"),
|
||||
user_name=in_data.pop("user_name"),
|
||||
**in_data
|
||||
)
|
||||
finally:
|
||||
wiretap_handler.close()
|
||||
|
||||
# set returned args back to out data
|
||||
out_data.update({
|
||||
"app_args": app_args
|
||||
})
|
||||
|
||||
# write it out back to the exchange json file
|
||||
with open(json_path, "w") as file_stream:
|
||||
json.dump(out_data, file_stream, indent=4)
|
||||
191
openpype/hosts/flame/utility_scripts/openpype_in_flame.py
Normal file
191
openpype/hosts/flame/utility_scripts/openpype_in_flame.py
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
from __future__ import print_function
|
||||
import sys
|
||||
from Qt import QtWidgets
|
||||
from pprint import pformat
|
||||
import atexit
|
||||
import openpype
|
||||
import avalon
|
||||
import openpype.hosts.flame as opflame
|
||||
|
||||
flh = sys.modules[__name__]
|
||||
flh._project = None
|
||||
|
||||
|
||||
def openpype_install():
|
||||
"""Registering OpenPype in context
|
||||
"""
|
||||
openpype.install()
|
||||
avalon.api.install(opflame)
|
||||
print("Avalon registred hosts: {}".format(
|
||||
avalon.api.registered_host()))
|
||||
|
||||
|
||||
# Exception handler
|
||||
def exeption_handler(exctype, value, _traceback):
|
||||
"""Exception handler for improving UX
|
||||
|
||||
Args:
|
||||
exctype (str): type of exception
|
||||
value (str): exception value
|
||||
tb (str): traceback to show
|
||||
"""
|
||||
import traceback
|
||||
msg = "OpenPype: Python exception {} in {}".format(value, exctype)
|
||||
mbox = QtWidgets.QMessageBox()
|
||||
mbox.setText(msg)
|
||||
mbox.setDetailedText(
|
||||
pformat(traceback.format_exception(exctype, value, _traceback)))
|
||||
mbox.setStyleSheet('QLabel{min-width: 800px;}')
|
||||
mbox.exec_()
|
||||
sys.__excepthook__(exctype, value, _traceback)
|
||||
|
||||
|
||||
# add exception handler into sys module
|
||||
sys.excepthook = exeption_handler
|
||||
|
||||
|
||||
# register clean up logic to be called at Flame exit
|
||||
def cleanup():
|
||||
"""Cleaning up Flame framework context
|
||||
"""
|
||||
if opflame.apps:
|
||||
print('`{}` cleaning up apps:\n {}\n'.format(
|
||||
__file__, pformat(opflame.apps)))
|
||||
while len(opflame.apps):
|
||||
app = opflame.apps.pop()
|
||||
print('`{}` removing : {}'.format(__file__, app.name))
|
||||
del app
|
||||
opflame.apps = []
|
||||
|
||||
if opflame.app_framework:
|
||||
print('PYTHON\t: %s cleaning up' % opflame.app_framework.bundle_name)
|
||||
opflame.app_framework.save_prefs()
|
||||
opflame.app_framework = None
|
||||
|
||||
|
||||
atexit.register(cleanup)
|
||||
|
||||
|
||||
def load_apps():
|
||||
"""Load available apps into Flame framework
|
||||
"""
|
||||
opflame.apps.append(opflame.FlameMenuProjectConnect(opflame.app_framework))
|
||||
opflame.apps.append(opflame.FlameMenuTimeline(opflame.app_framework))
|
||||
opflame.app_framework.log.info("Apps are loaded")
|
||||
|
||||
|
||||
def project_changed_dict(info):
|
||||
"""Hook for project change action
|
||||
|
||||
Args:
|
||||
info (str): info text
|
||||
"""
|
||||
cleanup()
|
||||
|
||||
|
||||
def app_initialized(parent=None):
|
||||
"""Inicialization of Framework
|
||||
|
||||
Args:
|
||||
parent (obj, optional): Parent object. Defaults to None.
|
||||
"""
|
||||
opflame.app_framework = opflame.FlameAppFramework()
|
||||
|
||||
print("{} initializing".format(
|
||||
opflame.app_framework.bundle_name))
|
||||
|
||||
load_apps()
|
||||
|
||||
|
||||
"""
|
||||
Initialisation of the hook is starting from here
|
||||
|
||||
First it needs to test if it can import the flame modul.
|
||||
This will happen only in case a project has been loaded.
|
||||
Then `app_initialized` will load main Framework which will load
|
||||
all menu objects as apps.
|
||||
"""
|
||||
|
||||
try:
|
||||
import flame # noqa
|
||||
app_initialized(parent=None)
|
||||
except ImportError:
|
||||
print("!!!! not able to import flame module !!!!")
|
||||
|
||||
|
||||
def rescan_hooks():
|
||||
import flame # noqa
|
||||
flame.execute_shortcut('Rescan Python Hooks')
|
||||
|
||||
|
||||
def _build_app_menu(app_name):
|
||||
"""Flame menu object generator
|
||||
|
||||
Args:
|
||||
app_name (str): name of menu object app
|
||||
|
||||
Returns:
|
||||
list: menu object
|
||||
"""
|
||||
menu = []
|
||||
|
||||
# first find the relative appname
|
||||
app = None
|
||||
for _app in opflame.apps:
|
||||
if _app.__class__.__name__ == app_name:
|
||||
app = _app
|
||||
|
||||
if app:
|
||||
menu.append(app.build_menu())
|
||||
|
||||
if opflame.app_framework:
|
||||
menu_auto_refresh = opflame.app_framework.prefs_global.get(
|
||||
'menu_auto_refresh', {})
|
||||
if menu_auto_refresh.get('timeline_menu', True):
|
||||
try:
|
||||
import flame # noqa
|
||||
flame.schedule_idle_event(rescan_hooks)
|
||||
except ImportError:
|
||||
print("!-!!! not able to import flame module !!!!")
|
||||
|
||||
return menu
|
||||
|
||||
|
||||
""" Flame hooks are starting here
|
||||
"""
|
||||
|
||||
|
||||
def project_saved(project_name, save_time, is_auto_save):
|
||||
"""Hook to activate when project is saved
|
||||
|
||||
Args:
|
||||
project_name (str): name of project
|
||||
save_time (str): time when it was saved
|
||||
is_auto_save (bool): autosave is on or off
|
||||
"""
|
||||
if opflame.app_framework:
|
||||
opflame.app_framework.save_prefs()
|
||||
|
||||
|
||||
def get_main_menu_custom_ui_actions():
|
||||
"""Hook to create submenu in start menu
|
||||
|
||||
Returns:
|
||||
list: menu object
|
||||
"""
|
||||
# install openpype and the host
|
||||
openpype_install()
|
||||
|
||||
return _build_app_menu("FlameMenuProjectConnect")
|
||||
|
||||
|
||||
def get_timeline_custom_ui_actions():
|
||||
"""Hook to create submenu in timeline
|
||||
|
||||
Returns:
|
||||
list: menu object
|
||||
"""
|
||||
# install openpype and the host
|
||||
openpype_install()
|
||||
|
||||
return _build_app_menu("FlameMenuTimeline")
|
||||
|
|
@ -437,7 +437,8 @@ def empty_sets(sets, force=False):
|
|||
cmds.connectAttr(src, dest)
|
||||
|
||||
# Restore original members
|
||||
for origin_set, members in original.iteritems():
|
||||
_iteritems = getattr(original, "iteritems", original.items)
|
||||
for origin_set, members in _iteritems():
|
||||
cmds.sets(members, forceElement=origin_set)
|
||||
|
||||
|
||||
|
|
@ -581,7 +582,7 @@ def get_shader_assignments_from_shapes(shapes, components=True):
|
|||
|
||||
# Build a mapping from parent to shapes to include in lookup.
|
||||
transforms = {shape.rsplit("|", 1)[0]: shape for shape in shapes}
|
||||
lookup = set(shapes + transforms.keys())
|
||||
lookup = set(shapes) | set(transforms.keys())
|
||||
|
||||
component_assignments = defaultdict(list)
|
||||
for shading_group in assignments.keys():
|
||||
|
|
@ -669,7 +670,8 @@ def displaySmoothness(nodes,
|
|||
yield
|
||||
finally:
|
||||
# Revert state
|
||||
for node, state in originals.iteritems():
|
||||
_iteritems = getattr(originals, "iteritems", originals.items)
|
||||
for node, state in _iteritems():
|
||||
if state:
|
||||
cmds.displaySmoothness(node, **state)
|
||||
|
||||
|
|
@ -712,7 +714,8 @@ def no_display_layers(nodes):
|
|||
yield
|
||||
finally:
|
||||
# Restore original members
|
||||
for layer, members in original.iteritems():
|
||||
_iteritems = getattr(original, "iteritems", original.items)
|
||||
for layer, members in _iteritems():
|
||||
cmds.editDisplayLayerMembers(layer, members, noRecurse=True)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -21,10 +21,8 @@ def _get_menu(menu_name=None):
|
|||
if menu_name is None:
|
||||
menu_name = pipeline._menu
|
||||
|
||||
widgets = dict((
|
||||
w.objectName(), w) for w in QtWidgets.QApplication.allWidgets())
|
||||
menu = widgets.get(menu_name)
|
||||
return menu
|
||||
widgets = {w.objectName(): w for w in QtWidgets.QApplication.allWidgets()}
|
||||
return widgets.get(menu_name)
|
||||
|
||||
|
||||
def deferred():
|
||||
|
|
@ -46,6 +44,43 @@ def deferred():
|
|||
)
|
||||
)
|
||||
|
||||
def add_experimental_item():
|
||||
cmds.menuItem(
|
||||
"Experimental tools...",
|
||||
parent=pipeline._menu,
|
||||
command=lambda *args: host_tools.show_experimental_tools_dialog(
|
||||
pipeline._parent
|
||||
)
|
||||
)
|
||||
|
||||
def add_scripts_menu():
|
||||
try:
|
||||
import scriptsmenu.launchformaya as launchformaya
|
||||
except ImportError:
|
||||
log.warning(
|
||||
"Skipping studio.menu install, because "
|
||||
"'scriptsmenu' module seems unavailable."
|
||||
)
|
||||
return
|
||||
|
||||
# load configuration of custom menu
|
||||
project_settings = get_project_settings(os.getenv("AVALON_PROJECT"))
|
||||
config = project_settings["maya"]["scriptsmenu"]["definition"]
|
||||
_menu = project_settings["maya"]["scriptsmenu"]["name"]
|
||||
|
||||
if not config:
|
||||
log.warning("Skipping studio menu, no definition found.")
|
||||
return
|
||||
|
||||
# run the launcher for Maya menu
|
||||
studio_menu = launchformaya.main(
|
||||
title=_menu.title(),
|
||||
objectName=_menu.title().lower().replace(" ", "_")
|
||||
)
|
||||
|
||||
# apply configuration
|
||||
studio_menu.build_from_configuration(studio_menu, config)
|
||||
|
||||
def modify_workfiles():
|
||||
# Find the pipeline menu
|
||||
top_menu = _get_menu()
|
||||
|
|
@ -101,38 +136,13 @@ def deferred():
|
|||
|
||||
log.info("Attempting to install scripts menu ...")
|
||||
|
||||
# add_scripts_menu()
|
||||
add_build_workfiles_item()
|
||||
add_look_assigner_item()
|
||||
add_experimental_item()
|
||||
modify_workfiles()
|
||||
remove_project_manager()
|
||||
|
||||
try:
|
||||
import scriptsmenu.launchformaya as launchformaya
|
||||
import scriptsmenu.scriptsmenu as scriptsmenu
|
||||
except ImportError:
|
||||
log.warning(
|
||||
"Skipping studio.menu install, because "
|
||||
"'scriptsmenu' module seems unavailable."
|
||||
)
|
||||
return
|
||||
|
||||
# load configuration of custom menu
|
||||
project_settings = get_project_settings(os.getenv("AVALON_PROJECT"))
|
||||
config = project_settings["maya"]["scriptsmenu"]["definition"]
|
||||
_menu = project_settings["maya"]["scriptsmenu"]["name"]
|
||||
|
||||
if not config:
|
||||
log.warning("Skipping studio menu, no definition found.")
|
||||
return
|
||||
|
||||
# run the launcher for Maya menu
|
||||
studio_menu = launchformaya.main(
|
||||
title=_menu.title(),
|
||||
objectName=_menu.title().lower().replace(" ", "_")
|
||||
)
|
||||
|
||||
# apply configuration
|
||||
studio_menu.build_from_configuration(studio_menu, config)
|
||||
add_scripts_menu()
|
||||
|
||||
|
||||
def uninstall():
|
||||
|
|
@ -153,7 +163,7 @@ def install():
|
|||
return
|
||||
|
||||
# Allow time for uninstallation to finish.
|
||||
cmds.evalDeferred(deferred)
|
||||
cmds.evalDeferred(deferred, lowestPriority=True)
|
||||
|
||||
|
||||
def popup():
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import os
|
|||
import contextlib
|
||||
import copy
|
||||
|
||||
import six
|
||||
from maya import cmds
|
||||
|
||||
from avalon import api, io
|
||||
|
|
@ -69,7 +70,8 @@ def unlocked(nodes):
|
|||
yield
|
||||
finally:
|
||||
# Reapply original states
|
||||
for uuid, state in states.iteritems():
|
||||
_iteritems = getattr(states, "iteritems", states.items)
|
||||
for uuid, state in _iteritems():
|
||||
nodes_from_id = cmds.ls(uuid, long=True)
|
||||
if nodes_from_id:
|
||||
node = nodes_from_id[0]
|
||||
|
|
@ -94,7 +96,7 @@ def load_package(filepath, name, namespace=None):
|
|||
# Define a unique namespace for the package
|
||||
namespace = os.path.basename(filepath).split(".")[0]
|
||||
unique_namespace(namespace)
|
||||
assert isinstance(namespace, basestring)
|
||||
assert isinstance(namespace, six.string_types)
|
||||
|
||||
# Load the setdress package data
|
||||
with open(filepath, "r") as fp:
|
||||
|
|
|
|||
|
|
@ -183,7 +183,8 @@ class ExtractFBX(openpype.api.Extractor):
|
|||
# Apply the FBX overrides through MEL since the commands
|
||||
# only work correctly in MEL according to online
|
||||
# available discussions on the topic
|
||||
for option, value in options.iteritems():
|
||||
_iteritems = getattr(options, "iteritems", options.items)
|
||||
for option, value in _iteritems():
|
||||
key = option[0].upper() + option[1:] # uppercase first letter
|
||||
|
||||
# Boolean must be passed as lower-case strings
|
||||
|
|
|
|||
|
|
@ -383,7 +383,7 @@ class MayaSubmitMuster(pyblish.api.InstancePlugin):
|
|||
"attributes": {
|
||||
"environmental_variables": {
|
||||
"value": ", ".join("{!s}={!r}".format(k, v)
|
||||
for (k, v) in env.iteritems()),
|
||||
for (k, v) in env.items()),
|
||||
|
||||
"state": True,
|
||||
"subst": False
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ import pyblish.api
|
|||
import openpype.api
|
||||
import string
|
||||
|
||||
import six
|
||||
|
||||
# Allow only characters, numbers and underscore
|
||||
allowed = set(string.ascii_lowercase +
|
||||
string.ascii_uppercase +
|
||||
|
|
@ -29,7 +31,7 @@ class ValidateSubsetName(pyblish.api.InstancePlugin):
|
|||
raise RuntimeError("Instance is missing subset "
|
||||
"name: {0}".format(subset))
|
||||
|
||||
if not isinstance(subset, basestring):
|
||||
if not isinstance(subset, six.string_types):
|
||||
raise TypeError("Instance subset name must be string, "
|
||||
"got: {0} ({1})".format(subset, type(subset)))
|
||||
|
||||
|
|
|
|||
|
|
@ -52,7 +52,8 @@ class ValidateNodeIdsUnique(pyblish.api.InstancePlugin):
|
|||
|
||||
# Take only the ids with more than one member
|
||||
invalid = list()
|
||||
for _ids, members in ids.iteritems():
|
||||
_iteritems = getattr(ids, "iteritems", ids.items)
|
||||
for _ids, members in _iteritems():
|
||||
if len(members) > 1:
|
||||
cls.log.error("ID found on multiple nodes: '%s'" % members)
|
||||
invalid.extend(members)
|
||||
|
|
|
|||
|
|
@ -32,7 +32,10 @@ class ValidateNodeNoGhosting(pyblish.api.InstancePlugin):
|
|||
nodes = cmds.ls(instance, long=True, type=['transform', 'shape'])
|
||||
invalid = []
|
||||
for node in nodes:
|
||||
for attr, required_value in cls._attributes.iteritems():
|
||||
_iteritems = getattr(
|
||||
cls._attributes, "iteritems", cls._attributes.items
|
||||
)
|
||||
for attr, required_value in _iteritems():
|
||||
if cmds.attributeQuery(attr, node=node, exists=True):
|
||||
|
||||
value = cmds.getAttr('{0}.{1}'.format(node, attr))
|
||||
|
|
|
|||
|
|
@ -33,7 +33,8 @@ class ValidateShapeRenderStats(pyblish.api.Validator):
|
|||
shapes = cmds.ls(instance, long=True, type='surfaceShape')
|
||||
invalid = []
|
||||
for shape in shapes:
|
||||
for attr, default_value in cls.defaults.iteritems():
|
||||
_iteritems = getattr(cls.defaults, "iteritems", cls.defaults.items)
|
||||
for attr, default_value in _iteritems():
|
||||
if cmds.attributeQuery(attr, node=shape, exists=True):
|
||||
value = cmds.getAttr('{}.{}'.format(shape, attr))
|
||||
if value != default_value:
|
||||
|
|
@ -52,7 +53,8 @@ class ValidateShapeRenderStats(pyblish.api.Validator):
|
|||
@classmethod
|
||||
def repair(cls, instance):
|
||||
for shape in cls.get_invalid(instance):
|
||||
for attr, default_value in cls.defaults.iteritems():
|
||||
_iteritems = getattr(cls.defaults, "iteritems", cls.defaults.items)
|
||||
for attr, default_value in _iteritems():
|
||||
|
||||
if cmds.attributeQuery(attr, node=shape, exists=True):
|
||||
plug = '{0}.{1}'.format(shape, attr)
|
||||
|
|
|
|||
|
|
@ -84,6 +84,12 @@ def install():
|
|||
)
|
||||
log.debug("Adding menu item: {}".format(name))
|
||||
|
||||
# Add experimental tools action
|
||||
menu.addSeparator()
|
||||
menu.addCommand(
|
||||
"Experimental tools...",
|
||||
host_tools.show_experimental_tools_dialog
|
||||
)
|
||||
|
||||
# adding shortcuts
|
||||
add_shortcuts_from_presets()
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ from openpype.hosts.photoshop.plugins.lib import get_unique_layer_name
|
|||
|
||||
stub = photoshop.stub()
|
||||
|
||||
|
||||
class ImageLoader(api.Loader):
|
||||
"""Load images
|
||||
|
||||
|
|
@ -21,7 +20,7 @@ class ImageLoader(api.Loader):
|
|||
context["asset"]["name"],
|
||||
name)
|
||||
with photoshop.maintained_selection():
|
||||
layer = stub.import_smart_object(self.fname, layer_name)
|
||||
layer = self.import_layer(self.fname, layer_name)
|
||||
|
||||
self[:] = [layer]
|
||||
namespace = namespace or layer_name
|
||||
|
|
@ -45,8 +44,9 @@ class ImageLoader(api.Loader):
|
|||
layer_name = "{}_{}".format(context["asset"], context["subset"])
|
||||
# switching assets
|
||||
if namespace_from_container != layer_name:
|
||||
layer_name = self._get_unique_layer_name(context["asset"],
|
||||
context["subset"])
|
||||
layer_name = get_unique_layer_name(stub.get_layers(),
|
||||
context["asset"],
|
||||
context["subset"])
|
||||
else: # switching version - keep same name
|
||||
layer_name = container["namespace"]
|
||||
|
||||
|
|
@ -72,3 +72,6 @@ class ImageLoader(api.Loader):
|
|||
|
||||
def switch(self, container, representation):
|
||||
self.update(container, representation)
|
||||
|
||||
def import_layer(self, file_name, layer_name):
|
||||
return stub.import_smart_object(file_name, layer_name)
|
||||
|
|
|
|||
82
openpype/hosts/photoshop/plugins/load/load_reference.py
Normal file
82
openpype/hosts/photoshop/plugins/load/load_reference.py
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
import re
|
||||
|
||||
from avalon import api, photoshop
|
||||
|
||||
from openpype.hosts.photoshop.plugins.lib import get_unique_layer_name
|
||||
|
||||
stub = photoshop.stub()
|
||||
|
||||
|
||||
class ReferenceLoader(api.Loader):
|
||||
"""Load reference images
|
||||
|
||||
Stores the imported asset in a container named after the asset.
|
||||
|
||||
Inheriting from 'load_image' didn't work because of
|
||||
"Cannot write to closing transport", possible refactor.
|
||||
"""
|
||||
|
||||
families = ["image", "render"]
|
||||
representations = ["*"]
|
||||
|
||||
def load(self, context, name=None, namespace=None, data=None):
|
||||
layer_name = get_unique_layer_name(stub.get_layers(),
|
||||
context["asset"]["name"],
|
||||
name)
|
||||
with photoshop.maintained_selection():
|
||||
layer = self.import_layer(self.fname, layer_name)
|
||||
|
||||
self[:] = [layer]
|
||||
namespace = namespace or layer_name
|
||||
|
||||
return photoshop.containerise(
|
||||
name,
|
||||
namespace,
|
||||
layer,
|
||||
context,
|
||||
self.__class__.__name__
|
||||
)
|
||||
|
||||
def update(self, container, representation):
|
||||
""" Switch asset or change version """
|
||||
layer = container.pop("layer")
|
||||
|
||||
context = representation.get("context", {})
|
||||
|
||||
namespace_from_container = re.sub(r'_\d{3}$', '',
|
||||
container["namespace"])
|
||||
layer_name = "{}_{}".format(context["asset"], context["subset"])
|
||||
# switching assets
|
||||
if namespace_from_container != layer_name:
|
||||
layer_name = get_unique_layer_name(stub.get_layers(),
|
||||
context["asset"],
|
||||
context["subset"])
|
||||
else: # switching version - keep same name
|
||||
layer_name = container["namespace"]
|
||||
|
||||
path = api.get_representation_path(representation)
|
||||
with photoshop.maintained_selection():
|
||||
stub.replace_smart_object(
|
||||
layer, path, layer_name
|
||||
)
|
||||
|
||||
stub.imprint(
|
||||
layer, {"representation": str(representation["_id"])}
|
||||
)
|
||||
|
||||
def remove(self, container):
|
||||
"""
|
||||
Removes element from scene: deletes layer + removes from Headline
|
||||
Args:
|
||||
container (dict): container to be removed - used to get layer_id
|
||||
"""
|
||||
layer = container.pop("layer")
|
||||
stub.imprint(layer, {})
|
||||
stub.delete_layer(layer.id)
|
||||
|
||||
def switch(self, container, representation):
|
||||
self.update(container, representation)
|
||||
|
||||
def import_layer(self, file_name, layer_name):
|
||||
return stub.import_smart_object(file_name, layer_name,
|
||||
as_reference=True)
|
||||
30
openpype/hosts/photoshop/plugins/publish/closePS.py
Normal file
30
openpype/hosts/photoshop/plugins/publish/closePS.py
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Close PS after publish. For Webpublishing only."""
|
||||
import os
|
||||
|
||||
import pyblish.api
|
||||
|
||||
from avalon import photoshop
|
||||
|
||||
|
||||
class ClosePS(pyblish.api.ContextPlugin):
|
||||
"""Close PS after publish. For Webpublishing only.
|
||||
"""
|
||||
|
||||
order = pyblish.api.IntegratorOrder + 14
|
||||
label = "Close PS"
|
||||
optional = True
|
||||
active = True
|
||||
|
||||
hosts = ["photoshop"]
|
||||
|
||||
def process(self, context):
|
||||
self.log.info("ClosePS")
|
||||
if not os.environ.get("IS_HEADLESS"):
|
||||
return
|
||||
|
||||
stub = photoshop.stub()
|
||||
self.log.info("Shutting down PS")
|
||||
stub.save()
|
||||
stub.close()
|
||||
self.log.info("PS closed")
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
import pyblish.api
|
||||
import os
|
||||
import re
|
||||
|
||||
from avalon import photoshop
|
||||
from openpype.lib import prepare_template_data
|
||||
from openpype.lib.plugin_tools import parse_json
|
||||
|
||||
|
||||
class CollectRemoteInstances(pyblish.api.ContextPlugin):
|
||||
"""Gather instances configured color code of a layer.
|
||||
|
||||
Used in remote publishing when artists marks publishable layers by color-
|
||||
coding.
|
||||
|
||||
Identifier:
|
||||
id (str): "pyblish.avalon.instance"
|
||||
"""
|
||||
order = pyblish.api.CollectorOrder + 0.100
|
||||
|
||||
label = "Instances"
|
||||
order = pyblish.api.CollectorOrder
|
||||
hosts = ["photoshop"]
|
||||
|
||||
# configurable by Settings
|
||||
color_code_mapping = []
|
||||
|
||||
def process(self, context):
|
||||
self.log.info("CollectRemoteInstances")
|
||||
self.log.info("mapping:: {}".format(self.color_code_mapping))
|
||||
if not os.environ.get("IS_HEADLESS"):
|
||||
self.log.debug("Not headless publishing, skipping.")
|
||||
return
|
||||
|
||||
# parse variant if used in webpublishing, comes from webpublisher batch
|
||||
batch_dir = os.environ.get("OPENPYPE_PUBLISH_DATA")
|
||||
variant = "Main"
|
||||
if batch_dir and os.path.exists(batch_dir):
|
||||
# TODO check if batch manifest is same as tasks manifests
|
||||
task_data = parse_json(os.path.join(batch_dir,
|
||||
"manifest.json"))
|
||||
if not task_data:
|
||||
raise ValueError(
|
||||
"Cannot parse batch meta in {} folder".format(batch_dir))
|
||||
variant = task_data["variant"]
|
||||
|
||||
stub = photoshop.stub()
|
||||
layers = stub.get_layers()
|
||||
|
||||
instance_names = []
|
||||
for layer in layers:
|
||||
self.log.info("Layer:: {}".format(layer))
|
||||
resolved_family, resolved_subset_template = self._resolve_mapping(
|
||||
layer
|
||||
)
|
||||
self.log.info("resolved_family {}".format(resolved_family))
|
||||
self.log.info("resolved_subset_template {}".format(
|
||||
resolved_subset_template))
|
||||
|
||||
if not resolved_subset_template or not resolved_family:
|
||||
self.log.debug("!!! Not marked, skip")
|
||||
continue
|
||||
|
||||
if layer.parents:
|
||||
self.log.debug("!!! Not a top layer, skip")
|
||||
continue
|
||||
|
||||
instance = context.create_instance(layer.name)
|
||||
instance.append(layer)
|
||||
instance.data["family"] = resolved_family
|
||||
instance.data["publish"] = layer.visible
|
||||
instance.data["asset"] = context.data["assetEntity"]["name"]
|
||||
instance.data["task"] = context.data["taskType"]
|
||||
|
||||
fill_pairs = {
|
||||
"variant": variant,
|
||||
"family": instance.data["family"],
|
||||
"task": instance.data["task"],
|
||||
"layer": layer.name
|
||||
}
|
||||
subset = resolved_subset_template.format(
|
||||
**prepare_template_data(fill_pairs))
|
||||
instance.data["subset"] = subset
|
||||
|
||||
instance_names.append(layer.name)
|
||||
|
||||
# Produce diagnostic message for any graphical
|
||||
# user interface interested in visualising it.
|
||||
self.log.info("Found: \"%s\" " % instance.data["name"])
|
||||
self.log.info("instance: {} ".format(instance.data))
|
||||
|
||||
if len(instance_names) != len(set(instance_names)):
|
||||
self.log.warning("Duplicate instances found. " +
|
||||
"Remove unwanted via SubsetManager")
|
||||
|
||||
def _resolve_mapping(self, layer):
|
||||
"""Matches 'layer' color code and name to mapping.
|
||||
|
||||
If both color code AND name regex is configured, BOTH must be valid
|
||||
If layer matches to multiple mappings, only first is used!
|
||||
"""
|
||||
family_list = []
|
||||
family = None
|
||||
subset_name_list = []
|
||||
resolved_subset_template = None
|
||||
for mapping in self.color_code_mapping:
|
||||
if mapping["color_code"] and \
|
||||
layer.color_code not in mapping["color_code"]:
|
||||
continue
|
||||
|
||||
if mapping["layer_name_regex"] and \
|
||||
not any(re.search(pattern, layer.name)
|
||||
for pattern in mapping["layer_name_regex"]):
|
||||
continue
|
||||
|
||||
family_list.append(mapping["family"])
|
||||
subset_name_list.append(mapping["subset_template_name"])
|
||||
|
||||
if len(subset_name_list) > 1:
|
||||
self.log.warning("Multiple mappings found for '{}'".
|
||||
format(layer.name))
|
||||
self.log.warning("Only first subset name template used!")
|
||||
subset_name_list[:] = subset_name_list[0]
|
||||
|
||||
if len(family_list) > 1:
|
||||
self.log.warning("Multiple mappings found for '{}'".
|
||||
format(layer.name))
|
||||
self.log.warning("Only first family used!")
|
||||
family_list[:] = family_list[0]
|
||||
if subset_name_list:
|
||||
resolved_subset_template = subset_name_list.pop()
|
||||
if family_list:
|
||||
family = family_list.pop()
|
||||
|
||||
return family, resolved_subset_template
|
||||
|
|
@ -12,7 +12,7 @@ class ExtractImage(openpype.api.Extractor):
|
|||
|
||||
label = "Extract Image"
|
||||
hosts = ["photoshop"]
|
||||
families = ["image"]
|
||||
families = ["image", "background"]
|
||||
formats = ["png", "jpg"]
|
||||
|
||||
def process(self, instance):
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
import pyblish.api
|
||||
import openpype.api
|
||||
|
||||
import os
|
||||
|
||||
|
||||
class ValidateSources(pyblish.api.InstancePlugin):
|
||||
"""Validates source files.
|
||||
|
||||
Loops through all 'files' in 'stagingDir' if actually exist. They might
|
||||
got deleted between starting of SP and now.
|
||||
|
||||
"""
|
||||
|
||||
order = openpype.api.ValidateContentsOrder
|
||||
label = "Check source files"
|
||||
|
||||
optional = True # only for unforeseeable cases
|
||||
|
||||
hosts = ["standalonepublisher"]
|
||||
|
||||
def process(self, instance):
|
||||
self.log.info("instance {}".format(instance.data))
|
||||
|
||||
for repre in instance.data.get("representations") or []:
|
||||
files = []
|
||||
if isinstance(repre["files"], str):
|
||||
files.append(repre["files"])
|
||||
else:
|
||||
files = list(repre["files"])
|
||||
|
||||
for file_name in files:
|
||||
source_file = os.path.join(repre["stagingDir"],
|
||||
file_name)
|
||||
|
||||
if not os.path.exists(source_file):
|
||||
raise ValueError("File {} not found".format(source_file))
|
||||
|
|
@ -253,6 +253,7 @@ def create_unreal_project(project_name: str,
|
|||
"Plugins": [
|
||||
{"Name": "PythonScriptPlugin", "Enabled": True},
|
||||
{"Name": "EditorScriptingUtilities", "Enabled": True},
|
||||
{"Name": "SequencerScripting", "Enabled": True},
|
||||
{"Name": "Avalon", "Enabled": True}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,9 @@ from pathlib import Path
|
|||
from openpype.lib import (
|
||||
PreLaunchHook,
|
||||
ApplicationLaunchFailed,
|
||||
ApplicationNotFound
|
||||
ApplicationNotFound,
|
||||
get_workdir_data,
|
||||
get_workfile_template_key
|
||||
)
|
||||
from openpype.hosts.unreal.api import lib as unreal_lib
|
||||
|
||||
|
|
@ -25,13 +27,46 @@ class UnrealPrelaunchHook(PreLaunchHook):
|
|||
|
||||
self.signature = "( {} )".format(self.__class__.__name__)
|
||||
|
||||
def _get_work_filename(self):
|
||||
# Use last workfile if was found
|
||||
if self.data.get("last_workfile_path"):
|
||||
last_workfile = Path(self.data.get("last_workfile_path"))
|
||||
if last_workfile and last_workfile.exists():
|
||||
return last_workfile.name
|
||||
|
||||
# Prepare data for fill data and for getting workfile template key
|
||||
task_name = self.data["task_name"]
|
||||
anatomy = self.data["anatomy"]
|
||||
asset_doc = self.data["asset_doc"]
|
||||
project_doc = self.data["project_doc"]
|
||||
|
||||
asset_tasks = asset_doc.get("data", {}).get("tasks") or {}
|
||||
task_info = asset_tasks.get(task_name) or {}
|
||||
task_type = task_info.get("type")
|
||||
|
||||
workdir_data = get_workdir_data(
|
||||
project_doc, asset_doc, task_name, self.host_name
|
||||
)
|
||||
# QUESTION raise exception if version is part of filename template?
|
||||
workdir_data["version"] = 1
|
||||
workdir_data["ext"] = "uproject"
|
||||
|
||||
# Get workfile template key for current context
|
||||
workfile_template_key = get_workfile_template_key(
|
||||
task_type,
|
||||
self.host_name,
|
||||
project_name=project_doc["name"]
|
||||
)
|
||||
# Fill templates
|
||||
filled_anatomy = anatomy.format(workdir_data)
|
||||
|
||||
# Return filename
|
||||
return filled_anatomy[workfile_template_key]["file"]
|
||||
|
||||
def execute(self):
|
||||
"""Hook entry method."""
|
||||
asset_name = self.data["asset_name"]
|
||||
task_name = self.data["task_name"]
|
||||
workdir = self.launch_context.env["AVALON_WORKDIR"]
|
||||
engine_version = self.app_name.split("/")[-1].replace("-", ".")
|
||||
unreal_project_name = f"{asset_name}_{task_name}"
|
||||
try:
|
||||
if int(engine_version.split(".")[0]) < 4 and \
|
||||
int(engine_version.split(".")[1]) < 26:
|
||||
|
|
@ -45,6 +80,8 @@ class UnrealPrelaunchHook(PreLaunchHook):
|
|||
# so lets keep it quite.
|
||||
...
|
||||
|
||||
unreal_project_filename = self._get_work_filename()
|
||||
unreal_project_name = os.path.splitext(unreal_project_filename)[0]
|
||||
# Unreal is sensitive about project names longer then 20 chars
|
||||
if len(unreal_project_name) > 20:
|
||||
self.log.warning((
|
||||
|
|
@ -89,10 +126,10 @@ class UnrealPrelaunchHook(PreLaunchHook):
|
|||
ue4_path = unreal_lib.get_editor_executable_path(
|
||||
Path(detected[engine_version]))
|
||||
|
||||
self.launch_context.launch_args.append(ue4_path.as_posix())
|
||||
self.launch_context.launch_args = [ue4_path.as_posix()]
|
||||
project_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
project_file = project_path / f"{unreal_project_name}.uproject"
|
||||
project_file = project_path / unreal_project_filename
|
||||
if not project_file.is_file():
|
||||
engine_path = detected[engine_version]
|
||||
self.log.info((
|
||||
|
|
|
|||
43
openpype/hosts/unreal/plugins/create/create_camera.py
Normal file
43
openpype/hosts/unreal/plugins/create/create_camera.py
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
import unreal
|
||||
from unreal import EditorAssetLibrary as eal
|
||||
from unreal import EditorLevelLibrary as ell
|
||||
|
||||
from openpype.hosts.unreal.api.plugin import Creator
|
||||
from avalon.unreal import (
|
||||
instantiate,
|
||||
)
|
||||
|
||||
|
||||
class CreateCamera(Creator):
|
||||
"""Layout output for character rigs"""
|
||||
|
||||
name = "layoutMain"
|
||||
label = "Camera"
|
||||
family = "camera"
|
||||
icon = "cubes"
|
||||
|
||||
root = "/Game/Avalon/Instances"
|
||||
suffix = "_INS"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(CreateCamera, self).__init__(*args, **kwargs)
|
||||
|
||||
def process(self):
|
||||
data = self.data
|
||||
|
||||
name = data["subset"]
|
||||
|
||||
data["level"] = ell.get_editor_world().get_path_name()
|
||||
|
||||
if not eal.does_directory_exist(self.root):
|
||||
eal.make_directory(self.root)
|
||||
|
||||
factory = unreal.LevelSequenceFactoryNew()
|
||||
tools = unreal.AssetToolsHelpers().get_asset_tools()
|
||||
tools.create_asset(name, f"{self.root}/{name}", None, factory)
|
||||
|
||||
asset_name = f"{self.root}/{name}/{name}.{name}"
|
||||
|
||||
data["members"] = [asset_name]
|
||||
|
||||
instantiate(f"{self.root}", name, data, None, self.suffix)
|
||||
206
openpype/hosts/unreal/plugins/load/load_camera.py
Normal file
206
openpype/hosts/unreal/plugins/load/load_camera.py
Normal file
|
|
@ -0,0 +1,206 @@
|
|||
import os
|
||||
|
||||
from avalon import api, io, pipeline
|
||||
from avalon.unreal import lib
|
||||
from avalon.unreal import pipeline as unreal_pipeline
|
||||
import unreal
|
||||
|
||||
|
||||
class CameraLoader(api.Loader):
|
||||
"""Load Unreal StaticMesh from FBX"""
|
||||
|
||||
families = ["camera"]
|
||||
label = "Load Camera"
|
||||
representations = ["fbx"]
|
||||
icon = "cube"
|
||||
color = "orange"
|
||||
|
||||
def load(self, context, name, namespace, data):
|
||||
"""
|
||||
Load and containerise representation into Content Browser.
|
||||
|
||||
This is two step process. First, import FBX to temporary path and
|
||||
then call `containerise()` on it - this moves all content to new
|
||||
directory and then it will create AssetContainer there and imprint it
|
||||
with metadata. This will mark this path as container.
|
||||
|
||||
Args:
|
||||
context (dict): application context
|
||||
name (str): subset name
|
||||
namespace (str): in Unreal this is basically path to container.
|
||||
This is not passed here, so namespace is set
|
||||
by `containerise()` because only then we know
|
||||
real path.
|
||||
data (dict): Those would be data to be imprinted. This is not used
|
||||
now, data are imprinted by `containerise()`.
|
||||
|
||||
Returns:
|
||||
list(str): list of container content
|
||||
"""
|
||||
|
||||
# Create directory for asset and avalon container
|
||||
root = "/Game/Avalon/Assets"
|
||||
asset = context.get('asset').get('name')
|
||||
suffix = "_CON"
|
||||
if asset:
|
||||
asset_name = "{}_{}".format(asset, name)
|
||||
else:
|
||||
asset_name = "{}".format(name)
|
||||
|
||||
tools = unreal.AssetToolsHelpers().get_asset_tools()
|
||||
|
||||
unique_number = 1
|
||||
|
||||
if unreal.EditorAssetLibrary.does_directory_exist(f"{root}/{asset}"):
|
||||
asset_content = unreal.EditorAssetLibrary.list_assets(
|
||||
f"{root}/{asset}", recursive=False, include_folder=True
|
||||
)
|
||||
|
||||
# Get highest number to make a unique name
|
||||
folders = [a for a in asset_content
|
||||
if a[-1] == "/" and f"{name}_" in a]
|
||||
f_numbers = []
|
||||
for f in folders:
|
||||
# Get number from folder name. Splits the string by "_" and
|
||||
# removes the last element (which is a "/").
|
||||
f_numbers.append(int(f.split("_")[-1][:-1]))
|
||||
f_numbers.sort()
|
||||
if not f_numbers:
|
||||
unique_number = 1
|
||||
else:
|
||||
unique_number = f_numbers[-1] + 1
|
||||
|
||||
asset_dir, container_name = tools.create_unique_asset_name(
|
||||
f"{root}/{asset}/{name}_{unique_number:02d}", suffix="")
|
||||
|
||||
container_name += suffix
|
||||
|
||||
unreal.EditorAssetLibrary.make_directory(asset_dir)
|
||||
|
||||
sequence = tools.create_asset(
|
||||
asset_name=asset_name,
|
||||
package_path=asset_dir,
|
||||
asset_class=unreal.LevelSequence,
|
||||
factory=unreal.LevelSequenceFactoryNew()
|
||||
)
|
||||
|
||||
io_asset = io.Session["AVALON_ASSET"]
|
||||
asset_doc = io.find_one({
|
||||
"type": "asset",
|
||||
"name": io_asset
|
||||
})
|
||||
|
||||
data = asset_doc.get("data")
|
||||
|
||||
if data:
|
||||
sequence.set_display_rate(unreal.FrameRate(data.get("fps"), 1.0))
|
||||
sequence.set_playback_start(data.get("frameStart"))
|
||||
sequence.set_playback_end(data.get("frameEnd"))
|
||||
|
||||
settings = unreal.MovieSceneUserImportFBXSettings()
|
||||
settings.set_editor_property('reduce_keys', False)
|
||||
|
||||
unreal.SequencerTools.import_fbx(
|
||||
unreal.EditorLevelLibrary.get_editor_world(),
|
||||
sequence,
|
||||
sequence.get_bindings(),
|
||||
settings,
|
||||
self.fname
|
||||
)
|
||||
|
||||
# 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,
|
||||
"asset_name": asset_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
|
||||
)
|
||||
|
||||
for a in asset_content:
|
||||
unreal.EditorAssetLibrary.save_asset(a)
|
||||
|
||||
return asset_content
|
||||
|
||||
def update(self, container, representation):
|
||||
path = container["namespace"]
|
||||
|
||||
ar = unreal.AssetRegistryHelpers.get_asset_registry()
|
||||
tools = unreal.AssetToolsHelpers().get_asset_tools()
|
||||
|
||||
asset_content = unreal.EditorAssetLibrary.list_assets(
|
||||
path, recursive=False, include_folder=False
|
||||
)
|
||||
asset_name = ""
|
||||
for a in asset_content:
|
||||
asset = ar.get_asset_by_object_path(a)
|
||||
if a.endswith("_CON"):
|
||||
loaded_asset = unreal.EditorAssetLibrary.load_asset(a)
|
||||
unreal.EditorAssetLibrary.set_metadata_tag(
|
||||
loaded_asset, "representation", str(representation["_id"])
|
||||
)
|
||||
unreal.EditorAssetLibrary.set_metadata_tag(
|
||||
loaded_asset, "parent", str(representation["parent"])
|
||||
)
|
||||
asset_name = unreal.EditorAssetLibrary.get_metadata_tag(
|
||||
loaded_asset, "asset_name"
|
||||
)
|
||||
elif asset.asset_class == "LevelSequence":
|
||||
unreal.EditorAssetLibrary.delete_asset(a)
|
||||
|
||||
sequence = tools.create_asset(
|
||||
asset_name=asset_name,
|
||||
package_path=path,
|
||||
asset_class=unreal.LevelSequence,
|
||||
factory=unreal.LevelSequenceFactoryNew()
|
||||
)
|
||||
|
||||
io_asset = io.Session["AVALON_ASSET"]
|
||||
asset_doc = io.find_one({
|
||||
"type": "asset",
|
||||
"name": io_asset
|
||||
})
|
||||
|
||||
data = asset_doc.get("data")
|
||||
|
||||
if data:
|
||||
sequence.set_display_rate(unreal.FrameRate(data.get("fps"), 1.0))
|
||||
sequence.set_playback_start(data.get("frameStart"))
|
||||
sequence.set_playback_end(data.get("frameEnd"))
|
||||
|
||||
settings = unreal.MovieSceneUserImportFBXSettings()
|
||||
settings.set_editor_property('reduce_keys', False)
|
||||
|
||||
unreal.SequencerTools.import_fbx(
|
||||
unreal.EditorLevelLibrary.get_editor_world(),
|
||||
sequence,
|
||||
sequence.get_bindings(),
|
||||
settings,
|
||||
str(representation["data"]["path"])
|
||||
)
|
||||
|
||||
def remove(self, container):
|
||||
path = container["namespace"]
|
||||
parent_path = os.path.dirname(path)
|
||||
|
||||
unreal.EditorAssetLibrary.delete_directory(path)
|
||||
|
||||
asset_content = unreal.EditorAssetLibrary.list_assets(
|
||||
parent_path, recursive=False, include_folder=True
|
||||
)
|
||||
|
||||
if len(asset_content) == 0:
|
||||
unreal.EditorAssetLibrary.delete_directory(parent_path)
|
||||
54
openpype/hosts/unreal/plugins/publish/extract_camera.py
Normal file
54
openpype/hosts/unreal/plugins/publish/extract_camera.py
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
import os
|
||||
|
||||
import unreal
|
||||
from unreal import EditorAssetLibrary as eal
|
||||
from unreal import EditorLevelLibrary as ell
|
||||
|
||||
import openpype.api
|
||||
|
||||
|
||||
class ExtractCamera(openpype.api.Extractor):
|
||||
"""Extract a camera."""
|
||||
|
||||
label = "Extract Camera"
|
||||
hosts = ["unreal"]
|
||||
families = ["camera"]
|
||||
optional = True
|
||||
|
||||
def process(self, instance):
|
||||
# Define extract output file path
|
||||
stagingdir = self.staging_dir(instance)
|
||||
fbx_filename = "{}.fbx".format(instance.name)
|
||||
|
||||
# Perform extraction
|
||||
self.log.info("Performing extraction..")
|
||||
|
||||
# Check if the loaded level is the same of the instance
|
||||
current_level = ell.get_editor_world().get_path_name()
|
||||
assert current_level == instance.data.get("level"), \
|
||||
"Wrong level loaded"
|
||||
|
||||
for member in instance[:]:
|
||||
data = eal.find_asset_data(member)
|
||||
if data.asset_class == "LevelSequence":
|
||||
ar = unreal.AssetRegistryHelpers.get_asset_registry()
|
||||
sequence = ar.get_asset_by_object_path(member).get_asset()
|
||||
unreal.SequencerTools.export_fbx(
|
||||
ell.get_editor_world(),
|
||||
sequence,
|
||||
sequence.get_bindings(),
|
||||
unreal.FbxExportOption(),
|
||||
os.path.join(stagingdir, fbx_filename)
|
||||
)
|
||||
break
|
||||
|
||||
if "representations" not in instance.data:
|
||||
instance.data["representations"] = []
|
||||
|
||||
fbx_representation = {
|
||||
'name': 'fbx',
|
||||
'ext': 'fbx',
|
||||
'files': fbx_filename,
|
||||
"stagingDir": stagingdir,
|
||||
}
|
||||
instance.data["representations"].append(fbx_representation)
|
||||
|
|
@ -15,6 +15,7 @@ import tempfile
|
|||
import pyblish.api
|
||||
from avalon import io
|
||||
from openpype.lib import prepare_template_data
|
||||
from openpype.lib.plugin_tools import parse_json, get_batch_asset_task_info
|
||||
|
||||
|
||||
class CollectPublishedFiles(pyblish.api.ContextPlugin):
|
||||
|
|
@ -33,22 +34,6 @@ class CollectPublishedFiles(pyblish.api.ContextPlugin):
|
|||
# from Settings
|
||||
task_type_to_family = {}
|
||||
|
||||
def _load_json(self, path):
|
||||
path = path.strip('\"')
|
||||
assert os.path.isfile(path), (
|
||||
"Path to json file doesn't exist. \"{}\"".format(path)
|
||||
)
|
||||
data = None
|
||||
with open(path, "r") as json_file:
|
||||
try:
|
||||
data = json.load(json_file)
|
||||
except Exception as exc:
|
||||
self.log.error(
|
||||
"Error loading json: "
|
||||
"{} - Exception: {}".format(path, exc)
|
||||
)
|
||||
return data
|
||||
|
||||
def _process_batch(self, dir_url):
|
||||
task_subfolders = [
|
||||
os.path.join(dir_url, o)
|
||||
|
|
@ -56,22 +41,15 @@ class CollectPublishedFiles(pyblish.api.ContextPlugin):
|
|||
if os.path.isdir(os.path.join(dir_url, o))]
|
||||
self.log.info("task_sub:: {}".format(task_subfolders))
|
||||
for task_dir in task_subfolders:
|
||||
task_data = self._load_json(os.path.join(task_dir,
|
||||
"manifest.json"))
|
||||
task_data = parse_json(os.path.join(task_dir,
|
||||
"manifest.json"))
|
||||
self.log.info("task_data:: {}".format(task_data))
|
||||
ctx = task_data["context"]
|
||||
task_type = "default_task_type"
|
||||
task_name = None
|
||||
|
||||
if ctx["type"] == "task":
|
||||
items = ctx["path"].split('/')
|
||||
asset = items[-2]
|
||||
os.environ["AVALON_TASK"] = ctx["name"]
|
||||
task_name = ctx["name"]
|
||||
task_type = ctx["attributes"]["type"]
|
||||
else:
|
||||
asset = ctx["name"]
|
||||
os.environ["AVALON_TASK"] = ""
|
||||
asset, task_name, task_type = get_batch_asset_task_info(ctx)
|
||||
|
||||
if task_name:
|
||||
os.environ["AVALON_TASK"] = task_name
|
||||
|
||||
is_sequence = len(task_data["files"]) > 1
|
||||
|
||||
|
|
@ -261,7 +239,7 @@ class CollectPublishedFiles(pyblish.api.ContextPlugin):
|
|||
assert batch_dir, (
|
||||
"Missing `OPENPYPE_PUBLISH_DATA`")
|
||||
|
||||
assert batch_dir, \
|
||||
assert os.path.exists(batch_dir), \
|
||||
"Folder {} doesn't exist".format(batch_dir)
|
||||
|
||||
project_name = os.environ.get("AVALON_PROJECT")
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ from avalon.api import AvalonMongoDB
|
|||
|
||||
from openpype.lib import OpenPypeMongoConnection
|
||||
from openpype_modules.avalon_apps.rest_api import _RestApiEndpoint
|
||||
from openpype.lib.plugin_tools import parse_json
|
||||
|
||||
from openpype.lib import PypeLogger
|
||||
|
||||
|
|
@ -175,6 +176,9 @@ class TaskNode(Node):
|
|||
class WebpublisherBatchPublishEndpoint(_RestApiEndpoint):
|
||||
"""Triggers headless publishing of batch."""
|
||||
async def post(self, request) -> Response:
|
||||
# for postprocessing in host, currently only PS
|
||||
host_map = {"photoshop": [".psd", ".psb"]}
|
||||
|
||||
output = {}
|
||||
log.info("WebpublisherBatchPublishEndpoint called")
|
||||
content = await request.json()
|
||||
|
|
@ -182,10 +186,44 @@ class WebpublisherBatchPublishEndpoint(_RestApiEndpoint):
|
|||
batch_path = os.path.join(self.resource.upload_dir,
|
||||
content["batch"])
|
||||
|
||||
add_args = {
|
||||
"host": "webpublisher",
|
||||
"project": content["project_name"],
|
||||
"user": content["user"]
|
||||
}
|
||||
|
||||
command = "remotepublish"
|
||||
|
||||
if content.get("studio_processing"):
|
||||
log.info("Post processing called")
|
||||
|
||||
batch_data = parse_json(os.path.join(batch_path, "manifest.json"))
|
||||
if not batch_data:
|
||||
raise ValueError(
|
||||
"Cannot parse batch meta in {} folder".format(batch_path))
|
||||
task_dir_name = batch_data["tasks"][0]
|
||||
task_data = parse_json(os.path.join(batch_path, task_dir_name,
|
||||
"manifest.json"))
|
||||
if not task_data:
|
||||
raise ValueError(
|
||||
"Cannot parse batch meta in {} folder".format(task_data))
|
||||
|
||||
command = "remotepublishfromapp"
|
||||
for host, extensions in host_map.items():
|
||||
for ext in extensions:
|
||||
for file_name in task_data["files"]:
|
||||
if ext in file_name:
|
||||
add_args["host"] = host
|
||||
break
|
||||
|
||||
if not add_args.get("host"):
|
||||
raise ValueError(
|
||||
"Couldn't discern host from {}".format(task_data["files"]))
|
||||
|
||||
openpype_app = self.resource.executable
|
||||
args = [
|
||||
openpype_app,
|
||||
'remotepublish',
|
||||
command,
|
||||
batch_path
|
||||
]
|
||||
|
||||
|
|
@ -193,12 +231,6 @@ class WebpublisherBatchPublishEndpoint(_RestApiEndpoint):
|
|||
msg = "Non existent OpenPype executable {}".format(openpype_app)
|
||||
raise RuntimeError(msg)
|
||||
|
||||
add_args = {
|
||||
"host": "webpublisher",
|
||||
"project": content["project_name"],
|
||||
"user": content["user"]
|
||||
}
|
||||
|
||||
for key, value in add_args.items():
|
||||
args.append("--{}".format(key))
|
||||
args.append(value)
|
||||
|
|
|
|||
|
|
@ -461,13 +461,8 @@ class ApplicationExecutable:
|
|||
# On MacOS check if exists path to executable when ends with `.app`
|
||||
# - it is common that path will lead to "/Applications/Blender" but
|
||||
# real path is "/Applications/Blender.app"
|
||||
if (
|
||||
platform.system().lower() == "darwin"
|
||||
and not os.path.exists(executable)
|
||||
):
|
||||
_executable = executable + ".app"
|
||||
if os.path.exists(_executable):
|
||||
executable = _executable
|
||||
if platform.system().lower() == "darwin":
|
||||
executable = self.macos_executable_prep(executable)
|
||||
|
||||
self.executable_path = executable
|
||||
|
||||
|
|
@ -477,6 +472,45 @@ class ApplicationExecutable:
|
|||
def __repr__(self):
|
||||
return "<{}> {}".format(self.__class__.__name__, self.executable_path)
|
||||
|
||||
@staticmethod
|
||||
def macos_executable_prep(executable):
|
||||
"""Try to find full path to executable file.
|
||||
|
||||
Real executable is stored in '*.app/Contents/MacOS/<executable>'.
|
||||
|
||||
Having path to '*.app' gives ability to read it's plist info and
|
||||
use "CFBundleExecutable" key from plist to know what is "executable."
|
||||
|
||||
Plist is stored in '*.app/Contents/Info.plist'.
|
||||
|
||||
This is because some '*.app' directories don't have same permissions
|
||||
as real executable.
|
||||
"""
|
||||
# Try to find if there is `.app` file
|
||||
if not os.path.exists(executable):
|
||||
_executable = executable + ".app"
|
||||
if os.path.exists(_executable):
|
||||
executable = _executable
|
||||
|
||||
# Try to find real executable if executable has `Contents` subfolder
|
||||
contents_dir = os.path.join(executable, "Contents")
|
||||
if os.path.exists(contents_dir):
|
||||
executable_filename = None
|
||||
# Load plist file and check for bundle executable
|
||||
plist_filepath = os.path.join(contents_dir, "Info.plist")
|
||||
if os.path.exists(plist_filepath):
|
||||
import plistlib
|
||||
|
||||
parsed_plist = plistlib.readPlist(plist_filepath)
|
||||
executable_filename = parsed_plist.get("CFBundleExecutable")
|
||||
|
||||
if executable_filename:
|
||||
executable = os.path.join(
|
||||
contents_dir, "MacOS", executable_filename
|
||||
)
|
||||
|
||||
return executable
|
||||
|
||||
def as_args(self):
|
||||
return [self.executable_path]
|
||||
|
||||
|
|
|
|||
|
|
@ -487,3 +487,48 @@ def should_decompress(file_url):
|
|||
"compression: \"dwab\"" in output
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def parse_json(path):
|
||||
"""Parses json file at 'path' location
|
||||
|
||||
Returns:
|
||||
(dict) or None if unparsable
|
||||
Raises:
|
||||
AsssertionError if 'path' doesn't exist
|
||||
"""
|
||||
path = path.strip('\"')
|
||||
assert os.path.isfile(path), (
|
||||
"Path to json file doesn't exist. \"{}\"".format(path)
|
||||
)
|
||||
data = None
|
||||
with open(path, "r") as json_file:
|
||||
try:
|
||||
data = json.load(json_file)
|
||||
except Exception as exc:
|
||||
log.error(
|
||||
"Error loading json: "
|
||||
"{} - Exception: {}".format(path, exc)
|
||||
)
|
||||
return data
|
||||
|
||||
|
||||
def get_batch_asset_task_info(ctx):
|
||||
"""Parses context data from webpublisher's batch metadata
|
||||
|
||||
Returns:
|
||||
(tuple): asset, task_name (Optional), task_type
|
||||
"""
|
||||
task_type = "default_task_type"
|
||||
task_name = None
|
||||
asset = None
|
||||
|
||||
if ctx["type"] == "task":
|
||||
items = ctx["path"].split('/')
|
||||
asset = items[-2]
|
||||
task_name = ctx["name"]
|
||||
task_type = ctx["attributes"]["type"]
|
||||
else:
|
||||
asset = ctx["name"]
|
||||
|
||||
return asset, task_name, task_type
|
||||
|
|
|
|||
159
openpype/lib/remote_publish.py
Normal file
159
openpype/lib/remote_publish.py
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
import os
|
||||
from datetime import datetime
|
||||
import sys
|
||||
from bson.objectid import ObjectId
|
||||
|
||||
import pyblish.util
|
||||
import pyblish.api
|
||||
|
||||
from openpype import uninstall
|
||||
from openpype.lib.mongo import OpenPypeMongoConnection
|
||||
|
||||
|
||||
def get_webpublish_conn():
|
||||
"""Get connection to OP 'webpublishes' collection."""
|
||||
mongo_client = OpenPypeMongoConnection.get_mongo_client()
|
||||
database_name = os.environ["OPENPYPE_DATABASE_NAME"]
|
||||
return mongo_client[database_name]["webpublishes"]
|
||||
|
||||
|
||||
def start_webpublish_log(dbcon, batch_id, user):
|
||||
"""Start new log record for 'batch_id'
|
||||
|
||||
Args:
|
||||
dbcon (OpenPypeMongoConnection)
|
||||
batch_id (str)
|
||||
user (str)
|
||||
Returns
|
||||
(ObjectId) from DB
|
||||
"""
|
||||
return dbcon.insert_one({
|
||||
"batch_id": batch_id,
|
||||
"start_date": datetime.now(),
|
||||
"user": user,
|
||||
"status": "in_progress"
|
||||
}).inserted_id
|
||||
|
||||
|
||||
def publish_and_log(dbcon, _id, log, close_plugin_name=None):
|
||||
"""Loops through all plugins, logs ok and fails into OP DB.
|
||||
|
||||
Args:
|
||||
dbcon (OpenPypeMongoConnection)
|
||||
_id (str)
|
||||
log (OpenPypeLogger)
|
||||
close_plugin_name (str): name of plugin with responsibility to
|
||||
close host app
|
||||
"""
|
||||
# Error exit as soon as any error occurs.
|
||||
error_format = "Failed {plugin.__name__}: {error} -- {error.traceback}"
|
||||
|
||||
close_plugin = _get_close_plugin(close_plugin_name, log)
|
||||
|
||||
if isinstance(_id, str):
|
||||
_id = ObjectId(_id)
|
||||
|
||||
log_lines = []
|
||||
for result in pyblish.util.publish_iter():
|
||||
for record in result["records"]:
|
||||
log_lines.append("{}: {}".format(
|
||||
result["plugin"].label, record.msg))
|
||||
|
||||
if result["error"]:
|
||||
log.error(error_format.format(**result))
|
||||
uninstall()
|
||||
log_lines.append(error_format.format(**result))
|
||||
dbcon.update_one(
|
||||
{"_id": _id},
|
||||
{"$set":
|
||||
{
|
||||
"finish_date": datetime.now(),
|
||||
"status": "error",
|
||||
"log": os.linesep.join(log_lines)
|
||||
|
||||
}}
|
||||
)
|
||||
if close_plugin: # close host app explicitly after error
|
||||
context = pyblish.api.Context()
|
||||
close_plugin().process(context)
|
||||
sys.exit(1)
|
||||
else:
|
||||
dbcon.update_one(
|
||||
{"_id": _id},
|
||||
{"$set":
|
||||
{
|
||||
"progress": max(result["progress"], 0.95),
|
||||
"log": os.linesep.join(log_lines)
|
||||
}}
|
||||
)
|
||||
|
||||
# final update
|
||||
dbcon.update_one(
|
||||
{"_id": _id},
|
||||
{"$set":
|
||||
{
|
||||
"finish_date": datetime.now(),
|
||||
"status": "finished_ok",
|
||||
"progress": 1,
|
||||
"log": os.linesep.join(log_lines)
|
||||
}}
|
||||
)
|
||||
|
||||
|
||||
def fail_batch(_id, batches_in_progress, dbcon):
|
||||
"""Set current batch as failed as there are some stuck batches."""
|
||||
running_batches = [str(batch["_id"])
|
||||
for batch in batches_in_progress
|
||||
if batch["_id"] != _id]
|
||||
msg = "There are still running batches {}\n". \
|
||||
format("\n".join(running_batches))
|
||||
msg += "Ask admin to check them and reprocess current batch"
|
||||
dbcon.update_one(
|
||||
{"_id": _id},
|
||||
{"$set":
|
||||
{
|
||||
"finish_date": datetime.now(),
|
||||
"status": "error",
|
||||
"log": msg
|
||||
|
||||
}}
|
||||
)
|
||||
raise ValueError(msg)
|
||||
|
||||
|
||||
def find_variant_key(application_manager, host):
|
||||
"""Searches for latest installed variant for 'host'
|
||||
|
||||
Args:
|
||||
application_manager (ApplicationManager)
|
||||
host (str)
|
||||
Returns
|
||||
(string) (optional)
|
||||
Raises:
|
||||
(ValueError) if no variant found
|
||||
"""
|
||||
app_group = application_manager.app_groups.get(host)
|
||||
if not app_group or not app_group.enabled:
|
||||
raise ValueError("No application {} configured".format(host))
|
||||
|
||||
found_variant_key = None
|
||||
# finds most up-to-date variant if any installed
|
||||
for variant_key, variant in app_group.variants.items():
|
||||
for executable in variant.executables:
|
||||
if executable.exists():
|
||||
found_variant_key = variant_key
|
||||
|
||||
if not found_variant_key:
|
||||
raise ValueError("No executable for {} found".format(host))
|
||||
|
||||
return found_variant_key
|
||||
|
||||
|
||||
def _get_close_plugin(close_plugin_name, log):
|
||||
if close_plugin_name:
|
||||
plugins = pyblish.api.discover()
|
||||
for plugin in plugins:
|
||||
if plugin.__name__ == close_plugin_name:
|
||||
return plugin
|
||||
|
||||
log.warning("Close plugin not found, app might not close.")
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
import os
|
||||
import openpype
|
||||
from openpype import resources
|
||||
from openpype.modules import OpenPypeModule
|
||||
from openpype_interfaces import ITrayModule
|
||||
|
||||
|
|
@ -52,16 +51,12 @@ class AvalonModule(OpenPypeModule, ITrayModule):
|
|||
def tray_init(self):
|
||||
# Add library tool
|
||||
try:
|
||||
from Qt import QtGui
|
||||
from avalon import style
|
||||
from openpype.tools.libraryloader import LibraryLoaderWindow
|
||||
|
||||
self.libraryloader = LibraryLoaderWindow(
|
||||
icon=QtGui.QIcon(resources.get_openpype_icon_filepath()),
|
||||
show_projects=True,
|
||||
show_libraries=True
|
||||
)
|
||||
self.libraryloader.setStyleSheet(style.load_stylesheet())
|
||||
except Exception:
|
||||
self.log.warning(
|
||||
"Couldn't load Library loader tool for tray.",
|
||||
|
|
@ -70,6 +65,9 @@ class AvalonModule(OpenPypeModule, ITrayModule):
|
|||
|
||||
# Definition of Tray menu
|
||||
def tray_menu(self, tray_menu):
|
||||
if self.libraryloader is None:
|
||||
return
|
||||
|
||||
from Qt import QtWidgets
|
||||
# Actions
|
||||
action_library_loader = QtWidgets.QAction(
|
||||
|
|
@ -87,6 +85,9 @@ class AvalonModule(OpenPypeModule, ITrayModule):
|
|||
return
|
||||
|
||||
def show_library_loader(self):
|
||||
if self.libraryloader is None:
|
||||
return
|
||||
|
||||
self.libraryloader.show()
|
||||
|
||||
# Raise and activate the window
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import json
|
|||
from avalon.api import AvalonMongoDB
|
||||
from openpype.api import ProjectSettings
|
||||
from openpype.lib import create_project
|
||||
from openpype.settings import SaveWarningExc
|
||||
|
||||
from openpype_modules.ftrack.lib import (
|
||||
ServerAction,
|
||||
|
|
@ -312,7 +313,6 @@ class PrepareProjectServer(ServerAction):
|
|||
if not in_data:
|
||||
return
|
||||
|
||||
|
||||
root_values = {}
|
||||
root_key = "__root__"
|
||||
for key in tuple(in_data.keys()):
|
||||
|
|
@ -392,7 +392,12 @@ class PrepareProjectServer(ServerAction):
|
|||
else:
|
||||
attributes_entity[key] = value
|
||||
|
||||
project_settings.save()
|
||||
try:
|
||||
project_settings.save()
|
||||
except SaveWarningExc as exc:
|
||||
self.log.info("Few warnings happened during settings save:")
|
||||
for warning in exc.warnings:
|
||||
self.log.info(str(warning))
|
||||
|
||||
# Change custom attributes on project
|
||||
if custom_attribute_values:
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import json
|
|||
from avalon.api import AvalonMongoDB
|
||||
from openpype.api import ProjectSettings
|
||||
from openpype.lib import create_project
|
||||
from openpype.settings import SaveWarningExc
|
||||
|
||||
from openpype_modules.ftrack.lib import (
|
||||
BaseAction,
|
||||
|
|
@ -417,7 +418,12 @@ class PrepareProjectLocal(BaseAction):
|
|||
else:
|
||||
attributes_entity[key] = value
|
||||
|
||||
project_settings.save()
|
||||
try:
|
||||
project_settings.save()
|
||||
except SaveWarningExc as exc:
|
||||
self.log.info("Few warnings happened during settings save:")
|
||||
for warning in exc.warnings:
|
||||
self.log.info(str(warning))
|
||||
|
||||
# Change custom attributes on project
|
||||
if custom_attribute_values:
|
||||
|
|
|
|||
|
|
@ -26,14 +26,21 @@ class CollectUsername(pyblish.api.ContextPlugin):
|
|||
"""
|
||||
order = pyblish.api.CollectorOrder - 0.488
|
||||
label = "Collect ftrack username"
|
||||
hosts = ["webpublisher"]
|
||||
hosts = ["webpublisher", "photoshop"]
|
||||
|
||||
_context = None
|
||||
|
||||
def process(self, context):
|
||||
self.log.info("CollectUsername")
|
||||
# photoshop could be triggered remotely in webpublisher fashion
|
||||
if os.environ["AVALON_APP"] == "photoshop":
|
||||
if not os.environ.get("IS_HEADLESS"):
|
||||
self.log.debug("Regular process, skipping")
|
||||
return
|
||||
|
||||
os.environ["FTRACK_API_USER"] = os.environ["FTRACK_BOT_API_USER"]
|
||||
os.environ["FTRACK_API_KEY"] = os.environ["FTRACK_BOT_API_KEY"]
|
||||
self.log.info("CollectUsername")
|
||||
|
||||
for instance in context:
|
||||
email = instance.data["user_email"]
|
||||
self.log.info("email:: {}".format(email))
|
||||
|
|
|
|||
|
|
@ -3,10 +3,18 @@
|
|||
import os
|
||||
import sys
|
||||
import json
|
||||
from datetime import datetime
|
||||
import time
|
||||
|
||||
from openpype.lib import PypeLogger
|
||||
from openpype.api import get_app_environments_for_context
|
||||
from openpype.lib.plugin_tools import parse_json, get_batch_asset_task_info
|
||||
from openpype.lib.remote_publish import (
|
||||
get_webpublish_conn,
|
||||
start_webpublish_log,
|
||||
publish_and_log,
|
||||
fail_batch,
|
||||
find_variant_key
|
||||
)
|
||||
|
||||
|
||||
class PypeCommands:
|
||||
|
|
@ -110,10 +118,116 @@ class PypeCommands:
|
|||
log.info("Publish finished.")
|
||||
uninstall()
|
||||
|
||||
@staticmethod
|
||||
def remotepublishfromapp(project, batch_dir, host, user, targets=None):
|
||||
"""Opens installed variant of 'host' and run remote publish there.
|
||||
|
||||
Currently implemented and tested for Photoshop where customer
|
||||
wants to process uploaded .psd file and publish collected layers
|
||||
from there.
|
||||
|
||||
Checks if no other batches are running (status =='in_progress). If
|
||||
so, it sleeps for SLEEP (this is separate process),
|
||||
waits for WAIT_FOR seconds altogether.
|
||||
|
||||
Requires installed host application on the machine.
|
||||
|
||||
Runs publish process as user would, in automatic fashion.
|
||||
"""
|
||||
SLEEP = 5 # seconds for another loop check for concurrently runs
|
||||
WAIT_FOR = 300 # seconds to wait for conc. runs
|
||||
|
||||
from openpype import install, uninstall
|
||||
from openpype.api import Logger
|
||||
|
||||
log = Logger.get_logger()
|
||||
|
||||
log.info("remotepublishphotoshop command")
|
||||
|
||||
install()
|
||||
|
||||
from openpype.lib import ApplicationManager
|
||||
application_manager = ApplicationManager()
|
||||
|
||||
found_variant_key = find_variant_key(application_manager, host)
|
||||
|
||||
app_name = "{}/{}".format(host, found_variant_key)
|
||||
|
||||
batch_data = None
|
||||
if batch_dir and os.path.exists(batch_dir):
|
||||
batch_data = parse_json(os.path.join(batch_dir, "manifest.json"))
|
||||
|
||||
if not batch_data:
|
||||
raise ValueError(
|
||||
"Cannot parse batch meta in {} folder".format(batch_dir))
|
||||
|
||||
asset, task_name, _task_type = get_batch_asset_task_info(
|
||||
batch_data["context"])
|
||||
|
||||
# processing from app expects JUST ONE task in batch and 1 workfile
|
||||
task_dir_name = batch_data["tasks"][0]
|
||||
task_data = parse_json(os.path.join(batch_dir, task_dir_name,
|
||||
"manifest.json"))
|
||||
|
||||
workfile_path = os.path.join(batch_dir,
|
||||
task_dir_name,
|
||||
task_data["files"][0])
|
||||
|
||||
print("workfile_path {}".format(workfile_path))
|
||||
|
||||
_, batch_id = os.path.split(batch_dir)
|
||||
dbcon = get_webpublish_conn()
|
||||
# safer to start logging here, launch might be broken altogether
|
||||
_id = start_webpublish_log(dbcon, batch_id, user)
|
||||
|
||||
in_progress = True
|
||||
slept_times = 0
|
||||
while in_progress:
|
||||
batches_in_progress = list(dbcon.find({
|
||||
"status": "in_progress"
|
||||
}))
|
||||
if len(batches_in_progress) > 1:
|
||||
if slept_times * SLEEP >= WAIT_FOR:
|
||||
fail_batch(_id, batches_in_progress, dbcon)
|
||||
|
||||
print("Another batch running, sleeping for a bit")
|
||||
time.sleep(SLEEP)
|
||||
slept_times += 1
|
||||
else:
|
||||
in_progress = False
|
||||
|
||||
# must have for proper launch of app
|
||||
env = get_app_environments_for_context(
|
||||
project,
|
||||
asset,
|
||||
task_name,
|
||||
app_name
|
||||
)
|
||||
os.environ.update(env)
|
||||
|
||||
os.environ["OPENPYPE_PUBLISH_DATA"] = batch_dir
|
||||
os.environ["IS_HEADLESS"] = "true"
|
||||
# must pass identifier to update log lines for a batch
|
||||
os.environ["BATCH_LOG_ID"] = str(_id)
|
||||
|
||||
data = {
|
||||
"last_workfile_path": workfile_path,
|
||||
"start_last_workfile": True
|
||||
}
|
||||
|
||||
launched_app = application_manager.launch(app_name, **data)
|
||||
|
||||
while launched_app.poll() is None:
|
||||
time.sleep(0.5)
|
||||
|
||||
uninstall()
|
||||
|
||||
@staticmethod
|
||||
def remotepublish(project, batch_path, host, user, targets=None):
|
||||
"""Start headless publishing.
|
||||
|
||||
Used to publish rendered assets, workfiles etc.
|
||||
|
||||
Publish use json from passed paths argument.
|
||||
|
||||
Args:
|
||||
|
|
@ -134,7 +248,6 @@ class PypeCommands:
|
|||
|
||||
from openpype import install, uninstall
|
||||
from openpype.api import Logger
|
||||
from openpype.lib import OpenPypeMongoConnection
|
||||
|
||||
# Register target and host
|
||||
import pyblish.api
|
||||
|
|
@ -166,62 +279,11 @@ class PypeCommands:
|
|||
|
||||
log.info("Running publish ...")
|
||||
|
||||
# Error exit as soon as any error occurs.
|
||||
error_format = "Failed {plugin.__name__}: {error} -- {error.traceback}"
|
||||
|
||||
mongo_client = OpenPypeMongoConnection.get_mongo_client()
|
||||
database_name = os.environ["OPENPYPE_DATABASE_NAME"]
|
||||
dbcon = mongo_client[database_name]["webpublishes"]
|
||||
|
||||
_, batch_id = os.path.split(batch_path)
|
||||
_id = dbcon.insert_one({
|
||||
"batch_id": batch_id,
|
||||
"start_date": datetime.now(),
|
||||
"user": user,
|
||||
"status": "in_progress"
|
||||
}).inserted_id
|
||||
dbcon = get_webpublish_conn()
|
||||
_id = start_webpublish_log(dbcon, batch_id, user)
|
||||
|
||||
log_lines = []
|
||||
for result in pyblish.util.publish_iter():
|
||||
for record in result["records"]:
|
||||
log_lines.append("{}: {}".format(
|
||||
result["plugin"].label, record.msg))
|
||||
|
||||
if result["error"]:
|
||||
log.error(error_format.format(**result))
|
||||
uninstall()
|
||||
log_lines.append(error_format.format(**result))
|
||||
dbcon.update_one(
|
||||
{"_id": _id},
|
||||
{"$set":
|
||||
{
|
||||
"finish_date": datetime.now(),
|
||||
"status": "error",
|
||||
"log": os.linesep.join(log_lines)
|
||||
|
||||
}}
|
||||
)
|
||||
sys.exit(1)
|
||||
else:
|
||||
dbcon.update_one(
|
||||
{"_id": _id},
|
||||
{"$set":
|
||||
{
|
||||
"progress": max(result["progress"], 0.95),
|
||||
"log": os.linesep.join(log_lines)
|
||||
}}
|
||||
)
|
||||
|
||||
dbcon.update_one(
|
||||
{"_id": _id},
|
||||
{"$set":
|
||||
{
|
||||
"finish_date": datetime.now(),
|
||||
"status": "finished_ok",
|
||||
"progress": 1,
|
||||
"log": os.linesep.join(log_lines)
|
||||
}}
|
||||
)
|
||||
publish_and_log(dbcon, _id, log)
|
||||
|
||||
log.info("Publish finished.")
|
||||
uninstall()
|
||||
|
|
|
|||
BIN
openpype/resources/app_icons/flame.png
Normal file
BIN
openpype/resources/app_icons/flame.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 73 KiB |
|
|
@ -162,9 +162,7 @@
|
|||
]
|
||||
}
|
||||
],
|
||||
"customNodes": [
|
||||
|
||||
]
|
||||
"customNodes": []
|
||||
},
|
||||
"regexInputs": {
|
||||
"inputs": [
|
||||
|
|
|
|||
|
|
@ -12,6 +12,16 @@
|
|||
"optional": true,
|
||||
"active": true
|
||||
},
|
||||
"CollectRemoteInstances": {
|
||||
"color_code_mapping": [
|
||||
{
|
||||
"color_code": [],
|
||||
"layer_name_regex": [],
|
||||
"family": "",
|
||||
"subset_template_name": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
"ExtractImage": {
|
||||
"formats": [
|
||||
"png",
|
||||
|
|
|
|||
|
|
@ -97,6 +97,42 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"flame": {
|
||||
"enabled": true,
|
||||
"label": "Flame",
|
||||
"icon": "{}/app_icons/flame.png",
|
||||
"host_name": "flame",
|
||||
"environment": {
|
||||
"FLAME_SCRIPT_DIRS": {
|
||||
"windows": "",
|
||||
"darwin": "",
|
||||
"linux": ""
|
||||
}
|
||||
},
|
||||
"variants": {
|
||||
"2021": {
|
||||
"use_python_2": true,
|
||||
"executables": {
|
||||
"windows": [],
|
||||
"darwin": [
|
||||
"/opt/Autodesk/flame_2021/bin/flame.app/Contents/MacOS/startApp"
|
||||
],
|
||||
"linux": [
|
||||
"/opt/Autodesk/flame_2021/bin/flame"
|
||||
]
|
||||
},
|
||||
"arguments": {
|
||||
"windows": [],
|
||||
"darwin": [],
|
||||
"linux": []
|
||||
},
|
||||
"environment": {}
|
||||
},
|
||||
"__dynamic_keys_labels__": {
|
||||
"2021": "2021 (Testing Only)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"nuke": {
|
||||
"enabled": true,
|
||||
"label": "Nuke",
|
||||
|
|
@ -620,12 +656,12 @@
|
|||
"FUSION_UTILITY_SCRIPTS_SOURCE_DIR": [],
|
||||
"FUSION_UTILITY_SCRIPTS_DIR": {
|
||||
"windows": "{PROGRAMDATA}/Blackmagic Design/Fusion/Scripts/Comp",
|
||||
"darvin": "/Library/Application Support/Blackmagic Design/Fusion/Scripts/Comp",
|
||||
"darwin": "/Library/Application Support/Blackmagic Design/Fusion/Scripts/Comp",
|
||||
"linux": "/opt/Fusion/Scripts/Comp"
|
||||
},
|
||||
"PYTHON36": {
|
||||
"windows": "{LOCALAPPDATA}/Programs/Python/Python36",
|
||||
"darvin": "~/Library/Python/3.6/bin",
|
||||
"darwin": "~/Library/Python/3.6/bin",
|
||||
"linux": "/opt/Python/3.6/bin"
|
||||
},
|
||||
"PYTHONPATH": [
|
||||
|
|
@ -686,22 +722,22 @@
|
|||
"RESOLVE_UTILITY_SCRIPTS_SOURCE_DIR": [],
|
||||
"RESOLVE_SCRIPT_API": {
|
||||
"windows": "{PROGRAMDATA}/Blackmagic Design/DaVinci Resolve/Support/Developer/Scripting",
|
||||
"darvin": "/Library/Application Support/Blackmagic Design/DaVinci Resolve/Developer/Scripting",
|
||||
"darwin": "/Library/Application Support/Blackmagic Design/DaVinci Resolve/Developer/Scripting",
|
||||
"linux": "/opt/resolve/Developer/Scripting"
|
||||
},
|
||||
"RESOLVE_SCRIPT_LIB": {
|
||||
"windows": "C:/Program Files/Blackmagic Design/DaVinci Resolve/fusionscript.dll",
|
||||
"darvin": "/Applications/DaVinci Resolve/DaVinci Resolve.app/Contents/Libraries/Fusion/fusionscript.so",
|
||||
"darwin": "/Applications/DaVinci Resolve/DaVinci Resolve.app/Contents/Libraries/Fusion/fusionscript.so",
|
||||
"linux": "/opt/resolve/libs/Fusion/fusionscript.so"
|
||||
},
|
||||
"RESOLVE_UTILITY_SCRIPTS_DIR": {
|
||||
"windows": "{PROGRAMDATA}/Blackmagic Design/DaVinci Resolve/Fusion/Scripts/Comp",
|
||||
"darvin": "/Library/Application Support/Blackmagic Design/DaVinci Resolve/Fusion/Scripts/Comp",
|
||||
"darwin": "/Library/Application Support/Blackmagic Design/DaVinci Resolve/Fusion/Scripts/Comp",
|
||||
"linux": "/opt/resolve/Fusion/Scripts/Comp"
|
||||
},
|
||||
"PYTHON36_RESOLVE": {
|
||||
"windows": "{LOCALAPPDATA}/Programs/Python/Python36",
|
||||
"darvin": "~/Library/Python/3.6/bin",
|
||||
"darwin": "~/Library/Python/3.6/bin",
|
||||
"linux": "/opt/Python/3.6/bin"
|
||||
},
|
||||
"PYTHONPATH": [
|
||||
|
|
|
|||
|
|
@ -143,6 +143,7 @@ class HostsEnumEntity(BaseEnumEntity):
|
|||
"aftereffects",
|
||||
"blender",
|
||||
"celaction",
|
||||
"flame",
|
||||
"fusion",
|
||||
"harmony",
|
||||
"hiero",
|
||||
|
|
|
|||
|
|
@ -43,6 +43,61 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"is_group": true,
|
||||
"key": "CollectRemoteInstances",
|
||||
"label": "Collect Instances for Webpublish",
|
||||
"children": [
|
||||
{
|
||||
"type": "label",
|
||||
"label": "Set color for publishable layers, set publishable families."
|
||||
},
|
||||
{
|
||||
"type": "list",
|
||||
"key": "color_code_mapping",
|
||||
"label": "Color code mappings",
|
||||
"use_label_wrap": false,
|
||||
"collapsible": false,
|
||||
"object_type": {
|
||||
"type": "dict",
|
||||
"children": [
|
||||
{
|
||||
"type": "list",
|
||||
"key": "color_code",
|
||||
"label": "Color codes for layers",
|
||||
"object_type": "text"
|
||||
},
|
||||
{
|
||||
"type": "list",
|
||||
"key": "layer_name_regex",
|
||||
"label": "Layer name regex",
|
||||
"object_type": "text"
|
||||
},
|
||||
{
|
||||
"type": "splitter"
|
||||
},
|
||||
{
|
||||
"key": "family",
|
||||
"label": "Resulting family",
|
||||
"type": "enum",
|
||||
"enum_items": [
|
||||
{
|
||||
"image": "image"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"key": "subset_template_name",
|
||||
"label": "Subset template name"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
{
|
||||
"type": "dict",
|
||||
"key": "flame",
|
||||
"label": "Autodesk Flame",
|
||||
"collapsible": true,
|
||||
"checkbox_key": "enabled",
|
||||
"children": [
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "enabled",
|
||||
"label": "Enabled"
|
||||
},
|
||||
{
|
||||
"type": "schema_template",
|
||||
"name": "template_host_unchangables"
|
||||
},
|
||||
{
|
||||
"key": "environment",
|
||||
"label": "Environment",
|
||||
"type": "raw-json"
|
||||
},
|
||||
{
|
||||
"type": "dict-modifiable",
|
||||
"key": "variants",
|
||||
"collapsible_key": true,
|
||||
"use_label_wrap": false,
|
||||
"object_type": {
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"children": [
|
||||
{
|
||||
"type": "schema_template",
|
||||
"name": "template_host_variant_items"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -9,6 +9,10 @@
|
|||
"type": "schema",
|
||||
"name": "schema_maya"
|
||||
},
|
||||
{
|
||||
"type": "schema",
|
||||
"name": "schema_flame"
|
||||
},
|
||||
{
|
||||
"type": "schema_template",
|
||||
"name": "template_nuke",
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ import os
|
|||
import json
|
||||
import collections
|
||||
from openpype import resources
|
||||
import six
|
||||
from .color_defs import parse_color
|
||||
|
||||
|
||||
_STYLESHEET_CACHE = None
|
||||
|
|
@ -10,7 +12,71 @@ _FONT_IDS = None
|
|||
current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
||||
def _get_colors_raw_data():
|
||||
"""Read data file with stylesheet fill values.
|
||||
|
||||
Returns:
|
||||
dict: Loaded data for stylesheet.
|
||||
"""
|
||||
data_path = os.path.join(current_dir, "data.json")
|
||||
with open(data_path, "r") as data_stream:
|
||||
data = json.load(data_stream)
|
||||
return data
|
||||
|
||||
|
||||
def get_colors_data():
|
||||
"""Only color data from stylesheet data."""
|
||||
data = _get_colors_raw_data()
|
||||
return data.get("color") or {}
|
||||
|
||||
|
||||
def _convert_color_values_to_objects(value):
|
||||
"""Parse all string values in dictionary to Color definitions.
|
||||
|
||||
Recursive function calling itself if value is dictionary.
|
||||
|
||||
Args:
|
||||
value (dict, str): String is parsed into color definition object and
|
||||
dictionary is passed into this function.
|
||||
|
||||
Raises:
|
||||
TypeError: If value in color data do not contain string of dictionary.
|
||||
"""
|
||||
if isinstance(value, dict):
|
||||
output = {}
|
||||
for _key, _value in value.items():
|
||||
output[_key] = _convert_color_values_to_objects(_value)
|
||||
return output
|
||||
|
||||
if not isinstance(value, six.string_types):
|
||||
raise TypeError((
|
||||
"Unexpected type in colors data '{}'. Expected 'str' or 'dict'."
|
||||
).format(str(type(value))))
|
||||
return parse_color(value)
|
||||
|
||||
|
||||
def get_objected_colors():
|
||||
"""Colors parsed from stylesheet data into color definitions.
|
||||
|
||||
Returns:
|
||||
dict: Parsed color objects by keys in data.
|
||||
"""
|
||||
colors_data = get_colors_data()
|
||||
output = {}
|
||||
for key, value in colors_data.items():
|
||||
output[key] = _convert_color_values_to_objects(value)
|
||||
return output
|
||||
|
||||
|
||||
def _load_stylesheet():
|
||||
"""Load strylesheet and trigger all related callbacks.
|
||||
|
||||
Style require more than a stylesheet string. Stylesheet string
|
||||
contains paths to resources which must be registered into Qt application
|
||||
and load fonts used in stylesheets.
|
||||
|
||||
Also replace values from stylesheet data into stylesheet text.
|
||||
"""
|
||||
from . import qrc_resources
|
||||
|
||||
qrc_resources.qInitResources()
|
||||
|
|
@ -19,9 +85,7 @@ def _load_stylesheet():
|
|||
with open(style_path, "r") as style_file:
|
||||
stylesheet = style_file.read()
|
||||
|
||||
data_path = os.path.join(current_dir, "data.json")
|
||||
with open(data_path, "r") as data_stream:
|
||||
data = json.load(data_stream)
|
||||
data = _get_colors_raw_data()
|
||||
|
||||
data_deque = collections.deque()
|
||||
for item in data.items():
|
||||
|
|
@ -44,6 +108,7 @@ def _load_stylesheet():
|
|||
|
||||
|
||||
def _load_font():
|
||||
"""Load and register fonts into Qt application."""
|
||||
from Qt import QtGui
|
||||
|
||||
global _FONT_IDS
|
||||
|
|
@ -83,6 +148,7 @@ def _load_font():
|
|||
|
||||
|
||||
def load_stylesheet():
|
||||
"""Load and return OpenPype Qt stylesheet."""
|
||||
global _STYLESHEET_CACHE
|
||||
if _STYLESHEET_CACHE is None:
|
||||
_STYLESHEET_CACHE = _load_stylesheet()
|
||||
|
|
@ -91,4 +157,5 @@ def load_stylesheet():
|
|||
|
||||
|
||||
def app_icon_path():
|
||||
"""Path to OpenPype icon."""
|
||||
return resources.get_openpype_icon_filepath()
|
||||
|
|
|
|||
391
openpype/style/color_defs.py
Normal file
391
openpype/style/color_defs.py
Normal file
|
|
@ -0,0 +1,391 @@
|
|||
"""Color definitions that can be used to parse strings for stylesheet.
|
||||
|
||||
Each definition must have available method `get_qcolor` which should return
|
||||
`QtGui.QColor` representation of the color.
|
||||
|
||||
# TODO create abstract class to force this method implementation
|
||||
|
||||
Usage: Some colors may be not be used only in stylesheet but is required to
|
||||
use them in code too. To not hardcode these color values into code it is better
|
||||
to use same colors that are available fro stylesheets.
|
||||
|
||||
It is possible that some colors may not be used in stylesheet at all and thei
|
||||
definition is used only in code.
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
|
||||
def parse_color(value):
|
||||
"""Parse string value of color to one of objected representation.
|
||||
|
||||
Args:
|
||||
value(str): Color definition usable in stylesheet.
|
||||
"""
|
||||
modified_value = value.strip().lower()
|
||||
if modified_value.startswith("hsla"):
|
||||
return HSLAColor(value)
|
||||
|
||||
if modified_value.startswith("hsl"):
|
||||
return HSLColor(value)
|
||||
|
||||
if modified_value.startswith("#"):
|
||||
return HEXColor(value)
|
||||
|
||||
if modified_value.startswith("rgba"):
|
||||
return RGBAColor(value)
|
||||
|
||||
if modified_value.startswith("rgb"):
|
||||
return RGBColor(value)
|
||||
return UnknownColor(value)
|
||||
|
||||
|
||||
def create_qcolor(*args):
|
||||
"""Create QtGui.QColor object.
|
||||
|
||||
Args:
|
||||
*args (tuple): It is possible to pass initialization arguments for
|
||||
Qcolor.
|
||||
"""
|
||||
from Qt import QtGui
|
||||
|
||||
return QtGui.QColor(*args)
|
||||
|
||||
|
||||
def min_max_check(value, min_value, max_value):
|
||||
"""Validate number value if is in passed range.
|
||||
|
||||
Args:
|
||||
value (int, float): Value which is validated.
|
||||
min_value (int, float): Minimum possible value. Validation is skipped
|
||||
if passed value is None.
|
||||
max_value (int, float): Maximum possible value. Validation is skipped
|
||||
if passed value is None.
|
||||
|
||||
Raises:
|
||||
ValueError: When 'value' is out of specified range.
|
||||
"""
|
||||
if min_value is not None and value < min_value:
|
||||
raise ValueError("Minimum expected value is '{}' got '{}'".format(
|
||||
min_value, value
|
||||
))
|
||||
|
||||
if max_value is not None and value > max_value:
|
||||
raise ValueError("Maximum expected value is '{}' got '{}'".format(
|
||||
min_value, value
|
||||
))
|
||||
|
||||
|
||||
def int_validation(value, min_value=None, max_value=None):
|
||||
"""Validation of integer value within range.
|
||||
|
||||
Args:
|
||||
value (int): Validated value.
|
||||
min_value (int): Minimum possible value.
|
||||
max_value (int): Maximum possible value.
|
||||
|
||||
Raises:
|
||||
TypeError: If 'value' is not 'int' type.
|
||||
"""
|
||||
if not isinstance(value, int):
|
||||
raise TypeError((
|
||||
"Invalid type of hue expected 'int' got {}"
|
||||
).format(str(type(value))))
|
||||
|
||||
min_max_check(value, min_value, max_value)
|
||||
|
||||
|
||||
def float_validation(value, min_value=None, max_value=None):
|
||||
"""Validation of float value within range.
|
||||
|
||||
Args:
|
||||
value (float): Validated value.
|
||||
min_value (float): Minimum possible value.
|
||||
max_value (float): Maximum possible value.
|
||||
|
||||
Raises:
|
||||
TypeError: If 'value' is not 'float' type.
|
||||
"""
|
||||
if not isinstance(value, float):
|
||||
raise TypeError((
|
||||
"Invalid type of hue expected 'int' got {}"
|
||||
).format(str(type(value))))
|
||||
|
||||
min_max_check(value, min_value, max_value)
|
||||
|
||||
|
||||
class UnknownColor:
|
||||
"""Color from stylesheet data without known color definition.
|
||||
|
||||
This is backup for unknown color definitions which may be for example
|
||||
constants or definition not yet defined by class.
|
||||
"""
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
|
||||
def get_qcolor(self):
|
||||
return create_qcolor(self.value)
|
||||
|
||||
|
||||
class HEXColor:
|
||||
"""Hex color definition.
|
||||
|
||||
Hex color is defined by '#' and 3 or 6 hex values (0-F).
|
||||
|
||||
Examples:
|
||||
"#fff"
|
||||
"#f3f3f3"
|
||||
"""
|
||||
regex = re.compile(r"[a-fA-F0-9]{3}(?:[a-fA-F0-9]{3})?$")
|
||||
|
||||
def __init__(self, color_string):
|
||||
red, green, blue = self.hex_to_rgb(color_string)
|
||||
|
||||
self._color_string = color_string
|
||||
self._red = red
|
||||
self._green = green
|
||||
self._blue = blue
|
||||
|
||||
@property
|
||||
def red(self):
|
||||
return self._red
|
||||
|
||||
@property
|
||||
def green(self):
|
||||
return self._green
|
||||
|
||||
@property
|
||||
def blue(self):
|
||||
return self._blue
|
||||
|
||||
def to_stylesheet_str(self):
|
||||
return self._color_string
|
||||
|
||||
@classmethod
|
||||
def hex_to_rgb(cls, value):
|
||||
"""Convert hex value to rgb."""
|
||||
hex_value = value.lstrip("#")
|
||||
if not cls.regex.match(hex_value):
|
||||
raise ValueError("\"{}\" is not a valid HEX code.".format(value))
|
||||
|
||||
output = []
|
||||
if len(hex_value) == 3:
|
||||
for char in hex_value:
|
||||
output.append(int(char * 2, 16))
|
||||
else:
|
||||
for idx in range(3):
|
||||
start_idx = idx * 2
|
||||
output.append(int(hex_value[start_idx:start_idx + 2], 16))
|
||||
return output
|
||||
|
||||
def get_qcolor(self):
|
||||
return create_qcolor(self.red, self.green, self.blue)
|
||||
|
||||
|
||||
class RGBColor:
|
||||
"""Color defined by red green and blue values.
|
||||
|
||||
Each color has possible integer range 0-255.
|
||||
|
||||
Examples:
|
||||
"rgb(255, 127, 0)"
|
||||
"""
|
||||
def __init__(self, value):
|
||||
modified_color = value.lower().strip()
|
||||
content = modified_color.rstrip(")").lstrip("rgb(")
|
||||
red_str, green_str, blue_str = (
|
||||
item.strip() for item in content.split(",")
|
||||
)
|
||||
red = int(red_str)
|
||||
green = int(green_str)
|
||||
blue = int(blue_str)
|
||||
|
||||
int_validation(red, 0, 255)
|
||||
int_validation(green, 0, 255)
|
||||
int_validation(blue, 0, 255)
|
||||
|
||||
self._red = red
|
||||
self._green = green
|
||||
self._blue = blue
|
||||
|
||||
@property
|
||||
def red(self):
|
||||
return self._red
|
||||
|
||||
@property
|
||||
def green(self):
|
||||
return self._green
|
||||
|
||||
@property
|
||||
def blue(self):
|
||||
return self._blue
|
||||
|
||||
def get_qcolor(self):
|
||||
return create_qcolor(self.red, self.green, self.blue)
|
||||
|
||||
|
||||
class RGBAColor:
|
||||
"""Color defined by red green, blue and alpha values.
|
||||
|
||||
Each color has possible integer range 0-255.
|
||||
|
||||
Examples:
|
||||
"rgba(255, 127, 0, 127)"
|
||||
"""
|
||||
def __init__(self, value):
|
||||
modified_color = value.lower().strip()
|
||||
content = modified_color.rstrip(")").lstrip("rgba(")
|
||||
red_str, green_str, blue_str, alpha_str = (
|
||||
item.strip() for item in content.split(",")
|
||||
)
|
||||
red = int(red_str)
|
||||
green = int(green_str)
|
||||
blue = int(blue_str)
|
||||
if "." in alpha_str:
|
||||
alpha = int(float(alpha_str) * 100)
|
||||
else:
|
||||
alpha = int(alpha_str)
|
||||
|
||||
int_validation(red, 0, 255)
|
||||
int_validation(green, 0, 255)
|
||||
int_validation(blue, 0, 255)
|
||||
int_validation(alpha, 0, 255)
|
||||
|
||||
self._red = red
|
||||
self._green = green
|
||||
self._blue = blue
|
||||
self._alpha = alpha
|
||||
|
||||
@property
|
||||
def red(self):
|
||||
return self._red
|
||||
|
||||
@property
|
||||
def green(self):
|
||||
return self._green
|
||||
|
||||
@property
|
||||
def blue(self):
|
||||
return self._blue
|
||||
|
||||
@property
|
||||
def alpha(self):
|
||||
return self._alpha
|
||||
|
||||
def get_qcolor(self):
|
||||
return create_qcolor(self.red, self.green, self.blue, self.alpha)
|
||||
|
||||
|
||||
class HSLColor:
|
||||
"""Color defined by hue, saturation and light values.
|
||||
|
||||
Hue is defined as integer in rage 0-360. Saturation and light can be
|
||||
defined as float or percent value.
|
||||
|
||||
Examples:
|
||||
"hsl(27, 0.7, 0.3)"
|
||||
"hsl(27, 70%, 30%)"
|
||||
"""
|
||||
def __init__(self, value):
|
||||
modified_color = value.lower().strip()
|
||||
content = modified_color.rstrip(")").lstrip("hsl(")
|
||||
hue_str, sat_str, light_str = (
|
||||
item.strip() for item in content.split(",")
|
||||
)
|
||||
hue = int(hue_str) % 360
|
||||
if "%" in sat_str:
|
||||
sat = float(sat_str.rstrip("%")) / 100
|
||||
else:
|
||||
sat = float(sat)
|
||||
|
||||
if "%" in light_str:
|
||||
light = float(light_str.rstrip("%")) / 100
|
||||
else:
|
||||
light = float(light_str)
|
||||
|
||||
int_validation(hue, 0, 360)
|
||||
float_validation(sat, 0, 1)
|
||||
float_validation(light, 0, 1)
|
||||
|
||||
self._hue = hue
|
||||
self._saturation = sat
|
||||
self._light = light
|
||||
|
||||
@property
|
||||
def hue(self):
|
||||
return self._hue
|
||||
|
||||
@property
|
||||
def saturation(self):
|
||||
return self._saturation
|
||||
|
||||
@property
|
||||
def light(self):
|
||||
return self._light
|
||||
|
||||
def get_qcolor(self):
|
||||
color = create_qcolor()
|
||||
color.setHslF(self.hue / 360, self.saturation, self.light)
|
||||
return color
|
||||
|
||||
|
||||
class HSLAColor:
|
||||
"""Color defined by hue, saturation, light and alpha values.
|
||||
|
||||
Hue is defined as integer in rage 0-360. Saturation and light can be
|
||||
defined as float (0-1 range) or percent value(0-100%). And alpha
|
||||
as float (0-1 range).
|
||||
|
||||
Examples:
|
||||
"hsl(27, 0.7, 0.3)"
|
||||
"hsl(27, 70%, 30%)"
|
||||
"""
|
||||
def __init__(self, value):
|
||||
modified_color = value.lower().strip()
|
||||
content = modified_color.rstrip(")").lstrip("hsla(")
|
||||
hue_str, sat_str, light_str, alpha_str = (
|
||||
item.strip() for item in content.split(",")
|
||||
)
|
||||
hue = int(hue_str) % 360
|
||||
if "%" in sat_str:
|
||||
sat = float(sat_str.rstrip("%")) / 100
|
||||
else:
|
||||
sat = float(sat)
|
||||
|
||||
if "%" in light_str:
|
||||
light = float(light_str.rstrip("%")) / 100
|
||||
else:
|
||||
light = float(light_str)
|
||||
|
||||
alpha = float(alpha_str)
|
||||
|
||||
int_validation(hue, 0, 360)
|
||||
float_validation(sat, 0, 1)
|
||||
float_validation(light, 0, 1)
|
||||
float_validation(alpha, 0, 1)
|
||||
|
||||
self._hue = hue
|
||||
self._saturation = sat
|
||||
self._light = light
|
||||
self._alpha = alpha
|
||||
|
||||
@property
|
||||
def hue(self):
|
||||
return self._hue
|
||||
|
||||
@property
|
||||
def saturation(self):
|
||||
return self._saturation
|
||||
|
||||
@property
|
||||
def light(self):
|
||||
return self._light
|
||||
|
||||
@property
|
||||
def alpha(self):
|
||||
return self._alpha
|
||||
|
||||
def get_qcolor(self):
|
||||
color = create_qcolor()
|
||||
color.setHslF(self.hue / 360, self.saturation, self.light, self.alpha)
|
||||
return color
|
||||
|
|
@ -28,25 +28,36 @@
|
|||
"bg": "#2C313A",
|
||||
"bg-inputs": "#21252B",
|
||||
"bg-buttons": "#434a56",
|
||||
"bg-button-hover": "hsla(220, 14%, 70%, .3)",
|
||||
"bg-button-hover": "rgba(168, 175, 189, 0.3)",
|
||||
"bg-inputs-disabled": "#2C313A",
|
||||
"bg-buttons-disabled": "#434a56",
|
||||
|
||||
"bg-splitter": "#434a56",
|
||||
"bg-splitter-hover": "rgba(168, 175, 189, 0.3)",
|
||||
|
||||
"bg-menu-separator": "rgba(75, 83, 98, 127)",
|
||||
|
||||
"bg-scroll-handle": "#4B5362",
|
||||
|
||||
"bg-view": "#21252B",
|
||||
"bg-view-header": "#373D48",
|
||||
"bg-view-hover": "hsla(220, 14%, 70%, .3)",
|
||||
"bg-view-hover": "rgba(168, 175, 189, .3)",
|
||||
"bg-view-alternate": "rgb(36, 42, 50)",
|
||||
"bg-view-disabled": "#434a56",
|
||||
"bg-view-alternate-disabled": "#2C313A",
|
||||
"bg-view-selection": "hsla(200, 60%, 60%, .4)",
|
||||
"bg-view-selection-hover": "hsla(200, 60%, 60%, .8)",
|
||||
"bg-view-selection": "rgba(92, 173, 214, .4)",
|
||||
"bg-view-selection-hover": "rgba(92, 173, 214, .8)",
|
||||
|
||||
"border": "#373D48",
|
||||
"border-hover": "hsla(220, 14%, 70%, .3)",
|
||||
"border-focus": "hsl(200, 60%, 60%)"
|
||||
"border-hover": "rgba(168, 175, 189, .3)",
|
||||
"border-focus": "hsl(200, 60%, 60%)",
|
||||
|
||||
"loader": {
|
||||
"asset-view": {
|
||||
"selected": "rgba(168, 175, 189, 0.6)",
|
||||
"hover": "rgba(168, 175, 189, 0.3)",
|
||||
"selected-hover": "rgba(168, 175, 189, 0.7)"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
BIN
openpype/style/images/transparent.png
Normal file
BIN
openpype/style/images/transparent.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 69 B |
|
|
@ -10,19 +10,7 @@ from PyQt5 import QtCore
|
|||
|
||||
|
||||
qt_resource_data = b"\
|
||||
\x00\x00\x00\xa0\
|
||||
\x89\
|
||||
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
|
||||
\x00\x00\x06\x00\x00\x00\x09\x08\x04\x00\x00\x00\xbb\x93\x95\x16\
|
||||
\x00\x00\x00\x01\x73\x52\x47\x42\x00\xae\xce\x1c\xe9\x00\x00\x00\
|
||||
\x02\x62\x4b\x47\x44\x00\xff\x87\x8f\xcc\xbf\x00\x00\x00\x09\x70\
|
||||
\x48\x59\x73\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\
|
||||
\x00\x00\x00\x07\x74\x49\x4d\x45\x07\xdc\x08\x17\x14\x1c\x1f\x24\
|
||||
\xc6\x09\x17\x00\x00\x00\x24\x49\x44\x41\x54\x08\xd7\x63\x60\x40\
|
||||
\x05\xff\xcf\xc3\x58\x4c\xc8\x5c\x26\x64\x59\x26\x64\xc5\x70\x0e\
|
||||
\xa3\x21\x9c\xc3\x68\x88\x61\x1a\x0a\x00\x00\x6d\x84\x09\x75\x37\
|
||||
\x9e\xd9\x23\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
|
||||
\x00\x00\x07\x30\
|
||||
\x00\x00\x07\x06\
|
||||
\x89\
|
||||
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
|
||||
\x00\x00\x0a\x00\x00\x00\x07\x08\x06\x00\x00\x00\x31\xac\xdc\x63\
|
||||
|
|
@ -80,10 +68,10 @@ qt_resource_data = b"\
|
|||
\x65\x3d\x22\x73\x52\x47\x42\x20\x49\x45\x43\x36\x31\x39\x36\x36\
|
||||
\x2d\x32\x2e\x31\x22\x0a\x20\x20\x20\x78\x6d\x70\x3a\x4d\x6f\x64\
|
||||
\x69\x66\x79\x44\x61\x74\x65\x3d\x22\x32\x30\x32\x31\x2d\x30\x35\
|
||||
\x2d\x33\x31\x54\x31\x32\x3a\x33\x33\x3a\x31\x34\x2b\x30\x32\x3a\
|
||||
\x2d\x33\x31\x54\x31\x32\x3a\x33\x30\x3a\x31\x31\x2b\x30\x32\x3a\
|
||||
\x30\x30\x22\x0a\x20\x20\x20\x78\x6d\x70\x3a\x4d\x65\x74\x61\x64\
|
||||
\x61\x74\x61\x44\x61\x74\x65\x3d\x22\x32\x30\x32\x31\x2d\x30\x35\
|
||||
\x2d\x33\x31\x54\x31\x32\x3a\x33\x33\x3a\x31\x34\x2b\x30\x32\x3a\
|
||||
\x2d\x33\x31\x54\x31\x32\x3a\x33\x30\x3a\x31\x31\x2b\x30\x32\x3a\
|
||||
\x30\x30\x22\x3e\x0a\x20\x20\x20\x3c\x78\x6d\x70\x4d\x4d\x3a\x48\
|
||||
\x69\x73\x74\x6f\x72\x79\x3e\x0a\x20\x20\x20\x20\x3c\x72\x64\x66\
|
||||
\x3a\x53\x65\x71\x3e\x0a\x20\x20\x20\x20\x20\x3c\x72\x64\x66\x3a\
|
||||
|
|
@ -94,14 +82,14 @@ qt_resource_data = b"\
|
|||
\x6e\x69\x74\x79\x20\x44\x65\x73\x69\x67\x6e\x65\x72\x20\x31\x2e\
|
||||
\x39\x2e\x32\x22\x0a\x20\x20\x20\x20\x20\x20\x73\x74\x45\x76\x74\
|
||||
\x3a\x77\x68\x65\x6e\x3d\x22\x32\x30\x32\x31\x2d\x30\x35\x2d\x33\
|
||||
\x31\x54\x31\x32\x3a\x33\x33\x3a\x31\x34\x2b\x30\x32\x3a\x30\x30\
|
||||
\x31\x54\x31\x32\x3a\x33\x30\x3a\x31\x31\x2b\x30\x32\x3a\x30\x30\
|
||||
\x22\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x2f\x72\x64\x66\x3a\x53\x65\
|
||||
\x71\x3e\x0a\x20\x20\x20\x3c\x2f\x78\x6d\x70\x4d\x4d\x3a\x48\x69\
|
||||
\x73\x74\x6f\x72\x79\x3e\x0a\x20\x20\x3c\x2f\x72\x64\x66\x3a\x44\
|
||||
\x65\x73\x63\x72\x69\x70\x74\x69\x6f\x6e\x3e\x0a\x20\x3c\x2f\x72\
|
||||
\x64\x66\x3a\x52\x44\x46\x3e\x0a\x3c\x2f\x78\x3a\x78\x6d\x70\x6d\
|
||||
\x65\x74\x61\x3e\x0a\x3c\x3f\x78\x70\x61\x63\x6b\x65\x74\x20\x65\
|
||||
\x6e\x64\x3d\x22\x72\x22\x3f\x3e\x48\x8b\x5b\x5e\x00\x00\x01\x83\
|
||||
\x6e\x64\x3d\x22\x72\x22\x3f\x3e\x85\x9d\x9f\x08\x00\x00\x01\x83\
|
||||
\x69\x43\x43\x50\x73\x52\x47\x42\x20\x49\x45\x43\x36\x31\x39\x36\
|
||||
\x36\x2d\x32\x2e\x31\x00\x00\x28\x91\x75\x91\xcf\x2b\x44\x51\x14\
|
||||
\xc7\x3f\x66\x68\xfc\x18\x8d\x62\x61\x31\x65\x12\x16\x42\x83\x12\
|
||||
|
|
@ -128,30 +116,15 @@ qt_resource_data = b"\
|
|||
\xfd\xec\x73\x74\x07\xd1\x35\xf9\xaa\x4b\xd8\xd9\x85\x0e\x39\xef\
|
||||
\x59\xf8\x06\x8e\xfd\x67\xf8\xfd\x8a\x18\x97\x00\x00\x00\x09\x70\
|
||||
\x48\x59\x73\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\
|
||||
\x00\x00\x00\x97\x49\x44\x41\x54\x18\x95\x6d\xcf\xb1\x6a\x02\x41\
|
||||
\x14\x85\xe1\x6f\xb7\xb6\xd0\x27\x48\x3d\x56\x69\x03\xb1\xb4\x48\
|
||||
\x3b\x6c\xa5\xf1\x39\xf6\x59\x02\x56\x42\xba\x61\x0a\x0b\x3b\x1b\
|
||||
\x1b\x6b\x41\x18\x02\x29\x6d\xe3\xbe\x82\xcd\x06\x16\xd9\xdb\xdd\
|
||||
\x9f\xff\x5c\xee\xa9\x62\x2a\x13\x4c\x73\x13\x6e\x46\x26\xa6\xf2\
|
||||
\x82\xae\x46\x8b\xdf\x98\xca\xfb\x88\xb4\xc0\x0f\xda\x1a\x5b\x74\
|
||||
\xd8\xc7\x54\xc2\x40\x9a\x63\x8f\x3f\x7c\x55\x3d\x7c\xc5\x09\x77\
|
||||
\xbc\xa1\xc2\x19\x33\x2c\x72\x13\x2e\xd5\xe0\xc2\x12\x07\x5c\x51\
|
||||
\x23\xe0\x23\x37\xe1\xa8\x4f\x0e\x7f\xda\x60\xd7\xaf\x9f\xb9\x09\
|
||||
\xdf\x63\x05\xff\xe5\x75\x4c\x65\xf5\xcc\x1f\x0d\x33\x2c\x83\xb6\
|
||||
\x06\x44\x83\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
|
||||
\x00\x00\x00\xa5\
|
||||
\x89\
|
||||
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
|
||||
\x00\x00\x09\x00\x00\x00\x06\x08\x04\x00\x00\x00\xbb\xce\x7c\x4e\
|
||||
\x00\x00\x00\x01\x73\x52\x47\x42\x00\xae\xce\x1c\xe9\x00\x00\x00\
|
||||
\x02\x62\x4b\x47\x44\x00\x9c\x53\x34\xfc\x5d\x00\x00\x00\x09\x70\
|
||||
\x48\x59\x73\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\
|
||||
\x00\x00\x00\x07\x74\x49\x4d\x45\x07\xdc\x08\x17\x0b\x02\x04\x6d\
|
||||
\x98\x1b\x69\x00\x00\x00\x29\x49\x44\x41\x54\x08\xd7\x63\x60\xc0\
|
||||
\x00\x8c\x0c\x0c\xff\xcf\xa3\x08\x18\x32\x32\x30\x20\x0b\x32\x1a\
|
||||
\x32\x30\x30\x42\x98\x10\x41\x46\x43\x14\x13\x50\xb5\xa3\x01\x00\
|
||||
\xd6\x10\x07\xd2\x2f\x48\xdf\x4a\x00\x00\x00\x00\x49\x45\x4e\x44\
|
||||
\xae\x42\x60\x82\
|
||||
\x00\x00\x00\x6d\x49\x44\x41\x54\x18\x95\x75\xcf\xc1\x09\xc2\x50\
|
||||
\x10\x84\xe1\xd7\x85\x07\x9b\xd0\x43\x40\xd2\x82\x78\x14\x7b\x30\
|
||||
\x57\x21\x8d\x84\x60\x3f\x62\x4b\x7a\x48\xcc\x97\x83\xfb\x30\x04\
|
||||
\xdf\x9c\x86\x7f\x67\x99\xdd\x84\x0d\xaa\x54\x10\x6a\x6c\x13\x1e\
|
||||
\xbe\xba\xfe\x09\x35\x31\x7b\xe6\x8d\x0f\x26\x1c\x17\xa1\x53\xb0\
|
||||
\x11\x87\x0c\x2f\x01\x07\xec\xb0\x0f\x3f\xe1\xbc\xae\x69\xa3\xe6\
|
||||
\x85\x77\xf8\x5b\xe9\xf0\xbb\x9f\xfa\xd2\x83\x39\xdc\xa3\x5b\xf3\
|
||||
\x19\x2e\xa8\x89\xb5\x30\xf7\x43\xa0\x00\x00\x00\x00\x49\x45\x4e\
|
||||
\x44\xae\x42\x60\x82\
|
||||
\x00\x00\x00\xa0\
|
||||
\x89\
|
||||
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
|
||||
|
|
@ -164,6 +137,19 @@ qt_resource_data = b"\
|
|||
\x05\x73\x3e\xc0\x58\x4c\xc8\x5c\x26\x64\x59\x26\x64\xc5\x70\x4e\
|
||||
\x8a\x00\x9c\x93\x22\x80\x61\x1a\x0a\x00\x00\x29\x95\x08\xaf\x88\
|
||||
\xac\xba\x34\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
|
||||
\x00\x00\x00\xa6\
|
||||
\x89\
|
||||
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
|
||||
\x00\x00\x09\x00\x00\x00\x06\x08\x04\x00\x00\x00\xbb\xce\x7c\x4e\
|
||||
\x00\x00\x00\x01\x73\x52\x47\x42\x00\xae\xce\x1c\xe9\x00\x00\x00\
|
||||
\x02\x62\x4b\x47\x44\x00\xff\x87\x8f\xcc\xbf\x00\x00\x00\x09\x70\
|
||||
\x48\x59\x73\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\
|
||||
\x00\x00\x00\x07\x74\x49\x4d\x45\x07\xdc\x08\x17\x08\x15\x3b\xdc\
|
||||
\x3b\x0c\x9b\x00\x00\x00\x2a\x49\x44\x41\x54\x08\xd7\x63\x60\xc0\
|
||||
\x00\x8c\x0c\x0c\x73\x3e\x20\x0b\xa4\x08\x30\x32\x30\x20\x0b\xa6\
|
||||
\x08\x30\x30\x30\x42\x98\x10\xc1\x14\x01\x14\x13\x50\xb5\xa3\x01\
|
||||
\x00\xc6\xb9\x07\x90\x5d\x66\x1f\x83\x00\x00\x00\x00\x49\x45\x4e\
|
||||
\x44\xae\x42\x60\x82\
|
||||
\x00\x00\x07\xad\
|
||||
\x89\
|
||||
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
|
||||
|
|
@ -289,6 +275,45 @@ qt_resource_data = b"\
|
|||
\x5e\x78\xa2\x9e\x0e\xa7\x20\x74\x47\x39\x1d\xf6\xe1\x95\x2b\xd6\
|
||||
\xb1\x44\x8e\x0e\xcb\x58\xf0\x0f\x52\x8a\x79\x18\xdc\xe2\x02\x70\
|
||||
\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
|
||||
\x00\x00\x00\xa6\
|
||||
\x89\
|
||||
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
|
||||
\x00\x00\x09\x00\x00\x00\x06\x08\x04\x00\x00\x00\xbb\xce\x7c\x4e\
|
||||
\x00\x00\x00\x01\x73\x52\x47\x42\x00\xae\xce\x1c\xe9\x00\x00\x00\
|
||||
\x02\x62\x4b\x47\x44\x00\xff\x87\x8f\xcc\xbf\x00\x00\x00\x09\x70\
|
||||
\x48\x59\x73\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\
|
||||
\x00\x00\x00\x07\x74\x49\x4d\x45\x07\xdc\x08\x17\x08\x15\x3b\xdc\
|
||||
\x3b\x0c\x9b\x00\x00\x00\x2a\x49\x44\x41\x54\x08\xd7\x63\x60\xc0\
|
||||
\x00\x8c\x0c\x0c\x73\x3e\x20\x0b\xa4\x08\x30\x32\x30\x20\x0b\xa6\
|
||||
\x08\x30\x30\x30\x42\x98\x10\xc1\x14\x01\x14\x13\x50\xb5\xa3\x01\
|
||||
\x00\xc6\xb9\x07\x90\x5d\x66\x1f\x83\x00\x00\x00\x00\x49\x45\x4e\
|
||||
\x44\xae\x42\x60\x82\
|
||||
\x00\x00\x00\xa6\
|
||||
\x89\
|
||||
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
|
||||
\x00\x00\x06\x00\x00\x00\x09\x08\x04\x00\x00\x00\xbb\x93\x95\x16\
|
||||
\x00\x00\x00\x01\x73\x52\x47\x42\x00\xae\xce\x1c\xe9\x00\x00\x00\
|
||||
\x02\x62\x4b\x47\x44\x00\xff\x87\x8f\xcc\xbf\x00\x00\x00\x09\x70\
|
||||
\x48\x59\x73\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\
|
||||
\x00\x00\x00\x07\x74\x49\x4d\x45\x07\xdc\x08\x17\x14\x1d\x00\xb0\
|
||||
\xd5\x35\xa3\x00\x00\x00\x2a\x49\x44\x41\x54\x08\xd7\x63\x60\xc0\
|
||||
\x06\xfe\x9f\x67\x60\x60\x42\x30\xa1\x1c\x08\x93\x81\x81\x09\xc1\
|
||||
\x64\x60\x60\x62\x60\x60\x34\x44\xe2\x20\x73\x19\x90\x8d\x40\x02\
|
||||
\x00\x64\x40\x09\x75\x86\xb3\xad\x9c\x00\x00\x00\x00\x49\x45\x4e\
|
||||
\x44\xae\x42\x60\x82\
|
||||
\x00\x00\x00\xa6\
|
||||
\x89\
|
||||
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
|
||||
\x00\x00\x09\x00\x00\x00\x06\x08\x04\x00\x00\x00\xbb\xce\x7c\x4e\
|
||||
\x00\x00\x00\x01\x73\x52\x47\x42\x00\xae\xce\x1c\xe9\x00\x00\x00\
|
||||
\x02\x62\x4b\x47\x44\x00\xff\x87\x8f\xcc\xbf\x00\x00\x00\x09\x70\
|
||||
\x48\x59\x73\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\
|
||||
\x00\x00\x00\x07\x74\x49\x4d\x45\x07\xdc\x08\x17\x08\x15\x3b\xdc\
|
||||
\x3b\x0c\x9b\x00\x00\x00\x2a\x49\x44\x41\x54\x08\xd7\x63\x60\xc0\
|
||||
\x00\x8c\x0c\x0c\x73\x3e\x20\x0b\xa4\x08\x30\x32\x30\x20\x0b\xa6\
|
||||
\x08\x30\x30\x30\x42\x98\x10\xc1\x14\x01\x14\x13\x50\xb5\xa3\x01\
|
||||
\x00\xc6\xb9\x07\x90\x5d\x66\x1f\x83\x00\x00\x00\x00\x49\x45\x4e\
|
||||
\x44\xae\x42\x60\x82\
|
||||
\x00\x00\x00\xa0\
|
||||
\x89\
|
||||
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
|
||||
|
|
@ -313,18 +338,26 @@ qt_resource_data = b"\
|
|||
\x0d\xe6\x7c\x80\xb1\x18\x91\x05\x52\x04\xe0\x42\x08\x15\x29\x02\
|
||||
\x0c\x0c\x8c\xc8\x02\x08\x95\x68\x00\x00\xac\xac\x07\x90\x4e\x65\
|
||||
\x34\xac\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
|
||||
\x00\x00\x00\x9e\
|
||||
\x00\x00\x00\x45\
|
||||
\x89\
|
||||
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
|
||||
\x00\x00\x01\x00\x00\x00\x01\x08\x02\x00\x00\x00\x90\x77\x53\xde\
|
||||
\x00\x00\x00\x0c\x49\x44\x41\x54\x08\x99\x63\x60\x60\x60\x00\x00\
|
||||
\x00\x04\x00\x01\xa3\x0a\x15\xe3\x00\x00\x00\x00\x49\x45\x4e\x44\
|
||||
\xae\x42\x60\x82\
|
||||
\x00\x00\x00\xa5\
|
||||
\x89\
|
||||
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
|
||||
\x00\x00\x09\x00\x00\x00\x06\x08\x04\x00\x00\x00\xbb\xce\x7c\x4e\
|
||||
\x00\x00\x00\x01\x73\x52\x47\x42\x00\xae\xce\x1c\xe9\x00\x00\x00\
|
||||
\x02\x62\x4b\x47\x44\x00\xff\x87\x8f\xcc\xbf\x00\x00\x00\x09\x70\
|
||||
\x02\x62\x4b\x47\x44\x00\x9c\x53\x34\xfc\x5d\x00\x00\x00\x09\x70\
|
||||
\x48\x59\x73\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\
|
||||
\x00\x00\x00\x07\x74\x49\x4d\x45\x07\xdc\x08\x17\x08\x15\x0f\xfd\
|
||||
\x8f\xf8\x2e\x00\x00\x00\x22\x49\x44\x41\x54\x08\xd7\x63\x60\xc0\
|
||||
\x0d\xfe\x9f\x87\xb1\x18\x91\x05\x18\x0d\xe1\x42\x48\x2a\x0c\x19\
|
||||
\x18\x18\x91\x05\x10\x2a\xd1\x00\x00\xca\xb5\x07\xd2\x76\xbb\xb2\
|
||||
\xc5\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
|
||||
\x00\x00\x00\x07\x74\x49\x4d\x45\x07\xdc\x08\x17\x0b\x02\x04\x6d\
|
||||
\x98\x1b\x69\x00\x00\x00\x29\x49\x44\x41\x54\x08\xd7\x63\x60\xc0\
|
||||
\x00\x8c\x0c\x0c\xff\xcf\xa3\x08\x18\x32\x32\x30\x20\x0b\x32\x1a\
|
||||
\x32\x30\x30\x42\x98\x10\x41\x46\x43\x14\x13\x50\xb5\xa3\x01\x00\
|
||||
\xd6\x10\x07\xd2\x2f\x48\xdf\x4a\x00\x00\x00\x00\x49\x45\x4e\x44\
|
||||
\xae\x42\x60\x82\
|
||||
\x00\x00\x00\xa5\
|
||||
\x89\
|
||||
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
|
||||
|
|
@ -341,15 +374,170 @@ qt_resource_data = b"\
|
|||
\x00\x00\x00\xa6\
|
||||
\x89\
|
||||
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
|
||||
\x00\x00\x09\x00\x00\x00\x06\x08\x04\x00\x00\x00\xbb\xce\x7c\x4e\
|
||||
\x00\x00\x06\x00\x00\x00\x09\x08\x04\x00\x00\x00\xbb\x93\x95\x16\
|
||||
\x00\x00\x00\x01\x73\x52\x47\x42\x00\xae\xce\x1c\xe9\x00\x00\x00\
|
||||
\x02\x62\x4b\x47\x44\x00\xff\x87\x8f\xcc\xbf\x00\x00\x00\x09\x70\
|
||||
\x48\x59\x73\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\
|
||||
\x00\x00\x00\x07\x74\x49\x4d\x45\x07\xdc\x08\x17\x08\x15\x3b\xdc\
|
||||
\x3b\x0c\x9b\x00\x00\x00\x2a\x49\x44\x41\x54\x08\xd7\x63\x60\xc0\
|
||||
\x00\x8c\x0c\x0c\x73\x3e\x20\x0b\xa4\x08\x30\x32\x30\x20\x0b\xa6\
|
||||
\x08\x30\x30\x30\x42\x98\x10\xc1\x14\x01\x14\x13\x50\xb5\xa3\x01\
|
||||
\x00\xc6\xb9\x07\x90\x5d\x66\x1f\x83\x00\x00\x00\x00\x49\x45\x4e\
|
||||
\x00\x00\x00\x07\x74\x49\x4d\x45\x07\xdc\x08\x17\x14\x1f\x20\xb9\
|
||||
\x8d\x77\xe9\x00\x00\x00\x2a\x49\x44\x41\x54\x08\xd7\x63\x60\xc0\
|
||||
\x06\xe6\x7c\x60\x60\x60\x42\x30\xa1\x1c\x08\x93\x81\x81\x09\xc1\
|
||||
\x64\x60\x60\x62\x60\x48\x11\x40\xe2\x20\x73\x19\x90\x8d\x40\x02\
|
||||
\x00\x23\xed\x08\xaf\x64\x9f\x0f\x15\x00\x00\x00\x00\x49\x45\x4e\
|
||||
\x44\xae\x42\x60\x82\
|
||||
\x00\x00\x00\xa5\
|
||||
\x89\
|
||||
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
|
||||
\x00\x00\x09\x00\x00\x00\x06\x08\x04\x00\x00\x00\xbb\xce\x7c\x4e\
|
||||
\x00\x00\x00\x01\x73\x52\x47\x42\x00\xae\xce\x1c\xe9\x00\x00\x00\
|
||||
\x02\x62\x4b\x47\x44\x00\x9c\x53\x34\xfc\x5d\x00\x00\x00\x09\x70\
|
||||
\x48\x59\x73\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\
|
||||
\x00\x00\x00\x07\x74\x49\x4d\x45\x07\xdc\x08\x17\x0b\x02\x04\x6d\
|
||||
\x98\x1b\x69\x00\x00\x00\x29\x49\x44\x41\x54\x08\xd7\x63\x60\xc0\
|
||||
\x00\x8c\x0c\x0c\xff\xcf\xa3\x08\x18\x32\x32\x30\x20\x0b\x32\x1a\
|
||||
\x32\x30\x30\x42\x98\x10\x41\x46\x43\x14\x13\x50\xb5\xa3\x01\x00\
|
||||
\xd6\x10\x07\xd2\x2f\x48\xdf\x4a\x00\x00\x00\x00\x49\x45\x4e\x44\
|
||||
\xae\x42\x60\x82\
|
||||
\x00\x00\x07\x30\
|
||||
\x89\
|
||||
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
|
||||
\x00\x00\x0a\x00\x00\x00\x07\x08\x06\x00\x00\x00\x31\xac\xdc\x63\
|
||||
\x00\x00\x04\xb0\x69\x54\x58\x74\x58\x4d\x4c\x3a\x63\x6f\x6d\x2e\
|
||||
\x61\x64\x6f\x62\x65\x2e\x78\x6d\x70\x00\x00\x00\x00\x00\x3c\x3f\
|
||||
\x78\x70\x61\x63\x6b\x65\x74\x20\x62\x65\x67\x69\x6e\x3d\x22\xef\
|
||||
\xbb\xbf\x22\x20\x69\x64\x3d\x22\x57\x35\x4d\x30\x4d\x70\x43\x65\
|
||||
\x68\x69\x48\x7a\x72\x65\x53\x7a\x4e\x54\x63\x7a\x6b\x63\x39\x64\
|
||||
\x22\x3f\x3e\x0a\x3c\x78\x3a\x78\x6d\x70\x6d\x65\x74\x61\x20\x78\
|
||||
\x6d\x6c\x6e\x73\x3a\x78\x3d\x22\x61\x64\x6f\x62\x65\x3a\x6e\x73\
|
||||
\x3a\x6d\x65\x74\x61\x2f\x22\x20\x78\x3a\x78\x6d\x70\x74\x6b\x3d\
|
||||
\x22\x58\x4d\x50\x20\x43\x6f\x72\x65\x20\x35\x2e\x35\x2e\x30\x22\
|
||||
\x3e\x0a\x20\x3c\x72\x64\x66\x3a\x52\x44\x46\x20\x78\x6d\x6c\x6e\
|
||||
\x73\x3a\x72\x64\x66\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\
|
||||
\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\x39\x39\x2f\x30\x32\
|
||||
\x2f\x32\x32\x2d\x72\x64\x66\x2d\x73\x79\x6e\x74\x61\x78\x2d\x6e\
|
||||
\x73\x23\x22\x3e\x0a\x20\x20\x3c\x72\x64\x66\x3a\x44\x65\x73\x63\
|
||||
\x72\x69\x70\x74\x69\x6f\x6e\x20\x72\x64\x66\x3a\x61\x62\x6f\x75\
|
||||
\x74\x3d\x22\x22\x0a\x20\x20\x20\x20\x78\x6d\x6c\x6e\x73\x3a\x65\
|
||||
\x78\x69\x66\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x6e\x73\x2e\x61\
|
||||
\x64\x6f\x62\x65\x2e\x63\x6f\x6d\x2f\x65\x78\x69\x66\x2f\x31\x2e\
|
||||
\x30\x2f\x22\x0a\x20\x20\x20\x20\x78\x6d\x6c\x6e\x73\x3a\x74\x69\
|
||||
\x66\x66\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x6e\x73\x2e\x61\x64\
|
||||
\x6f\x62\x65\x2e\x63\x6f\x6d\x2f\x74\x69\x66\x66\x2f\x31\x2e\x30\
|
||||
\x2f\x22\x0a\x20\x20\x20\x20\x78\x6d\x6c\x6e\x73\x3a\x70\x68\x6f\
|
||||
\x74\x6f\x73\x68\x6f\x70\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x6e\
|
||||
\x73\x2e\x61\x64\x6f\x62\x65\x2e\x63\x6f\x6d\x2f\x70\x68\x6f\x74\
|
||||
\x6f\x73\x68\x6f\x70\x2f\x31\x2e\x30\x2f\x22\x0a\x20\x20\x20\x20\
|
||||
\x78\x6d\x6c\x6e\x73\x3a\x78\x6d\x70\x3d\x22\x68\x74\x74\x70\x3a\
|
||||
\x2f\x2f\x6e\x73\x2e\x61\x64\x6f\x62\x65\x2e\x63\x6f\x6d\x2f\x78\
|
||||
\x61\x70\x2f\x31\x2e\x30\x2f\x22\x0a\x20\x20\x20\x20\x78\x6d\x6c\
|
||||
\x6e\x73\x3a\x78\x6d\x70\x4d\x4d\x3d\x22\x68\x74\x74\x70\x3a\x2f\
|
||||
\x2f\x6e\x73\x2e\x61\x64\x6f\x62\x65\x2e\x63\x6f\x6d\x2f\x78\x61\
|
||||
\x70\x2f\x31\x2e\x30\x2f\x6d\x6d\x2f\x22\x0a\x20\x20\x20\x20\x78\
|
||||
\x6d\x6c\x6e\x73\x3a\x73\x74\x45\x76\x74\x3d\x22\x68\x74\x74\x70\
|
||||
\x3a\x2f\x2f\x6e\x73\x2e\x61\x64\x6f\x62\x65\x2e\x63\x6f\x6d\x2f\
|
||||
\x78\x61\x70\x2f\x31\x2e\x30\x2f\x73\x54\x79\x70\x65\x2f\x52\x65\
|
||||
\x73\x6f\x75\x72\x63\x65\x45\x76\x65\x6e\x74\x23\x22\x0a\x20\x20\
|
||||
\x20\x65\x78\x69\x66\x3a\x50\x69\x78\x65\x6c\x58\x44\x69\x6d\x65\
|
||||
\x6e\x73\x69\x6f\x6e\x3d\x22\x31\x30\x22\x0a\x20\x20\x20\x65\x78\
|
||||
\x69\x66\x3a\x50\x69\x78\x65\x6c\x59\x44\x69\x6d\x65\x6e\x73\x69\
|
||||
\x6f\x6e\x3d\x22\x37\x22\x0a\x20\x20\x20\x65\x78\x69\x66\x3a\x43\
|
||||
\x6f\x6c\x6f\x72\x53\x70\x61\x63\x65\x3d\x22\x31\x22\x0a\x20\x20\
|
||||
\x20\x74\x69\x66\x66\x3a\x49\x6d\x61\x67\x65\x57\x69\x64\x74\x68\
|
||||
\x3d\x22\x31\x30\x22\x0a\x20\x20\x20\x74\x69\x66\x66\x3a\x49\x6d\
|
||||
\x61\x67\x65\x4c\x65\x6e\x67\x74\x68\x3d\x22\x37\x22\x0a\x20\x20\
|
||||
\x20\x74\x69\x66\x66\x3a\x52\x65\x73\x6f\x6c\x75\x74\x69\x6f\x6e\
|
||||
\x55\x6e\x69\x74\x3d\x22\x32\x22\x0a\x20\x20\x20\x74\x69\x66\x66\
|
||||
\x3a\x58\x52\x65\x73\x6f\x6c\x75\x74\x69\x6f\x6e\x3d\x22\x37\x32\
|
||||
\x2e\x30\x22\x0a\x20\x20\x20\x74\x69\x66\x66\x3a\x59\x52\x65\x73\
|
||||
\x6f\x6c\x75\x74\x69\x6f\x6e\x3d\x22\x37\x32\x2e\x30\x22\x0a\x20\
|
||||
\x20\x20\x70\x68\x6f\x74\x6f\x73\x68\x6f\x70\x3a\x43\x6f\x6c\x6f\
|
||||
\x72\x4d\x6f\x64\x65\x3d\x22\x33\x22\x0a\x20\x20\x20\x70\x68\x6f\
|
||||
\x74\x6f\x73\x68\x6f\x70\x3a\x49\x43\x43\x50\x72\x6f\x66\x69\x6c\
|
||||
\x65\x3d\x22\x73\x52\x47\x42\x20\x49\x45\x43\x36\x31\x39\x36\x36\
|
||||
\x2d\x32\x2e\x31\x22\x0a\x20\x20\x20\x78\x6d\x70\x3a\x4d\x6f\x64\
|
||||
\x69\x66\x79\x44\x61\x74\x65\x3d\x22\x32\x30\x32\x31\x2d\x30\x35\
|
||||
\x2d\x33\x31\x54\x31\x32\x3a\x33\x33\x3a\x31\x34\x2b\x30\x32\x3a\
|
||||
\x30\x30\x22\x0a\x20\x20\x20\x78\x6d\x70\x3a\x4d\x65\x74\x61\x64\
|
||||
\x61\x74\x61\x44\x61\x74\x65\x3d\x22\x32\x30\x32\x31\x2d\x30\x35\
|
||||
\x2d\x33\x31\x54\x31\x32\x3a\x33\x33\x3a\x31\x34\x2b\x30\x32\x3a\
|
||||
\x30\x30\x22\x3e\x0a\x20\x20\x20\x3c\x78\x6d\x70\x4d\x4d\x3a\x48\
|
||||
\x69\x73\x74\x6f\x72\x79\x3e\x0a\x20\x20\x20\x20\x3c\x72\x64\x66\
|
||||
\x3a\x53\x65\x71\x3e\x0a\x20\x20\x20\x20\x20\x3c\x72\x64\x66\x3a\
|
||||
\x6c\x69\x0a\x20\x20\x20\x20\x20\x20\x73\x74\x45\x76\x74\x3a\x61\
|
||||
\x63\x74\x69\x6f\x6e\x3d\x22\x70\x72\x6f\x64\x75\x63\x65\x64\x22\
|
||||
\x0a\x20\x20\x20\x20\x20\x20\x73\x74\x45\x76\x74\x3a\x73\x6f\x66\
|
||||
\x74\x77\x61\x72\x65\x41\x67\x65\x6e\x74\x3d\x22\x41\x66\x66\x69\
|
||||
\x6e\x69\x74\x79\x20\x44\x65\x73\x69\x67\x6e\x65\x72\x20\x31\x2e\
|
||||
\x39\x2e\x32\x22\x0a\x20\x20\x20\x20\x20\x20\x73\x74\x45\x76\x74\
|
||||
\x3a\x77\x68\x65\x6e\x3d\x22\x32\x30\x32\x31\x2d\x30\x35\x2d\x33\
|
||||
\x31\x54\x31\x32\x3a\x33\x33\x3a\x31\x34\x2b\x30\x32\x3a\x30\x30\
|
||||
\x22\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x2f\x72\x64\x66\x3a\x53\x65\
|
||||
\x71\x3e\x0a\x20\x20\x20\x3c\x2f\x78\x6d\x70\x4d\x4d\x3a\x48\x69\
|
||||
\x73\x74\x6f\x72\x79\x3e\x0a\x20\x20\x3c\x2f\x72\x64\x66\x3a\x44\
|
||||
\x65\x73\x63\x72\x69\x70\x74\x69\x6f\x6e\x3e\x0a\x20\x3c\x2f\x72\
|
||||
\x64\x66\x3a\x52\x44\x46\x3e\x0a\x3c\x2f\x78\x3a\x78\x6d\x70\x6d\
|
||||
\x65\x74\x61\x3e\x0a\x3c\x3f\x78\x70\x61\x63\x6b\x65\x74\x20\x65\
|
||||
\x6e\x64\x3d\x22\x72\x22\x3f\x3e\x48\x8b\x5b\x5e\x00\x00\x01\x83\
|
||||
\x69\x43\x43\x50\x73\x52\x47\x42\x20\x49\x45\x43\x36\x31\x39\x36\
|
||||
\x36\x2d\x32\x2e\x31\x00\x00\x28\x91\x75\x91\xcf\x2b\x44\x51\x14\
|
||||
\xc7\x3f\x66\x68\xfc\x18\x8d\x62\x61\x31\x65\x12\x16\x42\x83\x12\
|
||||
\x1b\x8b\x99\x18\x0a\x8b\x99\x51\x7e\x6d\x66\x9e\x79\x33\x6a\xde\
|
||||
\x78\xbd\x37\xd2\x64\xab\x6c\xa7\x28\xb1\xf1\x6b\xc1\x5f\xc0\x56\
|
||||
\x59\x2b\x45\xa4\x64\xa7\xac\x89\x0d\x7a\xce\x9b\x51\x23\x99\x73\
|
||||
\x3b\xf7\x7c\xee\xf7\xde\x73\xba\xf7\x5c\x70\x44\xd3\x8a\x66\x56\
|
||||
\xfa\x41\xcb\x64\x8d\x70\x28\xe0\x9b\x99\x9d\xf3\xb9\x9e\xa8\xa2\
|
||||
\x85\x1a\x3a\xf1\xc6\x14\x53\x9f\x8c\x8c\x46\x29\x6b\xef\xb7\x54\
|
||||
\xd8\xf1\xba\xdb\xae\x55\xfe\xdc\xbf\x56\xb7\x98\x30\x15\xa8\xa8\
|
||||
\x16\x1e\x56\x74\x23\x2b\x3c\x26\x3c\xb1\x9a\xd5\x6d\xde\x12\x6e\
|
||||
\x52\x52\xb1\x45\xe1\x13\xe1\x2e\x43\x2e\x28\x7c\x63\xeb\xf1\x22\
|
||||
\x3f\xdb\x9c\x2c\xf2\xa7\xcd\x46\x34\x1c\x04\x47\x83\xb0\x2f\xf9\
|
||||
\x8b\xe3\xbf\x58\x49\x19\x9a\xb0\xbc\x9c\x36\x2d\xbd\xa2\xfc\xdc\
|
||||
\xc7\x7e\x89\x3b\x91\x99\x8e\x48\x6c\x15\xf7\x62\x12\x26\x44\x00\
|
||||
\x1f\xe3\x8c\x10\x64\x80\x5e\x86\x64\x1e\xa0\x9b\x3e\x7a\x64\x45\
|
||||
\x99\x7c\x7f\x21\x7f\x8a\x65\xc9\x55\x64\xd6\xc9\x61\xb0\x44\x92\
|
||||
\x14\x59\xba\x44\x5d\x91\xea\x09\x89\xaa\xe8\x09\x19\x69\x72\x76\
|
||||
\xff\xff\xf6\xd5\x54\xfb\xfb\x8a\xd5\xdd\x01\xa8\x7a\xb4\xac\xd7\
|
||||
\x76\x70\x6d\xc2\x57\xde\xb2\x3e\x0e\x2c\xeb\xeb\x10\x9c\x0f\x70\
|
||||
\x9e\x29\xe5\x2f\xef\xc3\xe0\x9b\xe8\xf9\x92\xd6\xb6\x07\x9e\x75\
|
||||
\x38\xbd\x28\x69\xf1\x6d\x38\xdb\x80\xe6\x7b\x3d\x66\xc4\x0a\x92\
|
||||
\x53\xdc\xa1\xaa\xf0\x72\x0c\xf5\xb3\xd0\x78\x05\xb5\xf3\xc5\x9e\
|
||||
\xfd\xec\x73\x74\x07\xd1\x35\xf9\xaa\x4b\xd8\xd9\x85\x0e\x39\xef\
|
||||
\x59\xf8\x06\x8e\xfd\x67\xf8\xfd\x8a\x18\x97\x00\x00\x00\x09\x70\
|
||||
\x48\x59\x73\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\
|
||||
\x00\x00\x00\x97\x49\x44\x41\x54\x18\x95\x6d\xcf\xb1\x6a\x02\x41\
|
||||
\x14\x85\xe1\x6f\xb7\xb6\xd0\x27\x48\x3d\x56\x69\x03\xb1\xb4\x48\
|
||||
\x3b\x6c\xa5\xf1\x39\xf6\x59\x02\x56\x42\xba\x61\x0a\x0b\x3b\x1b\
|
||||
\x1b\x6b\x41\x18\x02\x29\x6d\xe3\xbe\x82\xcd\x06\x16\xd9\xdb\xdd\
|
||||
\x9f\xff\x5c\xee\xa9\x62\x2a\x13\x4c\x73\x13\x6e\x46\x26\xa6\xf2\
|
||||
\x82\xae\x46\x8b\xdf\x98\xca\xfb\x88\xb4\xc0\x0f\xda\x1a\x5b\x74\
|
||||
\xd8\xc7\x54\xc2\x40\x9a\x63\x8f\x3f\x7c\x55\x3d\x7c\xc5\x09\x77\
|
||||
\xbc\xa1\xc2\x19\x33\x2c\x72\x13\x2e\xd5\xe0\xc2\x12\x07\x5c\x51\
|
||||
\x23\xe0\x23\x37\xe1\xa8\x4f\x0e\x7f\xda\x60\xd7\xaf\x9f\xb9\x09\
|
||||
\xdf\x63\x05\xff\xe5\x75\x4c\x65\xf5\xcc\x1f\x0d\x33\x2c\x83\xb6\
|
||||
\x06\x44\x83\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
|
||||
\x00\x00\x00\xa0\
|
||||
\x89\
|
||||
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
|
||||
\x00\x00\x06\x00\x00\x00\x09\x08\x04\x00\x00\x00\xbb\x93\x95\x16\
|
||||
\x00\x00\x00\x01\x73\x52\x47\x42\x00\xae\xce\x1c\xe9\x00\x00\x00\
|
||||
\x02\x62\x4b\x47\x44\x00\xff\x87\x8f\xcc\xbf\x00\x00\x00\x09\x70\
|
||||
\x48\x59\x73\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\
|
||||
\x00\x00\x00\x07\x74\x49\x4d\x45\x07\xdc\x08\x17\x14\x1c\x1f\x24\
|
||||
\xc6\x09\x17\x00\x00\x00\x24\x49\x44\x41\x54\x08\xd7\x63\x60\x40\
|
||||
\x05\xff\xcf\xc3\x58\x4c\xc8\x5c\x26\x64\x59\x26\x64\xc5\x70\x0e\
|
||||
\xa3\x21\x9c\xc3\x68\x88\x61\x1a\x0a\x00\x00\x6d\x84\x09\x75\x37\
|
||||
\x9e\xd9\x23\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
|
||||
\x00\x00\x00\xa6\
|
||||
\x89\
|
||||
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
|
||||
\x00\x00\x06\x00\x00\x00\x09\x08\x04\x00\x00\x00\xbb\x93\x95\x16\
|
||||
\x00\x00\x00\x01\x73\x52\x47\x42\x00\xae\xce\x1c\xe9\x00\x00\x00\
|
||||
\x02\x62\x4b\x47\x44\x00\xff\x87\x8f\xcc\xbf\x00\x00\x00\x09\x70\
|
||||
\x48\x59\x73\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\
|
||||
\x00\x00\x00\x07\x74\x49\x4d\x45\x07\xdc\x08\x17\x14\x1d\x00\xb0\
|
||||
\xd5\x35\xa3\x00\x00\x00\x2a\x49\x44\x41\x54\x08\xd7\x63\x60\xc0\
|
||||
\x06\xfe\x9f\x67\x60\x60\x42\x30\xa1\x1c\x08\x93\x81\x81\x09\xc1\
|
||||
\x64\x60\x60\x62\x60\x60\x34\x44\xe2\x20\x73\x19\x90\x8d\x40\x02\
|
||||
\x00\x64\x40\x09\x75\x86\xb3\xad\x9c\x00\x00\x00\x00\x49\x45\x4e\
|
||||
\x44\xae\x42\x60\x82\
|
||||
\x00\x00\x07\xdd\
|
||||
\x89\
|
||||
|
|
@ -479,45 +667,6 @@ qt_resource_data = b"\
|
|||
\x71\x5b\x73\x5c\x40\x48\xa5\xdd\x61\x81\x0d\x9e\x6b\x8e\xff\xfd\
|
||||
\xcf\x3f\xcc\x31\xe9\x01\x1c\x00\x73\x52\x2d\x71\xe4\x4a\x1b\x69\
|
||||
\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
|
||||
\x00\x00\x00\xa6\
|
||||
\x89\
|
||||
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
|
||||
\x00\x00\x09\x00\x00\x00\x06\x08\x04\x00\x00\x00\xbb\xce\x7c\x4e\
|
||||
\x00\x00\x00\x01\x73\x52\x47\x42\x00\xae\xce\x1c\xe9\x00\x00\x00\
|
||||
\x02\x62\x4b\x47\x44\x00\xff\x87\x8f\xcc\xbf\x00\x00\x00\x09\x70\
|
||||
\x48\x59\x73\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\
|
||||
\x00\x00\x00\x07\x74\x49\x4d\x45\x07\xdc\x08\x17\x08\x15\x3b\xdc\
|
||||
\x3b\x0c\x9b\x00\x00\x00\x2a\x49\x44\x41\x54\x08\xd7\x63\x60\xc0\
|
||||
\x00\x8c\x0c\x0c\x73\x3e\x20\x0b\xa4\x08\x30\x32\x30\x20\x0b\xa6\
|
||||
\x08\x30\x30\x30\x42\x98\x10\xc1\x14\x01\x14\x13\x50\xb5\xa3\x01\
|
||||
\x00\xc6\xb9\x07\x90\x5d\x66\x1f\x83\x00\x00\x00\x00\x49\x45\x4e\
|
||||
\x44\xae\x42\x60\x82\
|
||||
\x00\x00\x00\xa5\
|
||||
\x89\
|
||||
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
|
||||
\x00\x00\x09\x00\x00\x00\x06\x08\x04\x00\x00\x00\xbb\xce\x7c\x4e\
|
||||
\x00\x00\x00\x01\x73\x52\x47\x42\x00\xae\xce\x1c\xe9\x00\x00\x00\
|
||||
\x02\x62\x4b\x47\x44\x00\x9c\x53\x34\xfc\x5d\x00\x00\x00\x09\x70\
|
||||
\x48\x59\x73\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\
|
||||
\x00\x00\x00\x07\x74\x49\x4d\x45\x07\xdc\x08\x17\x0b\x02\x04\x6d\
|
||||
\x98\x1b\x69\x00\x00\x00\x29\x49\x44\x41\x54\x08\xd7\x63\x60\xc0\
|
||||
\x00\x8c\x0c\x0c\xff\xcf\xa3\x08\x18\x32\x32\x30\x20\x0b\x32\x1a\
|
||||
\x32\x30\x30\x42\x98\x10\x41\x46\x43\x14\x13\x50\xb5\xa3\x01\x00\
|
||||
\xd6\x10\x07\xd2\x2f\x48\xdf\x4a\x00\x00\x00\x00\x49\x45\x4e\x44\
|
||||
\xae\x42\x60\x82\
|
||||
\x00\x00\x00\xa6\
|
||||
\x89\
|
||||
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
|
||||
\x00\x00\x06\x00\x00\x00\x09\x08\x04\x00\x00\x00\xbb\x93\x95\x16\
|
||||
\x00\x00\x00\x01\x73\x52\x47\x42\x00\xae\xce\x1c\xe9\x00\x00\x00\
|
||||
\x02\x62\x4b\x47\x44\x00\xff\x87\x8f\xcc\xbf\x00\x00\x00\x09\x70\
|
||||
\x48\x59\x73\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\
|
||||
\x00\x00\x00\x07\x74\x49\x4d\x45\x07\xdc\x08\x17\x14\x1f\x20\xb9\
|
||||
\x8d\x77\xe9\x00\x00\x00\x2a\x49\x44\x41\x54\x08\xd7\x63\x60\xc0\
|
||||
\x06\xe6\x7c\x60\x60\x60\x42\x30\xa1\x1c\x08\x93\x81\x81\x09\xc1\
|
||||
\x64\x60\x60\x62\x60\x48\x11\x40\xe2\x20\x73\x19\x90\x8d\x40\x02\
|
||||
\x00\x23\xed\x08\xaf\x64\x9f\x0f\x15\x00\x00\x00\x00\x49\x45\x4e\
|
||||
\x44\xae\x42\x60\x82\
|
||||
\x00\x00\x00\x9e\
|
||||
\x89\
|
||||
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
|
||||
|
|
@ -530,160 +679,18 @@ qt_resource_data = b"\
|
|||
\x0d\xfe\x9f\x87\xb1\x18\x91\x05\x18\x0d\xe1\x42\x48\x2a\x0c\x19\
|
||||
\x18\x18\x91\x05\x10\x2a\xd1\x00\x00\xca\xb5\x07\xd2\x76\xbb\xb2\
|
||||
\xc5\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
|
||||
\x00\x00\x00\xa6\
|
||||
\x00\x00\x00\x9e\
|
||||
\x89\
|
||||
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
|
||||
\x00\x00\x09\x00\x00\x00\x06\x08\x04\x00\x00\x00\xbb\xce\x7c\x4e\
|
||||
\x00\x00\x00\x01\x73\x52\x47\x42\x00\xae\xce\x1c\xe9\x00\x00\x00\
|
||||
\x02\x62\x4b\x47\x44\x00\xff\x87\x8f\xcc\xbf\x00\x00\x00\x09\x70\
|
||||
\x48\x59\x73\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\
|
||||
\x00\x00\x00\x07\x74\x49\x4d\x45\x07\xdc\x08\x17\x08\x15\x3b\xdc\
|
||||
\x3b\x0c\x9b\x00\x00\x00\x2a\x49\x44\x41\x54\x08\xd7\x63\x60\xc0\
|
||||
\x00\x8c\x0c\x0c\x73\x3e\x20\x0b\xa4\x08\x30\x32\x30\x20\x0b\xa6\
|
||||
\x08\x30\x30\x30\x42\x98\x10\xc1\x14\x01\x14\x13\x50\xb5\xa3\x01\
|
||||
\x00\xc6\xb9\x07\x90\x5d\x66\x1f\x83\x00\x00\x00\x00\x49\x45\x4e\
|
||||
\x44\xae\x42\x60\x82\
|
||||
\x00\x00\x00\xa6\
|
||||
\x89\
|
||||
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
|
||||
\x00\x00\x06\x00\x00\x00\x09\x08\x04\x00\x00\x00\xbb\x93\x95\x16\
|
||||
\x00\x00\x00\x01\x73\x52\x47\x42\x00\xae\xce\x1c\xe9\x00\x00\x00\
|
||||
\x02\x62\x4b\x47\x44\x00\xff\x87\x8f\xcc\xbf\x00\x00\x00\x09\x70\
|
||||
\x48\x59\x73\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\
|
||||
\x00\x00\x00\x07\x74\x49\x4d\x45\x07\xdc\x08\x17\x14\x1d\x00\xb0\
|
||||
\xd5\x35\xa3\x00\x00\x00\x2a\x49\x44\x41\x54\x08\xd7\x63\x60\xc0\
|
||||
\x06\xfe\x9f\x67\x60\x60\x42\x30\xa1\x1c\x08\x93\x81\x81\x09\xc1\
|
||||
\x64\x60\x60\x62\x60\x60\x34\x44\xe2\x20\x73\x19\x90\x8d\x40\x02\
|
||||
\x00\x64\x40\x09\x75\x86\xb3\xad\x9c\x00\x00\x00\x00\x49\x45\x4e\
|
||||
\x44\xae\x42\x60\x82\
|
||||
\x00\x00\x00\xa6\
|
||||
\x89\
|
||||
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
|
||||
\x00\x00\x06\x00\x00\x00\x09\x08\x04\x00\x00\x00\xbb\x93\x95\x16\
|
||||
\x00\x00\x00\x01\x73\x52\x47\x42\x00\xae\xce\x1c\xe9\x00\x00\x00\
|
||||
\x02\x62\x4b\x47\x44\x00\xff\x87\x8f\xcc\xbf\x00\x00\x00\x09\x70\
|
||||
\x48\x59\x73\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\
|
||||
\x00\x00\x00\x07\x74\x49\x4d\x45\x07\xdc\x08\x17\x14\x1d\x00\xb0\
|
||||
\xd5\x35\xa3\x00\x00\x00\x2a\x49\x44\x41\x54\x08\xd7\x63\x60\xc0\
|
||||
\x06\xfe\x9f\x67\x60\x60\x42\x30\xa1\x1c\x08\x93\x81\x81\x09\xc1\
|
||||
\x64\x60\x60\x62\x60\x60\x34\x44\xe2\x20\x73\x19\x90\x8d\x40\x02\
|
||||
\x00\x64\x40\x09\x75\x86\xb3\xad\x9c\x00\x00\x00\x00\x49\x45\x4e\
|
||||
\x44\xae\x42\x60\x82\
|
||||
\x00\x00\x07\x06\
|
||||
\x89\
|
||||
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
|
||||
\x00\x00\x0a\x00\x00\x00\x07\x08\x06\x00\x00\x00\x31\xac\xdc\x63\
|
||||
\x00\x00\x04\xb0\x69\x54\x58\x74\x58\x4d\x4c\x3a\x63\x6f\x6d\x2e\
|
||||
\x61\x64\x6f\x62\x65\x2e\x78\x6d\x70\x00\x00\x00\x00\x00\x3c\x3f\
|
||||
\x78\x70\x61\x63\x6b\x65\x74\x20\x62\x65\x67\x69\x6e\x3d\x22\xef\
|
||||
\xbb\xbf\x22\x20\x69\x64\x3d\x22\x57\x35\x4d\x30\x4d\x70\x43\x65\
|
||||
\x68\x69\x48\x7a\x72\x65\x53\x7a\x4e\x54\x63\x7a\x6b\x63\x39\x64\
|
||||
\x22\x3f\x3e\x0a\x3c\x78\x3a\x78\x6d\x70\x6d\x65\x74\x61\x20\x78\
|
||||
\x6d\x6c\x6e\x73\x3a\x78\x3d\x22\x61\x64\x6f\x62\x65\x3a\x6e\x73\
|
||||
\x3a\x6d\x65\x74\x61\x2f\x22\x20\x78\x3a\x78\x6d\x70\x74\x6b\x3d\
|
||||
\x22\x58\x4d\x50\x20\x43\x6f\x72\x65\x20\x35\x2e\x35\x2e\x30\x22\
|
||||
\x3e\x0a\x20\x3c\x72\x64\x66\x3a\x52\x44\x46\x20\x78\x6d\x6c\x6e\
|
||||
\x73\x3a\x72\x64\x66\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\
|
||||
\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\x39\x39\x2f\x30\x32\
|
||||
\x2f\x32\x32\x2d\x72\x64\x66\x2d\x73\x79\x6e\x74\x61\x78\x2d\x6e\
|
||||
\x73\x23\x22\x3e\x0a\x20\x20\x3c\x72\x64\x66\x3a\x44\x65\x73\x63\
|
||||
\x72\x69\x70\x74\x69\x6f\x6e\x20\x72\x64\x66\x3a\x61\x62\x6f\x75\
|
||||
\x74\x3d\x22\x22\x0a\x20\x20\x20\x20\x78\x6d\x6c\x6e\x73\x3a\x65\
|
||||
\x78\x69\x66\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x6e\x73\x2e\x61\
|
||||
\x64\x6f\x62\x65\x2e\x63\x6f\x6d\x2f\x65\x78\x69\x66\x2f\x31\x2e\
|
||||
\x30\x2f\x22\x0a\x20\x20\x20\x20\x78\x6d\x6c\x6e\x73\x3a\x74\x69\
|
||||
\x66\x66\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x6e\x73\x2e\x61\x64\
|
||||
\x6f\x62\x65\x2e\x63\x6f\x6d\x2f\x74\x69\x66\x66\x2f\x31\x2e\x30\
|
||||
\x2f\x22\x0a\x20\x20\x20\x20\x78\x6d\x6c\x6e\x73\x3a\x70\x68\x6f\
|
||||
\x74\x6f\x73\x68\x6f\x70\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x6e\
|
||||
\x73\x2e\x61\x64\x6f\x62\x65\x2e\x63\x6f\x6d\x2f\x70\x68\x6f\x74\
|
||||
\x6f\x73\x68\x6f\x70\x2f\x31\x2e\x30\x2f\x22\x0a\x20\x20\x20\x20\
|
||||
\x78\x6d\x6c\x6e\x73\x3a\x78\x6d\x70\x3d\x22\x68\x74\x74\x70\x3a\
|
||||
\x2f\x2f\x6e\x73\x2e\x61\x64\x6f\x62\x65\x2e\x63\x6f\x6d\x2f\x78\
|
||||
\x61\x70\x2f\x31\x2e\x30\x2f\x22\x0a\x20\x20\x20\x20\x78\x6d\x6c\
|
||||
\x6e\x73\x3a\x78\x6d\x70\x4d\x4d\x3d\x22\x68\x74\x74\x70\x3a\x2f\
|
||||
\x2f\x6e\x73\x2e\x61\x64\x6f\x62\x65\x2e\x63\x6f\x6d\x2f\x78\x61\
|
||||
\x70\x2f\x31\x2e\x30\x2f\x6d\x6d\x2f\x22\x0a\x20\x20\x20\x20\x78\
|
||||
\x6d\x6c\x6e\x73\x3a\x73\x74\x45\x76\x74\x3d\x22\x68\x74\x74\x70\
|
||||
\x3a\x2f\x2f\x6e\x73\x2e\x61\x64\x6f\x62\x65\x2e\x63\x6f\x6d\x2f\
|
||||
\x78\x61\x70\x2f\x31\x2e\x30\x2f\x73\x54\x79\x70\x65\x2f\x52\x65\
|
||||
\x73\x6f\x75\x72\x63\x65\x45\x76\x65\x6e\x74\x23\x22\x0a\x20\x20\
|
||||
\x20\x65\x78\x69\x66\x3a\x50\x69\x78\x65\x6c\x58\x44\x69\x6d\x65\
|
||||
\x6e\x73\x69\x6f\x6e\x3d\x22\x31\x30\x22\x0a\x20\x20\x20\x65\x78\
|
||||
\x69\x66\x3a\x50\x69\x78\x65\x6c\x59\x44\x69\x6d\x65\x6e\x73\x69\
|
||||
\x6f\x6e\x3d\x22\x37\x22\x0a\x20\x20\x20\x65\x78\x69\x66\x3a\x43\
|
||||
\x6f\x6c\x6f\x72\x53\x70\x61\x63\x65\x3d\x22\x31\x22\x0a\x20\x20\
|
||||
\x20\x74\x69\x66\x66\x3a\x49\x6d\x61\x67\x65\x57\x69\x64\x74\x68\
|
||||
\x3d\x22\x31\x30\x22\x0a\x20\x20\x20\x74\x69\x66\x66\x3a\x49\x6d\
|
||||
\x61\x67\x65\x4c\x65\x6e\x67\x74\x68\x3d\x22\x37\x22\x0a\x20\x20\
|
||||
\x20\x74\x69\x66\x66\x3a\x52\x65\x73\x6f\x6c\x75\x74\x69\x6f\x6e\
|
||||
\x55\x6e\x69\x74\x3d\x22\x32\x22\x0a\x20\x20\x20\x74\x69\x66\x66\
|
||||
\x3a\x58\x52\x65\x73\x6f\x6c\x75\x74\x69\x6f\x6e\x3d\x22\x37\x32\
|
||||
\x2e\x30\x22\x0a\x20\x20\x20\x74\x69\x66\x66\x3a\x59\x52\x65\x73\
|
||||
\x6f\x6c\x75\x74\x69\x6f\x6e\x3d\x22\x37\x32\x2e\x30\x22\x0a\x20\
|
||||
\x20\x20\x70\x68\x6f\x74\x6f\x73\x68\x6f\x70\x3a\x43\x6f\x6c\x6f\
|
||||
\x72\x4d\x6f\x64\x65\x3d\x22\x33\x22\x0a\x20\x20\x20\x70\x68\x6f\
|
||||
\x74\x6f\x73\x68\x6f\x70\x3a\x49\x43\x43\x50\x72\x6f\x66\x69\x6c\
|
||||
\x65\x3d\x22\x73\x52\x47\x42\x20\x49\x45\x43\x36\x31\x39\x36\x36\
|
||||
\x2d\x32\x2e\x31\x22\x0a\x20\x20\x20\x78\x6d\x70\x3a\x4d\x6f\x64\
|
||||
\x69\x66\x79\x44\x61\x74\x65\x3d\x22\x32\x30\x32\x31\x2d\x30\x35\
|
||||
\x2d\x33\x31\x54\x31\x32\x3a\x33\x30\x3a\x31\x31\x2b\x30\x32\x3a\
|
||||
\x30\x30\x22\x0a\x20\x20\x20\x78\x6d\x70\x3a\x4d\x65\x74\x61\x64\
|
||||
\x61\x74\x61\x44\x61\x74\x65\x3d\x22\x32\x30\x32\x31\x2d\x30\x35\
|
||||
\x2d\x33\x31\x54\x31\x32\x3a\x33\x30\x3a\x31\x31\x2b\x30\x32\x3a\
|
||||
\x30\x30\x22\x3e\x0a\x20\x20\x20\x3c\x78\x6d\x70\x4d\x4d\x3a\x48\
|
||||
\x69\x73\x74\x6f\x72\x79\x3e\x0a\x20\x20\x20\x20\x3c\x72\x64\x66\
|
||||
\x3a\x53\x65\x71\x3e\x0a\x20\x20\x20\x20\x20\x3c\x72\x64\x66\x3a\
|
||||
\x6c\x69\x0a\x20\x20\x20\x20\x20\x20\x73\x74\x45\x76\x74\x3a\x61\
|
||||
\x63\x74\x69\x6f\x6e\x3d\x22\x70\x72\x6f\x64\x75\x63\x65\x64\x22\
|
||||
\x0a\x20\x20\x20\x20\x20\x20\x73\x74\x45\x76\x74\x3a\x73\x6f\x66\
|
||||
\x74\x77\x61\x72\x65\x41\x67\x65\x6e\x74\x3d\x22\x41\x66\x66\x69\
|
||||
\x6e\x69\x74\x79\x20\x44\x65\x73\x69\x67\x6e\x65\x72\x20\x31\x2e\
|
||||
\x39\x2e\x32\x22\x0a\x20\x20\x20\x20\x20\x20\x73\x74\x45\x76\x74\
|
||||
\x3a\x77\x68\x65\x6e\x3d\x22\x32\x30\x32\x31\x2d\x30\x35\x2d\x33\
|
||||
\x31\x54\x31\x32\x3a\x33\x30\x3a\x31\x31\x2b\x30\x32\x3a\x30\x30\
|
||||
\x22\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x2f\x72\x64\x66\x3a\x53\x65\
|
||||
\x71\x3e\x0a\x20\x20\x20\x3c\x2f\x78\x6d\x70\x4d\x4d\x3a\x48\x69\
|
||||
\x73\x74\x6f\x72\x79\x3e\x0a\x20\x20\x3c\x2f\x72\x64\x66\x3a\x44\
|
||||
\x65\x73\x63\x72\x69\x70\x74\x69\x6f\x6e\x3e\x0a\x20\x3c\x2f\x72\
|
||||
\x64\x66\x3a\x52\x44\x46\x3e\x0a\x3c\x2f\x78\x3a\x78\x6d\x70\x6d\
|
||||
\x65\x74\x61\x3e\x0a\x3c\x3f\x78\x70\x61\x63\x6b\x65\x74\x20\x65\
|
||||
\x6e\x64\x3d\x22\x72\x22\x3f\x3e\x85\x9d\x9f\x08\x00\x00\x01\x83\
|
||||
\x69\x43\x43\x50\x73\x52\x47\x42\x20\x49\x45\x43\x36\x31\x39\x36\
|
||||
\x36\x2d\x32\x2e\x31\x00\x00\x28\x91\x75\x91\xcf\x2b\x44\x51\x14\
|
||||
\xc7\x3f\x66\x68\xfc\x18\x8d\x62\x61\x31\x65\x12\x16\x42\x83\x12\
|
||||
\x1b\x8b\x99\x18\x0a\x8b\x99\x51\x7e\x6d\x66\x9e\x79\x33\x6a\xde\
|
||||
\x78\xbd\x37\xd2\x64\xab\x6c\xa7\x28\xb1\xf1\x6b\xc1\x5f\xc0\x56\
|
||||
\x59\x2b\x45\xa4\x64\xa7\xac\x89\x0d\x7a\xce\x9b\x51\x23\x99\x73\
|
||||
\x3b\xf7\x7c\xee\xf7\xde\x73\xba\xf7\x5c\x70\x44\xd3\x8a\x66\x56\
|
||||
\xfa\x41\xcb\x64\x8d\x70\x28\xe0\x9b\x99\x9d\xf3\xb9\x9e\xa8\xa2\
|
||||
\x85\x1a\x3a\xf1\xc6\x14\x53\x9f\x8c\x8c\x46\x29\x6b\xef\xb7\x54\
|
||||
\xd8\xf1\xba\xdb\xae\x55\xfe\xdc\xbf\x56\xb7\x98\x30\x15\xa8\xa8\
|
||||
\x16\x1e\x56\x74\x23\x2b\x3c\x26\x3c\xb1\x9a\xd5\x6d\xde\x12\x6e\
|
||||
\x52\x52\xb1\x45\xe1\x13\xe1\x2e\x43\x2e\x28\x7c\x63\xeb\xf1\x22\
|
||||
\x3f\xdb\x9c\x2c\xf2\xa7\xcd\x46\x34\x1c\x04\x47\x83\xb0\x2f\xf9\
|
||||
\x8b\xe3\xbf\x58\x49\x19\x9a\xb0\xbc\x9c\x36\x2d\xbd\xa2\xfc\xdc\
|
||||
\xc7\x7e\x89\x3b\x91\x99\x8e\x48\x6c\x15\xf7\x62\x12\x26\x44\x00\
|
||||
\x1f\xe3\x8c\x10\x64\x80\x5e\x86\x64\x1e\xa0\x9b\x3e\x7a\x64\x45\
|
||||
\x99\x7c\x7f\x21\x7f\x8a\x65\xc9\x55\x64\xd6\xc9\x61\xb0\x44\x92\
|
||||
\x14\x59\xba\x44\x5d\x91\xea\x09\x89\xaa\xe8\x09\x19\x69\x72\x76\
|
||||
\xff\xff\xf6\xd5\x54\xfb\xfb\x8a\xd5\xdd\x01\xa8\x7a\xb4\xac\xd7\
|
||||
\x76\x70\x6d\xc2\x57\xde\xb2\x3e\x0e\x2c\xeb\xeb\x10\x9c\x0f\x70\
|
||||
\x9e\x29\xe5\x2f\xef\xc3\xe0\x9b\xe8\xf9\x92\xd6\xb6\x07\x9e\x75\
|
||||
\x38\xbd\x28\x69\xf1\x6d\x38\xdb\x80\xe6\x7b\x3d\x66\xc4\x0a\x92\
|
||||
\x53\xdc\xa1\xaa\xf0\x72\x0c\xf5\xb3\xd0\x78\x05\xb5\xf3\xc5\x9e\
|
||||
\xfd\xec\x73\x74\x07\xd1\x35\xf9\xaa\x4b\xd8\xd9\x85\x0e\x39\xef\
|
||||
\x59\xf8\x06\x8e\xfd\x67\xf8\xfd\x8a\x18\x97\x00\x00\x00\x09\x70\
|
||||
\x48\x59\x73\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\
|
||||
\x00\x00\x00\x6d\x49\x44\x41\x54\x18\x95\x75\xcf\xc1\x09\xc2\x50\
|
||||
\x10\x84\xe1\xd7\x85\x07\x9b\xd0\x43\x40\xd2\x82\x78\x14\x7b\x30\
|
||||
\x57\x21\x8d\x84\x60\x3f\x62\x4b\x7a\x48\xcc\x97\x83\xfb\x30\x04\
|
||||
\xdf\x9c\x86\x7f\x67\x99\xdd\x84\x0d\xaa\x54\x10\x6a\x6c\x13\x1e\
|
||||
\xbe\xba\xfe\x09\x35\x31\x7b\xe6\x8d\x0f\x26\x1c\x17\xa1\x53\xb0\
|
||||
\x11\x87\x0c\x2f\x01\x07\xec\xb0\x0f\x3f\xe1\xbc\xae\x69\xa3\xe6\
|
||||
\x85\x77\xf8\x5b\xe9\xf0\xbb\x9f\xfa\xd2\x83\x39\xdc\xa3\x5b\xf3\
|
||||
\x19\x2e\xa8\x89\xb5\x30\xf7\x43\xa0\x00\x00\x00\x00\x49\x45\x4e\
|
||||
\x44\xae\x42\x60\x82\
|
||||
\x00\x00\x00\x07\x74\x49\x4d\x45\x07\xdc\x08\x17\x08\x15\x0f\xfd\
|
||||
\x8f\xf8\x2e\x00\x00\x00\x22\x49\x44\x41\x54\x08\xd7\x63\x60\xc0\
|
||||
\x0d\xfe\x9f\x87\xb1\x18\x91\x05\x18\x0d\xe1\x42\x48\x2a\x0c\x19\
|
||||
\x18\x18\x91\x05\x10\x2a\xd1\x00\x00\xca\xb5\x07\xd2\x76\xbb\xb2\
|
||||
\xc5\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
|
||||
"
|
||||
|
||||
qt_resource_name = b"\
|
||||
|
|
@ -696,119 +703,124 @@ qt_resource_name = b"\
|
|||
\x00\x69\
|
||||
\x00\x6d\x00\x61\x00\x67\x00\x65\x00\x73\
|
||||
\x00\x0f\
|
||||
\x02\x9f\x05\x87\
|
||||
\x00\x72\
|
||||
\x00\x69\x00\x67\x00\x68\x00\x74\x00\x5f\x00\x61\x00\x72\x00\x72\x00\x6f\x00\x77\x00\x2e\x00\x70\x00\x6e\x00\x67\
|
||||
\x00\x12\
|
||||
\x05\x8f\x9d\x07\
|
||||
\x06\x53\x25\xa7\
|
||||
\x00\x62\
|
||||
\x00\x72\x00\x61\x00\x6e\x00\x63\x00\x68\x00\x5f\x00\x6f\x00\x70\x00\x65\x00\x6e\x00\x5f\x00\x6f\x00\x6e\x00\x2e\x00\x70\x00\x6e\
|
||||
\x00\x67\
|
||||
\x00\x1b\
|
||||
\x03\x5a\x32\x27\
|
||||
\x00\x63\
|
||||
\x00\x6f\x00\x6d\x00\x62\x00\x6f\x00\x62\x00\x6f\x00\x78\x00\x5f\x00\x61\x00\x72\x00\x72\x00\x6f\x00\x77\x00\x5f\x00\x64\x00\x69\
|
||||
\x00\x73\x00\x61\x00\x62\x00\x6c\x00\x65\x00\x64\x00\x2e\x00\x70\x00\x6e\x00\x67\
|
||||
\x00\x72\x00\x61\x00\x6e\x00\x63\x00\x68\x00\x5f\x00\x6f\x00\x70\x00\x65\x00\x6e\x00\x2e\x00\x70\x00\x6e\x00\x67\
|
||||
\x00\x18\
|
||||
\x03\x8e\xde\x67\
|
||||
\x00\x72\
|
||||
\x00\x69\x00\x67\x00\x68\x00\x74\x00\x5f\x00\x61\x00\x72\x00\x72\x00\x6f\x00\x77\x00\x5f\x00\x64\x00\x69\x00\x73\x00\x61\x00\x62\
|
||||
\x00\x6c\x00\x65\x00\x64\x00\x2e\x00\x70\x00\x6e\x00\x67\
|
||||
\x00\x11\
|
||||
\x0b\xda\x30\xa7\
|
||||
\x00\x62\
|
||||
\x00\x72\x00\x61\x00\x6e\x00\x63\x00\x68\x00\x5f\x00\x63\x00\x6c\x00\x6f\x00\x73\x00\x65\x00\x64\x00\x2e\x00\x70\x00\x6e\x00\x67\
|
||||
\
|
||||
\x00\x12\
|
||||
\x03\x8d\x04\x47\
|
||||
\x00\x72\
|
||||
\x00\x69\x00\x67\x00\x68\x00\x74\x00\x5f\x00\x61\x00\x72\x00\x72\x00\x6f\x00\x77\x00\x5f\x00\x6f\x00\x6e\x00\x2e\x00\x70\x00\x6e\
|
||||
\x00\x67\
|
||||
\x00\x15\
|
||||
\x0f\xf3\xc0\x07\
|
||||
\x00\x75\
|
||||
\x00\x70\x00\x5f\x00\x61\x00\x72\x00\x72\x00\x6f\x00\x77\x00\x5f\x00\x64\x00\x69\x00\x73\x00\x61\x00\x62\x00\x6c\x00\x65\x00\x64\
|
||||
\x00\x2e\x00\x70\x00\x6e\x00\x67\
|
||||
\x00\x0f\
|
||||
\x01\x73\x8b\x07\
|
||||
\x00\x75\
|
||||
\x00\x70\x00\x5f\x00\x61\x00\x72\x00\x72\x00\x6f\x00\x77\x00\x5f\x00\x6f\x00\x6e\x00\x2e\x00\x70\x00\x6e\x00\x67\
|
||||
\x00\x0e\
|
||||
\x04\xa2\xfc\xa7\
|
||||
\x00\x64\
|
||||
\x00\x6f\x00\x77\x00\x6e\x00\x5f\x00\x61\x00\x72\x00\x72\x00\x6f\x00\x77\x00\x2e\x00\x70\x00\x6e\x00\x67\
|
||||
\x00\x12\
|
||||
\x01\x2e\x03\x27\
|
||||
\x00\x63\
|
||||
\x00\x6f\x00\x6d\x00\x62\x00\x6f\x00\x62\x00\x6f\x00\x78\x00\x5f\x00\x61\x00\x72\x00\x72\x00\x6f\x00\x77\x00\x2e\x00\x70\x00\x6e\
|
||||
\x00\x67\
|
||||
\x00\x14\
|
||||
\x04\x5e\x2d\xa7\
|
||||
\x00\x62\
|
||||
\x00\x72\x00\x61\x00\x6e\x00\x63\x00\x68\x00\x5f\x00\x63\x00\x6c\x00\x6f\x00\x73\x00\x65\x00\x64\x00\x5f\x00\x6f\x00\x6e\x00\x2e\
|
||||
\x00\x70\x00\x6e\x00\x67\
|
||||
\x00\x17\
|
||||
\x0c\xab\x51\x07\
|
||||
\x00\x64\
|
||||
\x00\x6f\x00\x77\x00\x6e\x00\x5f\x00\x61\x00\x72\x00\x72\x00\x6f\x00\x77\x00\x5f\x00\x64\x00\x69\x00\x73\x00\x61\x00\x62\x00\x6c\
|
||||
\x00\x65\x00\x64\x00\x2e\x00\x70\x00\x6e\x00\x67\
|
||||
\x00\x11\
|
||||
\x01\x1f\xc3\x87\
|
||||
\x00\x64\
|
||||
\x00\x6f\x00\x77\x00\x6e\x00\x5f\x00\x61\x00\x72\x00\x72\x00\x6f\x00\x77\x00\x5f\x00\x6f\x00\x6e\x00\x2e\x00\x70\x00\x6e\x00\x67\
|
||||
\
|
||||
\x00\x17\
|
||||
\x0c\x65\xce\x07\
|
||||
\x00\x6c\
|
||||
\x00\x65\x00\x66\x00\x74\x00\x5f\x00\x61\x00\x72\x00\x72\x00\x6f\x00\x77\x00\x5f\x00\x64\x00\x69\x00\x73\x00\x61\x00\x62\x00\x6c\
|
||||
\x00\x65\x00\x64\x00\x2e\x00\x70\x00\x6e\x00\x67\
|
||||
\x00\x0c\
|
||||
\x06\xe6\xe6\x67\
|
||||
\x00\x75\
|
||||
\x00\x70\x00\x5f\x00\x61\x00\x72\x00\x72\x00\x6f\x00\x77\x00\x2e\x00\x70\x00\x6e\x00\x67\
|
||||
\x00\x15\
|
||||
\x03\x27\x72\x67\
|
||||
\x00\x63\
|
||||
\x00\x6f\x00\x6d\x00\x62\x00\x6f\x00\x62\x00\x6f\x00\x78\x00\x5f\x00\x61\x00\x72\x00\x72\x00\x6f\x00\x77\x00\x5f\x00\x6f\x00\x6e\
|
||||
\x00\x2e\x00\x70\x00\x6e\x00\x67\
|
||||
\x00\x11\
|
||||
\x0b\xda\x30\xa7\
|
||||
\x00\x62\
|
||||
\x00\x72\x00\x61\x00\x6e\x00\x63\x00\x68\x00\x5f\x00\x63\x00\x6c\x00\x6f\x00\x73\x00\x65\x00\x64\x00\x2e\x00\x70\x00\x6e\x00\x67\
|
||||
\
|
||||
\x00\x17\
|
||||
\x0c\xab\x51\x07\
|
||||
\x00\x64\
|
||||
\x00\x6f\x00\x77\x00\x6e\x00\x5f\x00\x61\x00\x72\x00\x72\x00\x6f\x00\x77\x00\x5f\x00\x64\x00\x69\x00\x73\x00\x61\x00\x62\x00\x6c\
|
||||
\x00\x65\x00\x64\x00\x2e\x00\x70\x00\x6e\x00\x67\
|
||||
\x00\x11\
|
||||
\x00\xb8\x8c\x07\
|
||||
\x00\x6c\
|
||||
\x00\x65\x00\x66\x00\x74\x00\x5f\x00\x61\x00\x72\x00\x72\x00\x6f\x00\x77\x00\x5f\x00\x6f\x00\x6e\x00\x2e\x00\x70\x00\x6e\x00\x67\
|
||||
\
|
||||
\x00\x12\
|
||||
\x01\x2e\x03\x27\
|
||||
\x00\x63\
|
||||
\x00\x6f\x00\x6d\x00\x62\x00\x6f\x00\x62\x00\x6f\x00\x78\x00\x5f\x00\x61\x00\x72\x00\x72\x00\x6f\x00\x77\x00\x2e\x00\x70\x00\x6e\
|
||||
\x00\x67\
|
||||
\x00\x0f\
|
||||
\x02\x9f\x05\x87\
|
||||
\x00\x72\
|
||||
\x00\x69\x00\x67\x00\x68\x00\x74\x00\x5f\x00\x61\x00\x72\x00\x72\x00\x6f\x00\x77\x00\x2e\x00\x70\x00\x6e\x00\x67\
|
||||
\x00\x15\
|
||||
\x0f\xf3\xc0\x07\
|
||||
\x00\x75\
|
||||
\x00\x70\x00\x5f\x00\x61\x00\x72\x00\x72\x00\x6f\x00\x77\x00\x5f\x00\x64\x00\x69\x00\x73\x00\x61\x00\x62\x00\x6c\x00\x65\x00\x64\
|
||||
\x00\x2e\x00\x70\x00\x6e\x00\x67\
|
||||
\x00\x0f\
|
||||
\x0c\xe2\x68\x67\
|
||||
\x00\x74\
|
||||
\x00\x72\x00\x61\x00\x6e\x00\x73\x00\x70\x00\x61\x00\x72\x00\x65\x00\x6e\x00\x74\x00\x2e\x00\x70\x00\x6e\x00\x67\
|
||||
\x00\x0e\
|
||||
\x04\xa2\xfc\xa7\
|
||||
\x00\x64\
|
||||
\x00\x6f\x00\x77\x00\x6e\x00\x5f\x00\x61\x00\x72\x00\x72\x00\x6f\x00\x77\x00\x2e\x00\x70\x00\x6e\x00\x67\
|
||||
\x00\x1b\
|
||||
\x03\x5a\x32\x27\
|
||||
\x00\x63\
|
||||
\x00\x6f\x00\x6d\x00\x62\x00\x6f\x00\x62\x00\x6f\x00\x78\x00\x5f\x00\x61\x00\x72\x00\x72\x00\x6f\x00\x77\x00\x5f\x00\x64\x00\x69\
|
||||
\x00\x73\x00\x61\x00\x62\x00\x6c\x00\x65\x00\x64\x00\x2e\x00\x70\x00\x6e\x00\x67\
|
||||
\x00\x17\
|
||||
\x0c\x65\xce\x07\
|
||||
\x00\x6c\
|
||||
\x00\x65\x00\x66\x00\x74\x00\x5f\x00\x61\x00\x72\x00\x72\x00\x6f\x00\x77\x00\x5f\x00\x64\x00\x69\x00\x73\x00\x61\x00\x62\x00\x6c\
|
||||
\x00\x65\x00\x64\x00\x2e\x00\x70\x00\x6e\x00\x67\
|
||||
\x00\x11\
|
||||
\x01\x1f\xc3\x87\
|
||||
\x00\x64\
|
||||
\x00\x6f\x00\x77\x00\x6e\x00\x5f\x00\x61\x00\x72\x00\x72\x00\x6f\x00\x77\x00\x5f\x00\x6f\x00\x6e\x00\x2e\x00\x70\x00\x6e\x00\x67\
|
||||
\
|
||||
\x00\x12\
|
||||
\x05\x8f\x9d\x07\
|
||||
\x00\x62\
|
||||
\x00\x72\x00\x61\x00\x6e\x00\x63\x00\x68\x00\x5f\x00\x6f\x00\x70\x00\x65\x00\x6e\x00\x5f\x00\x6f\x00\x6e\x00\x2e\x00\x70\x00\x6e\
|
||||
\x00\x67\
|
||||
\x00\x12\
|
||||
\x03\x8d\x04\x47\
|
||||
\x00\x72\
|
||||
\x00\x69\x00\x67\x00\x68\x00\x74\x00\x5f\x00\x61\x00\x72\x00\x72\x00\x6f\x00\x77\x00\x5f\x00\x6f\x00\x6e\x00\x2e\x00\x70\x00\x6e\
|
||||
\x00\x67\
|
||||
\x00\x0e\
|
||||
\x0e\xde\xfa\xc7\
|
||||
\x00\x6c\
|
||||
\x00\x65\x00\x66\x00\x74\x00\x5f\x00\x61\x00\x72\x00\x72\x00\x6f\x00\x77\x00\x2e\x00\x70\x00\x6e\x00\x67\
|
||||
\x00\x0f\
|
||||
\x06\x53\x25\xa7\
|
||||
\x00\x14\
|
||||
\x04\x5e\x2d\xa7\
|
||||
\x00\x62\
|
||||
\x00\x72\x00\x61\x00\x6e\x00\x63\x00\x68\x00\x5f\x00\x6f\x00\x70\x00\x65\x00\x6e\x00\x2e\x00\x70\x00\x6e\x00\x67\
|
||||
\x00\x72\x00\x61\x00\x6e\x00\x63\x00\x68\x00\x5f\x00\x63\x00\x6c\x00\x6f\x00\x73\x00\x65\x00\x64\x00\x5f\x00\x6f\x00\x6e\x00\x2e\
|
||||
\x00\x70\x00\x6e\x00\x67\
|
||||
\x00\x0c\
|
||||
\x06\xe6\xe6\x67\
|
||||
\x00\x75\
|
||||
\x00\x70\x00\x5f\x00\x61\x00\x72\x00\x72\x00\x6f\x00\x77\x00\x2e\x00\x70\x00\x6e\x00\x67\
|
||||
\x00\x0f\
|
||||
\x01\x73\x8b\x07\
|
||||
\x00\x75\
|
||||
\x00\x70\x00\x5f\x00\x61\x00\x72\x00\x72\x00\x6f\x00\x77\x00\x5f\x00\x6f\x00\x6e\x00\x2e\x00\x70\x00\x6e\x00\x67\
|
||||
"
|
||||
|
||||
qt_resource_struct_v1 = b"\
|
||||
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\
|
||||
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\
|
||||
\x00\x00\x00\x16\x00\x02\x00\x00\x00\x13\x00\x00\x00\x03\
|
||||
\x00\x00\x02\xe6\x00\x00\x00\x00\x00\x01\x00\x00\x1f\x3c\
|
||||
\x00\x00\x02\x3c\x00\x00\x00\x00\x00\x01\x00\x00\x1c\x9d\
|
||||
\x00\x00\x01\xb0\x00\x00\x00\x00\x00\x01\x00\x00\x13\x68\
|
||||
\x00\x00\x01\x6a\x00\x00\x00\x00\x00\x01\x00\x00\x12\x1d\
|
||||
\x00\x00\x00\x16\x00\x02\x00\x00\x00\x14\x00\x00\x00\x03\
|
||||
\x00\x00\x01\x0e\x00\x00\x00\x00\x00\x01\x00\x00\x10\xb3\
|
||||
\x00\x00\x02\x6a\x00\x00\x00\x00\x00\x01\x00\x00\x15\x93\
|
||||
\x00\x00\x01\x36\x00\x00\x00\x00\x00\x01\x00\x00\x11\x5d\
|
||||
\x00\x00\x03\x54\x00\x00\x00\x00\x00\x01\x00\x00\x27\x41\
|
||||
\x00\x00\x01\x60\x00\x00\x00\x00\x00\x01\x00\x00\x12\x07\
|
||||
\x00\x00\x00\x82\x00\x00\x00\x00\x00\x01\x00\x00\x07\xae\
|
||||
\x00\x00\x01\xfa\x00\x00\x00\x00\x00\x01\x00\x00\x14\x40\
|
||||
\x00\x00\x02\xbc\x00\x00\x00\x00\x00\x01\x00\x00\x1d\x70\
|
||||
\x00\x00\x00\x4c\x00\x00\x00\x00\x00\x01\x00\x00\x07\x0a\
|
||||
\x00\x00\x03\x08\x00\x00\x00\x00\x00\x01\x00\x00\x1e\xbe\
|
||||
\x00\x00\x01\xd8\x00\x00\x00\x00\x00\x01\x00\x00\x13\x97\
|
||||
\x00\x00\x02\x92\x00\x00\x00\x00\x00\x01\x00\x00\x16\x3c\
|
||||
\x00\x00\x00\x28\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
|
||||
\x00\x00\x02\xb6\x00\x00\x00\x00\x00\x01\x00\x00\x1e\x92\
|
||||
\x00\x00\x00\x76\x00\x00\x00\x00\x00\x01\x00\x00\x07\xd8\
|
||||
\x00\x00\x01\x10\x00\x00\x00\x00\x00\x01\x00\x00\x10\xd6\
|
||||
\x00\x00\x00\xb2\x00\x00\x00\x00\x00\x01\x00\x00\x08\x81\
|
||||
\x00\x00\x01\xda\x00\x00\x00\x00\x00\x01\x00\x00\x14\x12\
|
||||
\x00\x00\x01\x8e\x00\x00\x00\x00\x00\x01\x00\x00\x12\xbf\
|
||||
\x00\x00\x00\x4c\x00\x00\x00\x00\x00\x01\x00\x00\x00\xa4\
|
||||
\x00\x00\x03\x30\x00\x00\x00\x00\x00\x01\x00\x00\x20\x90\
|
||||
\x00\x00\x02\x98\x00\x00\x00\x00\x00\x01\x00\x00\x1d\xf0\
|
||||
\x00\x00\x00\xe8\x00\x00\x00\x00\x00\x01\x00\x00\x09\x25\
|
||||
\x00\x00\x02\x64\x00\x00\x00\x00\x00\x01\x00\x00\x1d\x46\
|
||||
\x00\x00\x02\x08\x00\x00\x00\x00\x00\x01\x00\x00\x1b\xf3\
|
||||
\x00\x00\x03\x0e\x00\x00\x00\x00\x00\x01\x00\x00\x1f\xe6\
|
||||
\x00\x00\x01\x3a\x00\x00\x00\x00\x00\x01\x00\x00\x11\x7a\
|
||||
\x00\x00\x03\x36\x00\x00\x00\x00\x00\x01\x00\x00\x26\x9f\
|
||||
\x00\x00\x00\xb2\x00\x00\x00\x00\x00\x01\x00\x00\x08\x58\
|
||||
\x00\x00\x02\x36\x00\x00\x00\x00\x00\x01\x00\x00\x14\xe9\
|
||||
\x00\x00\x00\xda\x00\x00\x00\x00\x00\x01\x00\x00\x10\x09\
|
||||
\x00\x00\x01\xb4\x00\x00\x00\x00\x00\x01\x00\x00\x13\x4e\
|
||||
\x00\x00\x02\xe6\x00\x00\x00\x00\x00\x01\x00\x00\x1e\x14\
|
||||
\x00\x00\x01\x84\x00\x00\x00\x00\x00\x01\x00\x00\x12\xab\
|
||||
"
|
||||
|
||||
qt_resource_struct_v2 = b"\
|
||||
|
|
@ -816,49 +828,50 @@ qt_resource_struct_v2 = b"\
|
|||
\x00\x00\x00\x00\x00\x00\x00\x00\
|
||||
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\
|
||||
\x00\x00\x00\x00\x00\x00\x00\x00\
|
||||
\x00\x00\x00\x16\x00\x02\x00\x00\x00\x13\x00\x00\x00\x03\
|
||||
\x00\x00\x00\x16\x00\x02\x00\x00\x00\x14\x00\x00\x00\x03\
|
||||
\x00\x00\x00\x00\x00\x00\x00\x00\
|
||||
\x00\x00\x02\xe6\x00\x00\x00\x00\x00\x01\x00\x00\x1f\x3c\
|
||||
\x00\x00\x01\x76\x41\x9d\xa2\x35\
|
||||
\x00\x00\x02\x3c\x00\x00\x00\x00\x00\x01\x00\x00\x1c\x9d\
|
||||
\x00\x00\x01\x76\x41\x9d\xa2\x35\
|
||||
\x00\x00\x01\xb0\x00\x00\x00\x00\x00\x01\x00\x00\x13\x68\
|
||||
\x00\x00\x01\x79\xb4\x72\xcc\x9c\
|
||||
\x00\x00\x01\x6a\x00\x00\x00\x00\x00\x01\x00\x00\x12\x1d\
|
||||
\x00\x00\x01\x76\x41\x9d\xa2\x39\
|
||||
\x00\x00\x01\x0e\x00\x00\x00\x00\x00\x01\x00\x00\x10\xb3\
|
||||
\x00\x00\x01\x79\xec\x37\x3f\xbc\
|
||||
\x00\x00\x02\x6a\x00\x00\x00\x00\x00\x01\x00\x00\x15\x93\
|
||||
\x00\x00\x01\x79\xec\x37\x3f\xba\
|
||||
\x00\x00\x01\x36\x00\x00\x00\x00\x00\x01\x00\x00\x11\x5d\
|
||||
\x00\x00\x01\x79\xec\x37\x3f\xb6\
|
||||
\x00\x00\x03\x54\x00\x00\x00\x00\x00\x01\x00\x00\x27\x41\
|
||||
\x00\x00\x01\x79\xec\x37\x3f\xc0\
|
||||
\x00\x00\x01\x60\x00\x00\x00\x00\x00\x01\x00\x00\x12\x07\
|
||||
\x00\x00\x01\x79\xec\x37\x3f\xbc\
|
||||
\x00\x00\x00\x82\x00\x00\x00\x00\x00\x01\x00\x00\x07\xae\
|
||||
\x00\x00\x01\x79\xec\x37\x3f\xb7\
|
||||
\x00\x00\x01\xfa\x00\x00\x00\x00\x00\x01\x00\x00\x14\x40\
|
||||
\x00\x00\x01\x79\xec\x37\x3f\xb7\
|
||||
\x00\x00\x02\xbc\x00\x00\x00\x00\x00\x01\x00\x00\x1d\x70\
|
||||
\x00\x00\x01\x79\xec\x37\x3f\xbe\
|
||||
\x00\x00\x00\x4c\x00\x00\x00\x00\x00\x01\x00\x00\x07\x0a\
|
||||
\x00\x00\x01\x79\xec\x37\x3f\xbd\
|
||||
\x00\x00\x03\x08\x00\x00\x00\x00\x00\x01\x00\x00\x1e\xbe\
|
||||
\x00\x00\x01\x79\xec\x37\x3f\xb4\
|
||||
\x00\x00\x01\xd8\x00\x00\x00\x00\x00\x01\x00\x00\x13\x97\
|
||||
\x00\x00\x01\x79\xec\x37\x3f\xb8\
|
||||
\x00\x00\x02\x92\x00\x00\x00\x00\x00\x01\x00\x00\x16\x3c\
|
||||
\x00\x00\x01\x79\xec\x37\x3f\xb5\
|
||||
\x00\x00\x00\x28\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
|
||||
\x00\x00\x01\x76\x41\x9d\xa2\x37\
|
||||
\x00\x00\x02\xb6\x00\x00\x00\x00\x00\x01\x00\x00\x1e\x92\
|
||||
\x00\x00\x01\x79\xb4\x72\xcc\x9c\
|
||||
\x00\x00\x00\x76\x00\x00\x00\x00\x00\x01\x00\x00\x07\xd8\
|
||||
\x00\x00\x01\x79\xb4\x72\xcc\x9c\
|
||||
\x00\x00\x01\x10\x00\x00\x00\x00\x00\x01\x00\x00\x10\xd6\
|
||||
\x00\x00\x01\x76\x41\x9d\xa2\x37\
|
||||
\x00\x00\x00\xb2\x00\x00\x00\x00\x00\x01\x00\x00\x08\x81\
|
||||
\x00\x00\x01\x76\x41\x9d\xa2\x37\
|
||||
\x00\x00\x01\xda\x00\x00\x00\x00\x00\x01\x00\x00\x14\x12\
|
||||
\x00\x00\x01\x79\xc2\x05\x2b\x60\
|
||||
\x00\x00\x01\x8e\x00\x00\x00\x00\x00\x01\x00\x00\x12\xbf\
|
||||
\x00\x00\x01\x76\x41\x9d\xa2\x35\
|
||||
\x00\x00\x00\x4c\x00\x00\x00\x00\x00\x01\x00\x00\x00\xa4\
|
||||
\x00\x00\x01\x79\xc1\xfc\x16\x91\
|
||||
\x00\x00\x03\x30\x00\x00\x00\x00\x00\x01\x00\x00\x20\x90\
|
||||
\x00\x00\x01\x79\xc1\xf9\x4b\x78\
|
||||
\x00\x00\x02\x98\x00\x00\x00\x00\x00\x01\x00\x00\x1d\xf0\
|
||||
\x00\x00\x01\x76\x41\x9d\xa2\x39\
|
||||
\x00\x00\x00\xe8\x00\x00\x00\x00\x00\x01\x00\x00\x09\x25\
|
||||
\x00\x00\x01\x79\xc2\x05\x91\x2a\
|
||||
\x00\x00\x02\x64\x00\x00\x00\x00\x00\x01\x00\x00\x1d\x46\
|
||||
\x00\x00\x01\x76\x41\x9d\xa2\x35\
|
||||
\x00\x00\x02\x08\x00\x00\x00\x00\x00\x01\x00\x00\x1b\xf3\
|
||||
\x00\x00\x01\x76\x41\x9d\xa2\x35\
|
||||
\x00\x00\x03\x0e\x00\x00\x00\x00\x00\x01\x00\x00\x1f\xe6\
|
||||
\x00\x00\x01\x76\x41\x9d\xa2\x35\
|
||||
\x00\x00\x01\x3a\x00\x00\x00\x00\x00\x01\x00\x00\x11\x7a\
|
||||
\x00\x00\x01\x76\x41\x9d\xa2\x39\
|
||||
\x00\x00\x01\x79\xec\x37\x3f\xb5\
|
||||
\x00\x00\x03\x36\x00\x00\x00\x00\x00\x01\x00\x00\x26\x9f\
|
||||
\x00\x00\x01\x79\xec\x37\x3f\xbe\
|
||||
\x00\x00\x00\xb2\x00\x00\x00\x00\x00\x01\x00\x00\x08\x58\
|
||||
\x00\x00\x01\x79\xec\x37\x3f\xb3\
|
||||
\x00\x00\x02\x36\x00\x00\x00\x00\x00\x01\x00\x00\x14\xe9\
|
||||
\x00\x00\x01\x79\xec\x37\x3f\xbb\
|
||||
\x00\x00\x00\xda\x00\x00\x00\x00\x00\x01\x00\x00\x10\x09\
|
||||
\x00\x00\x01\x79\xec\x37\x3f\xb9\
|
||||
\x00\x00\x01\xb4\x00\x00\x00\x00\x00\x01\x00\x00\x13\x4e\
|
||||
\x00\x00\x01\x7c\xa7\x41\xfc\x00\
|
||||
\x00\x00\x02\xe6\x00\x00\x00\x00\x00\x01\x00\x00\x1e\x14\
|
||||
\x00\x00\x01\x79\xec\x37\x3f\xbb\
|
||||
\x00\x00\x01\x84\x00\x00\x00\x00\x00\x01\x00\x00\x12\xab\
|
||||
\x00\x00\x01\x79\xec\x37\x3f\xbf\
|
||||
"
|
||||
|
||||
|
||||
qt_version = [int(v) for v in QtCore.qVersion().split('.')]
|
||||
if qt_version < [5, 8, 0]:
|
||||
rcc_version = 1
|
||||
|
|
|
|||
|
|
@ -1,75 +1,15 @@
|
|||
# Resource object code (Python 3)
|
||||
# Created by: object code
|
||||
# Created by: The Resource Compiler for Qt version 5.15.2
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Resource object code
|
||||
#
|
||||
# Created: Fri Oct 22 11:42:52 2021
|
||||
# by: The Resource Compiler for PySide2 (Qt v5.6.1)
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
|
||||
from PySide2 import QtCore
|
||||
|
||||
|
||||
qt_resource_data = b"\
|
||||
\x00\x00\x00\x9f\
|
||||
\x89\
|
||||
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
|
||||
\x00\x00\x09\x00\x00\x00\x06\x08\x04\x00\x00\x00\xbb\xce|N\
|
||||
\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\
|
||||
\x02bKGD\x00\xff\x87\x8f\xcc\xbf\x00\x00\x00\x09p\
|
||||
HYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\
|
||||
\x00\x00\x00\x07tIME\x07\xdc\x08\x17\x08\x14\x1f\xf9\
|
||||
#\xd9\x0b\x00\x00\x00#IDAT\x08\xd7c`\xc0\
|
||||
\x0d\xe6|\x80\xb1\x18\x91\x05R\x04\xe0B\x08\x15)\x02\
|
||||
\x0c\x0c\x8c\xc8\x02\x08\x95h\x00\x00\xac\xac\x07\x90Ne\
|
||||
4\xac\x00\x00\x00\x00IEND\xaeB`\x82\
|
||||
\x00\x00\x00\xa6\
|
||||
\x89\
|
||||
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
|
||||
\x00\x00\x09\x00\x00\x00\x06\x08\x04\x00\x00\x00\xbb\xce|N\
|
||||
\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\
|
||||
\x02bKGD\x00\xff\x87\x8f\xcc\xbf\x00\x00\x00\x09p\
|
||||
HYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\
|
||||
\x00\x00\x00\x07tIME\x07\xdc\x08\x17\x08\x15;\xdc\
|
||||
;\x0c\x9b\x00\x00\x00*IDAT\x08\xd7c`\xc0\
|
||||
\x00\x8c\x0c\x0cs> \x0b\xa4\x08020 \x0b\xa6\
|
||||
\x08000B\x98\x10\xc1\x14\x01\x14\x13P\xb5\xa3\x01\
|
||||
\x00\xc6\xb9\x07\x90]f\x1f\x83\x00\x00\x00\x00IEN\
|
||||
D\xaeB`\x82\
|
||||
\x00\x00\x00\xa5\
|
||||
\x89\
|
||||
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
|
||||
\x00\x00\x09\x00\x00\x00\x06\x08\x04\x00\x00\x00\xbb\xce|N\
|
||||
\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\
|
||||
\x02bKGD\x00\x9cS4\xfc]\x00\x00\x00\x09p\
|
||||
HYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\
|
||||
\x00\x00\x00\x07tIME\x07\xdc\x08\x17\x0b\x02\x04m\
|
||||
\x98\x1bi\x00\x00\x00)IDAT\x08\xd7c`\xc0\
|
||||
\x00\x8c\x0c\x0c\xff\xcf\xa3\x08\x18220 \x0b2\x1a\
|
||||
200B\x98\x10AFC\x14\x13P\xb5\xa3\x01\x00\
|
||||
\xd6\x10\x07\xd2/H\xdfJ\x00\x00\x00\x00IEND\
|
||||
\xaeB`\x82\
|
||||
\x00\x00\x00\xa5\
|
||||
\x89\
|
||||
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
|
||||
\x00\x00\x09\x00\x00\x00\x06\x08\x04\x00\x00\x00\xbb\xce|N\
|
||||
\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\
|
||||
\x02bKGD\x00\x9cS4\xfc]\x00\x00\x00\x09p\
|
||||
HYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\
|
||||
\x00\x00\x00\x07tIME\x07\xdc\x08\x17\x0b\x02\x04m\
|
||||
\x98\x1bi\x00\x00\x00)IDAT\x08\xd7c`\xc0\
|
||||
\x00\x8c\x0c\x0c\xff\xcf\xa3\x08\x18220 \x0b2\x1a\
|
||||
200B\x98\x10AFC\x14\x13P\xb5\xa3\x01\x00\
|
||||
\xd6\x10\x07\xd2/H\xdfJ\x00\x00\x00\x00IEND\
|
||||
\xaeB`\x82\
|
||||
\x00\x00\x00\xa0\
|
||||
\x89\
|
||||
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
|
||||
\x00\x00\x06\x00\x00\x00\x09\x08\x04\x00\x00\x00\xbb\x93\x95\x16\
|
||||
\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\
|
||||
\x02bKGD\x00\xff\x87\x8f\xcc\xbf\x00\x00\x00\x09p\
|
||||
HYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\
|
||||
\x00\x00\x00\x07tIME\x07\xdc\x08\x17\x14\x1f\x0d\xfc\
|
||||
R+\x9c\x00\x00\x00$IDAT\x08\xd7c`@\
|
||||
\x05s>\xc0XL\xc8\x5c&dY&d\xc5pN\
|
||||
\x8a\x00\x9c\x93\x22\x80a\x1a\x0a\x00\x00)\x95\x08\xaf\x88\
|
||||
\xac\xba4\x00\x00\x00\x00IEND\xaeB`\x82\
|
||||
\x00\x00\x00\xa6\
|
||||
\x89\
|
||||
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
|
||||
|
|
@ -83,31 +23,33 @@ HYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\
|
|||
d``b``4D\xe2 s\x19\x90\x8d@\x02\
|
||||
\x00d@\x09u\x86\xb3\xad\x9c\x00\x00\x00\x00IEN\
|
||||
D\xaeB`\x82\
|
||||
\x00\x00\x00\x9e\
|
||||
\x00\x00\x00\xa5\
|
||||
\x89\
|
||||
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
|
||||
\x00\x00\x09\x00\x00\x00\x06\x08\x04\x00\x00\x00\xbb\xce|N\
|
||||
\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\
|
||||
\x02bKGD\x00\x9cS4\xfc]\x00\x00\x00\x09p\
|
||||
HYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\
|
||||
\x00\x00\x00\x07tIME\x07\xdc\x08\x17\x0b\x02\x04m\
|
||||
\x98\x1bi\x00\x00\x00)IDAT\x08\xd7c`\xc0\
|
||||
\x00\x8c\x0c\x0c\xff\xcf\xa3\x08\x18220 \x0b2\x1a\
|
||||
200B\x98\x10AFC\x14\x13P\xb5\xa3\x01\x00\
|
||||
\xd6\x10\x07\xd2/H\xdfJ\x00\x00\x00\x00IEND\
|
||||
\xaeB`\x82\
|
||||
\x00\x00\x00\xa6\
|
||||
\x89\
|
||||
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
|
||||
\x00\x00\x09\x00\x00\x00\x06\x08\x04\x00\x00\x00\xbb\xce|N\
|
||||
\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\
|
||||
\x02bKGD\x00\xff\x87\x8f\xcc\xbf\x00\x00\x00\x09p\
|
||||
HYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\
|
||||
\x00\x00\x00\x07tIME\x07\xdc\x08\x17\x08\x15\x0f\xfd\
|
||||
\x8f\xf8.\x00\x00\x00\x22IDAT\x08\xd7c`\xc0\
|
||||
\x0d\xfe\x9f\x87\xb1\x18\x91\x05\x18\x0d\xe1BH*\x0c\x19\
|
||||
\x18\x18\x91\x05\x10*\xd1\x00\x00\xca\xb5\x07\xd2v\xbb\xb2\
|
||||
\xc5\x00\x00\x00\x00IEND\xaeB`\x82\
|
||||
\x00\x00\x00\x9e\
|
||||
\x89\
|
||||
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
|
||||
\x00\x00\x09\x00\x00\x00\x06\x08\x04\x00\x00\x00\xbb\xce|N\
|
||||
\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\
|
||||
\x02bKGD\x00\xff\x87\x8f\xcc\xbf\x00\x00\x00\x09p\
|
||||
HYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\
|
||||
\x00\x00\x00\x07tIME\x07\xdc\x08\x17\x08\x15\x0f\xfd\
|
||||
\x8f\xf8.\x00\x00\x00\x22IDAT\x08\xd7c`\xc0\
|
||||
\x0d\xfe\x9f\x87\xb1\x18\x91\x05\x18\x0d\xe1BH*\x0c\x19\
|
||||
\x18\x18\x91\x05\x10*\xd1\x00\x00\xca\xb5\x07\xd2v\xbb\xb2\
|
||||
\xc5\x00\x00\x00\x00IEND\xaeB`\x82\
|
||||
\x00\x00\x07\x06\
|
||||
\x00\x00\x00\x07tIME\x07\xdc\x08\x17\x08\x15;\xdc\
|
||||
;\x0c\x9b\x00\x00\x00*IDAT\x08\xd7c`\xc0\
|
||||
\x00\x8c\x0c\x0cs> \x0b\xa4\x08020 \x0b\xa6\
|
||||
\x08000B\x98\x10\xc1\x14\x01\x14\x13P\xb5\xa3\x01\
|
||||
\x00\xc6\xb9\x07\x90]f\x1f\x83\x00\x00\x00\x00IEN\
|
||||
D\xaeB`\x82\
|
||||
\x00\x00\x070\
|
||||
\x89\
|
||||
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
|
||||
\x00\x00\x0a\x00\x00\x00\x07\x08\x06\x00\x00\x001\xac\xdcc\
|
||||
|
|
@ -165,10 +107,10 @@ toshop:ICCProfil\
|
|||
e=\x22sRGB IEC61966\
|
||||
-2.1\x22\x0a xmp:Mod\
|
||||
ifyDate=\x222021-05\
|
||||
-31T12:30:11+02:\
|
||||
-31T12:33:14+02:\
|
||||
00\x22\x0a xmp:Metad\
|
||||
ataDate=\x222021-05\
|
||||
-31T12:30:11+02:\
|
||||
-31T12:33:14+02:\
|
||||
00\x22>\x0a <xmpMM:H\
|
||||
istory>\x0a <rdf\
|
||||
:Seq>\x0a <rdf:\
|
||||
|
|
@ -179,14 +121,14 @@ twareAgent=\x22Affi\
|
|||
nity Designer 1.\
|
||||
9.2\x22\x0a stEvt\
|
||||
:when=\x222021-05-3\
|
||||
1T12:30:11+02:00\
|
||||
1T12:33:14+02:00\
|
||||
\x22/>\x0a </rdf:Se\
|
||||
q>\x0a </xmpMM:Hi\
|
||||
story>\x0a </rdf:D\
|
||||
escription>\x0a </r\
|
||||
df:RDF>\x0a</x:xmpm\
|
||||
eta>\x0a<?xpacket e\
|
||||
nd=\x22r\x22?>\x85\x9d\x9f\x08\x00\x00\x01\x83\
|
||||
nd=\x22r\x22?>H\x8b[^\x00\x00\x01\x83\
|
||||
iCCPsRGB IEC6196\
|
||||
6-2.1\x00\x00(\x91u\x91\xcf+DQ\x14\
|
||||
\xc7?fh\xfc\x18\x8dba1e\x12\x16B\x83\x12\
|
||||
|
|
@ -213,28 +155,54 @@ S\xdc\xa1\xaa\xf0r\x0c\xf5\xb3\xd0x\x05\xb5\xf3\xc5\x9e\
|
|||
\xfd\xecst\x07\xd15\xf9\xaaK\xd8\xd9\x85\x0e9\xef\
|
||||
Y\xf8\x06\x8e\xfdg\xf8\xfd\x8a\x18\x97\x00\x00\x00\x09p\
|
||||
HYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\
|
||||
\x00\x00\x00mIDAT\x18\x95u\xcf\xc1\x09\xc2P\
|
||||
\x10\x84\xe1\xd7\x85\x07\x9b\xd0C@\xd2\x82x\x14{0\
|
||||
W!\x8d\x84`?bKzH\xcc\x97\x83\xfb0\x04\
|
||||
\xdf\x9c\x86\x7fg\x99\xdd\x84\x0d\xaaT\x10jl\x13\x1e\
|
||||
\xbe\xba\xfe\x0951{\xe6\x8d\x0f&\x1c\x17\xa1S\xb0\
|
||||
\x11\x87\x0c/\x01\x07\xec\xb0\x0f?\xe1\xbc\xaei\xa3\xe6\
|
||||
\x85w\xf8[\xe9\xf0\xbb\x9f\xfa\xd2\x839\xdc\xa3[\xf3\
|
||||
\x19.\xa8\x89\xb50\xf7C\xa0\x00\x00\x00\x00IEN\
|
||||
D\xaeB`\x82\
|
||||
\x00\x00\x00\xa6\
|
||||
\x00\x00\x00\x97IDAT\x18\x95m\xcf\xb1j\x02A\
|
||||
\x14\x85\xe1o\xb7\xb6\xd0'H=Vi\x03\xb1\xb4H\
|
||||
;l\xa5\xf19\xf6Y\x02VB\xbaa\x0a\x0b;\x1b\
|
||||
\x1bkA\x18\x02)m\xe3\xbe\x82\xcd\x06\x16\xd9\xdb\xdd\
|
||||
\x9f\xff\x5c\xee\xa9b*\x13Ls\x13nF&\xa6\xf2\
|
||||
\x82\xaeF\x8b\xdf\x98\xca\xfb\x88\xb4\xc0\x0f\xda\x1a[t\
|
||||
\xd8\xc7T\xc2@\x9ac\x8f?|U=|\xc5\x09w\
|
||||
\xbc\xa1\xc2\x193,r\x13.\xd5\xe0\xc2\x12\x07\x5cQ\
|
||||
#\xe0#7\xe1\xa8O\x0e\x7f\xda`\xd7\xaf\x9f\xb9\x09\
|
||||
\xdfc\x05\xff\xe5uLe\xf5\xcc\x1f\x0d3,\x83\xb6\
|
||||
\x06D\x83\x00\x00\x00\x00IEND\xaeB`\x82\
|
||||
\x00\x00\x00\xa0\
|
||||
\x89\
|
||||
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
|
||||
\x00\x00\x06\x00\x00\x00\x09\x08\x04\x00\x00\x00\xbb\x93\x95\x16\
|
||||
\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\
|
||||
\x02bKGD\x00\xff\x87\x8f\xcc\xbf\x00\x00\x00\x09p\
|
||||
HYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\
|
||||
\x00\x00\x00\x07tIME\x07\xdc\x08\x17\x14\x1f \xb9\
|
||||
\x8dw\xe9\x00\x00\x00*IDAT\x08\xd7c`\xc0\
|
||||
\x06\xe6|```B0\xa1\x1c\x08\x93\x81\x81\x09\xc1\
|
||||
d``b`H\x11@\xe2 s\x19\x90\x8d@\x02\
|
||||
\x00#\xed\x08\xafd\x9f\x0f\x15\x00\x00\x00\x00IEN\
|
||||
D\xaeB`\x82\
|
||||
\x00\x00\x00\x07tIME\x07\xdc\x08\x17\x14\x1c\x1f$\
|
||||
\xc6\x09\x17\x00\x00\x00$IDAT\x08\xd7c`@\
|
||||
\x05\xff\xcf\xc3XL\xc8\x5c&dY&d\xc5p\x0e\
|
||||
\xa3!\x9c\xc3h\x88a\x1a\x0a\x00\x00m\x84\x09u7\
|
||||
\x9e\xd9#\x00\x00\x00\x00IEND\xaeB`\x82\
|
||||
\x00\x00\x00\x9e\
|
||||
\x89\
|
||||
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
|
||||
\x00\x00\x09\x00\x00\x00\x06\x08\x04\x00\x00\x00\xbb\xce|N\
|
||||
\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\
|
||||
\x02bKGD\x00\xff\x87\x8f\xcc\xbf\x00\x00\x00\x09p\
|
||||
HYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\
|
||||
\x00\x00\x00\x07tIME\x07\xdc\x08\x17\x08\x15\x0f\xfd\
|
||||
\x8f\xf8.\x00\x00\x00\x22IDAT\x08\xd7c`\xc0\
|
||||
\x0d\xfe\x9f\x87\xb1\x18\x91\x05\x18\x0d\xe1BH*\x0c\x19\
|
||||
\x18\x18\x91\x05\x10*\xd1\x00\x00\xca\xb5\x07\xd2v\xbb\xb2\
|
||||
\xc5\x00\x00\x00\x00IEND\xaeB`\x82\
|
||||
\x00\x00\x00\xa5\
|
||||
\x89\
|
||||
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
|
||||
\x00\x00\x09\x00\x00\x00\x06\x08\x04\x00\x00\x00\xbb\xce|N\
|
||||
\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\
|
||||
\x02bKGD\x00\x9cS4\xfc]\x00\x00\x00\x09p\
|
||||
HYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\
|
||||
\x00\x00\x00\x07tIME\x07\xdc\x08\x17\x0b\x02\x04m\
|
||||
\x98\x1bi\x00\x00\x00)IDAT\x08\xd7c`\xc0\
|
||||
\x00\x8c\x0c\x0c\xff\xcf\xa3\x08\x18220 \x0b2\x1a\
|
||||
200B\x98\x10AFC\x14\x13P\xb5\xa3\x01\x00\
|
||||
\xd6\x10\x07\xd2/H\xdfJ\x00\x00\x00\x00IEND\
|
||||
\xaeB`\x82\
|
||||
\x00\x00\x07\xdd\
|
||||
\x89\
|
||||
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
|
||||
|
|
@ -363,6 +331,221 @@ zpp\xf0\xe3\x0e.\xa4\xd2\xae\xf0\x8a\xf7\x9a\xe3V\
|
|||
q[s\x5c@H\xa5\xdda\x81\x0d\x9ek\x8e\xff\xfd\
|
||||
\xcf?\xcc1\xe9\x01\x1c\x00sR-q\xe4J\x1bi\
|
||||
\x00\x00\x00\x00IEND\xaeB`\x82\
|
||||
\x00\x00\x00\x9e\
|
||||
\x89\
|
||||
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
|
||||
\x00\x00\x09\x00\x00\x00\x06\x08\x04\x00\x00\x00\xbb\xce|N\
|
||||
\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\
|
||||
\x02bKGD\x00\xff\x87\x8f\xcc\xbf\x00\x00\x00\x09p\
|
||||
HYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\
|
||||
\x00\x00\x00\x07tIME\x07\xdc\x08\x17\x08\x15\x0f\xfd\
|
||||
\x8f\xf8.\x00\x00\x00\x22IDAT\x08\xd7c`\xc0\
|
||||
\x0d\xfe\x9f\x87\xb1\x18\x91\x05\x18\x0d\xe1BH*\x0c\x19\
|
||||
\x18\x18\x91\x05\x10*\xd1\x00\x00\xca\xb5\x07\xd2v\xbb\xb2\
|
||||
\xc5\x00\x00\x00\x00IEND\xaeB`\x82\
|
||||
\x00\x00\x00\xa6\
|
||||
\x89\
|
||||
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
|
||||
\x00\x00\x09\x00\x00\x00\x06\x08\x04\x00\x00\x00\xbb\xce|N\
|
||||
\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\
|
||||
\x02bKGD\x00\xff\x87\x8f\xcc\xbf\x00\x00\x00\x09p\
|
||||
HYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\
|
||||
\x00\x00\x00\x07tIME\x07\xdc\x08\x17\x08\x15;\xdc\
|
||||
;\x0c\x9b\x00\x00\x00*IDAT\x08\xd7c`\xc0\
|
||||
\x00\x8c\x0c\x0cs> \x0b\xa4\x08020 \x0b\xa6\
|
||||
\x08000B\x98\x10\xc1\x14\x01\x14\x13P\xb5\xa3\x01\
|
||||
\x00\xc6\xb9\x07\x90]f\x1f\x83\x00\x00\x00\x00IEN\
|
||||
D\xaeB`\x82\
|
||||
\x00\x00\x00\xa6\
|
||||
\x89\
|
||||
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
|
||||
\x00\x00\x09\x00\x00\x00\x06\x08\x04\x00\x00\x00\xbb\xce|N\
|
||||
\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\
|
||||
\x02bKGD\x00\xff\x87\x8f\xcc\xbf\x00\x00\x00\x09p\
|
||||
HYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\
|
||||
\x00\x00\x00\x07tIME\x07\xdc\x08\x17\x08\x15;\xdc\
|
||||
;\x0c\x9b\x00\x00\x00*IDAT\x08\xd7c`\xc0\
|
||||
\x00\x8c\x0c\x0cs> \x0b\xa4\x08020 \x0b\xa6\
|
||||
\x08000B\x98\x10\xc1\x14\x01\x14\x13P\xb5\xa3\x01\
|
||||
\x00\xc6\xb9\x07\x90]f\x1f\x83\x00\x00\x00\x00IEN\
|
||||
D\xaeB`\x82\
|
||||
\x00\x00\x00\xa5\
|
||||
\x89\
|
||||
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
|
||||
\x00\x00\x09\x00\x00\x00\x06\x08\x04\x00\x00\x00\xbb\xce|N\
|
||||
\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\
|
||||
\x02bKGD\x00\x9cS4\xfc]\x00\x00\x00\x09p\
|
||||
HYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\
|
||||
\x00\x00\x00\x07tIME\x07\xdc\x08\x17\x0b\x02\x04m\
|
||||
\x98\x1bi\x00\x00\x00)IDAT\x08\xd7c`\xc0\
|
||||
\x00\x8c\x0c\x0c\xff\xcf\xa3\x08\x18220 \x0b2\x1a\
|
||||
200B\x98\x10AFC\x14\x13P\xb5\xa3\x01\x00\
|
||||
\xd6\x10\x07\xd2/H\xdfJ\x00\x00\x00\x00IEND\
|
||||
\xaeB`\x82\
|
||||
\x00\x00\x00\xa0\
|
||||
\x89\
|
||||
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
|
||||
\x00\x00\x06\x00\x00\x00\x09\x08\x04\x00\x00\x00\xbb\x93\x95\x16\
|
||||
\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\
|
||||
\x02bKGD\x00\xff\x87\x8f\xcc\xbf\x00\x00\x00\x09p\
|
||||
HYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\
|
||||
\x00\x00\x00\x07tIME\x07\xdc\x08\x17\x14\x1f\x0d\xfc\
|
||||
R+\x9c\x00\x00\x00$IDAT\x08\xd7c`@\
|
||||
\x05s>\xc0XL\xc8\x5c&dY&d\xc5pN\
|
||||
\x8a\x00\x9c\x93\x22\x80a\x1a\x0a\x00\x00)\x95\x08\xaf\x88\
|
||||
\xac\xba4\x00\x00\x00\x00IEND\xaeB`\x82\
|
||||
\x00\x00\x00\x9f\
|
||||
\x89\
|
||||
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
|
||||
\x00\x00\x09\x00\x00\x00\x06\x08\x04\x00\x00\x00\xbb\xce|N\
|
||||
\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\
|
||||
\x02bKGD\x00\xff\x87\x8f\xcc\xbf\x00\x00\x00\x09p\
|
||||
HYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\
|
||||
\x00\x00\x00\x07tIME\x07\xdc\x08\x17\x08\x14\x1f\xf9\
|
||||
#\xd9\x0b\x00\x00\x00#IDAT\x08\xd7c`\xc0\
|
||||
\x0d\xe6|\x80\xb1\x18\x91\x05R\x04\xe0B\x08\x15)\x02\
|
||||
\x0c\x0c\x8c\xc8\x02\x08\x95h\x00\x00\xac\xac\x07\x90Ne\
|
||||
4\xac\x00\x00\x00\x00IEND\xaeB`\x82\
|
||||
\x00\x00\x07\x06\
|
||||
\x89\
|
||||
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
|
||||
\x00\x00\x0a\x00\x00\x00\x07\x08\x06\x00\x00\x001\xac\xdcc\
|
||||
\x00\x00\x04\xb0iTXtXML:com.\
|
||||
adobe.xmp\x00\x00\x00\x00\x00<?\
|
||||
xpacket begin=\x22\xef\
|
||||
\xbb\xbf\x22 id=\x22W5M0MpCe\
|
||||
hiHzreSzNTczkc9d\
|
||||
\x22?>\x0a<x:xmpmeta x\
|
||||
mlns:x=\x22adobe:ns\
|
||||
:meta/\x22 x:xmptk=\
|
||||
\x22XMP Core 5.5.0\x22\
|
||||
>\x0a <rdf:RDF xmln\
|
||||
s:rdf=\x22http://ww\
|
||||
w.w3.org/1999/02\
|
||||
/22-rdf-syntax-n\
|
||||
s#\x22>\x0a <rdf:Desc\
|
||||
ription rdf:abou\
|
||||
t=\x22\x22\x0a xmlns:e\
|
||||
xif=\x22http://ns.a\
|
||||
dobe.com/exif/1.\
|
||||
0/\x22\x0a xmlns:ti\
|
||||
ff=\x22http://ns.ad\
|
||||
obe.com/tiff/1.0\
|
||||
/\x22\x0a xmlns:pho\
|
||||
toshop=\x22http://n\
|
||||
s.adobe.com/phot\
|
||||
oshop/1.0/\x22\x0a \
|
||||
xmlns:xmp=\x22http:\
|
||||
//ns.adobe.com/x\
|
||||
ap/1.0/\x22\x0a xml\
|
||||
ns:xmpMM=\x22http:/\
|
||||
/ns.adobe.com/xa\
|
||||
p/1.0/mm/\x22\x0a x\
|
||||
mlns:stEvt=\x22http\
|
||||
://ns.adobe.com/\
|
||||
xap/1.0/sType/Re\
|
||||
sourceEvent#\x22\x0a \
|
||||
exif:PixelXDime\
|
||||
nsion=\x2210\x22\x0a ex\
|
||||
if:PixelYDimensi\
|
||||
on=\x227\x22\x0a exif:C\
|
||||
olorSpace=\x221\x22\x0a \
|
||||
tiff:ImageWidth\
|
||||
=\x2210\x22\x0a tiff:Im\
|
||||
ageLength=\x227\x22\x0a \
|
||||
tiff:Resolution\
|
||||
Unit=\x222\x22\x0a tiff\
|
||||
:XResolution=\x2272\
|
||||
.0\x22\x0a tiff:YRes\
|
||||
olution=\x2272.0\x22\x0a \
|
||||
photoshop:Colo\
|
||||
rMode=\x223\x22\x0a pho\
|
||||
toshop:ICCProfil\
|
||||
e=\x22sRGB IEC61966\
|
||||
-2.1\x22\x0a xmp:Mod\
|
||||
ifyDate=\x222021-05\
|
||||
-31T12:30:11+02:\
|
||||
00\x22\x0a xmp:Metad\
|
||||
ataDate=\x222021-05\
|
||||
-31T12:30:11+02:\
|
||||
00\x22>\x0a <xmpMM:H\
|
||||
istory>\x0a <rdf\
|
||||
:Seq>\x0a <rdf:\
|
||||
li\x0a stEvt:a\
|
||||
ction=\x22produced\x22\
|
||||
\x0a stEvt:sof\
|
||||
twareAgent=\x22Affi\
|
||||
nity Designer 1.\
|
||||
9.2\x22\x0a stEvt\
|
||||
:when=\x222021-05-3\
|
||||
1T12:30:11+02:00\
|
||||
\x22/>\x0a </rdf:Se\
|
||||
q>\x0a </xmpMM:Hi\
|
||||
story>\x0a </rdf:D\
|
||||
escription>\x0a </r\
|
||||
df:RDF>\x0a</x:xmpm\
|
||||
eta>\x0a<?xpacket e\
|
||||
nd=\x22r\x22?>\x85\x9d\x9f\x08\x00\x00\x01\x83\
|
||||
iCCPsRGB IEC6196\
|
||||
6-2.1\x00\x00(\x91u\x91\xcf+DQ\x14\
|
||||
\xc7?fh\xfc\x18\x8dba1e\x12\x16B\x83\x12\
|
||||
\x1b\x8b\x99\x18\x0a\x8b\x99Q~mf\x9ey3j\xde\
|
||||
x\xbd7\xd2d\xabl\xa7(\xb1\xf1k\xc1_\xc0V\
|
||||
Y+E\xa4d\xa7\xac\x89\x0dz\xce\x9bQ#\x99s\
|
||||
;\xf7|\xee\xf7\xdes\xba\xf7\x5cpD\xd3\x8afV\
|
||||
\xfaA\xcbd\x8dp(\xe0\x9b\x99\x9d\xf3\xb9\x9e\xa8\xa2\
|
||||
\x85\x1a:\xf1\xc6\x14S\x9f\x8c\x8cF)k\xef\xb7T\
|
||||
\xd8\xf1\xba\xdb\xaeU\xfe\xdc\xbfV\xb7\x980\x15\xa8\xa8\
|
||||
\x16\x1eVt#+<&<\xb1\x9a\xd5m\xde\x12n\
|
||||
RR\xb1E\xe1\x13\xe1.C.(|c\xeb\xf1\x22\
|
||||
?\xdb\x9c,\xf2\xa7\xcdF4\x1c\x04G\x83\xb0/\xf9\
|
||||
\x8b\xe3\xbfXI\x19\x9a\xb0\xbc\x9c6-\xbd\xa2\xfc\xdc\
|
||||
\xc7~\x89;\x91\x99\x8eHl\x15\xf7b\x12&D\x00\
|
||||
\x1f\xe3\x8c\x10d\x80^\x86d\x1e\xa0\x9b>zdE\
|
||||
\x99|\x7f!\x7f\x8ae\xc9Ud\xd6\xc9a\xb0D\x92\
|
||||
\x14Y\xbaD]\x91\xea\x09\x89\xaa\xe8\x09\x19irv\
|
||||
\xff\xff\xf6\xd5T\xfb\xfb\x8a\xd5\xdd\x01\xa8z\xb4\xac\xd7\
|
||||
vpm\xc2W\xde\xb2>\x0e,\xeb\xeb\x10\x9c\x0fp\
|
||||
\x9e)\xe5/\xef\xc3\xe0\x9b\xe8\xf9\x92\xd6\xb6\x07\x9eu\
|
||||
8\xbd(i\xf1m8\xdb\x80\xe6{=f\xc4\x0a\x92\
|
||||
S\xdc\xa1\xaa\xf0r\x0c\xf5\xb3\xd0x\x05\xb5\xf3\xc5\x9e\
|
||||
\xfd\xecst\x07\xd15\xf9\xaaK\xd8\xd9\x85\x0e9\xef\
|
||||
Y\xf8\x06\x8e\xfdg\xf8\xfd\x8a\x18\x97\x00\x00\x00\x09p\
|
||||
HYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\
|
||||
\x00\x00\x00mIDAT\x18\x95u\xcf\xc1\x09\xc2P\
|
||||
\x10\x84\xe1\xd7\x85\x07\x9b\xd0C@\xd2\x82x\x14{0\
|
||||
W!\x8d\x84`?bKzH\xcc\x97\x83\xfb0\x04\
|
||||
\xdf\x9c\x86\x7fg\x99\xdd\x84\x0d\xaaT\x10jl\x13\x1e\
|
||||
\xbe\xba\xfe\x0951{\xe6\x8d\x0f&\x1c\x17\xa1S\xb0\
|
||||
\x11\x87\x0c/\x01\x07\xec\xb0\x0f?\xe1\xbc\xaei\xa3\xe6\
|
||||
\x85w\xf8[\xe9\xf0\xbb\x9f\xfa\xd2\x839\xdc\xa3[\xf3\
|
||||
\x19.\xa8\x89\xb50\xf7C\xa0\x00\x00\x00\x00IEN\
|
||||
D\xaeB`\x82\
|
||||
\x00\x00\x00\xa6\
|
||||
\x89\
|
||||
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
|
||||
\x00\x00\x06\x00\x00\x00\x09\x08\x04\x00\x00\x00\xbb\x93\x95\x16\
|
||||
\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\
|
||||
\x02bKGD\x00\xff\x87\x8f\xcc\xbf\x00\x00\x00\x09p\
|
||||
HYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\
|
||||
\x00\x00\x00\x07tIME\x07\xdc\x08\x17\x14\x1f \xb9\
|
||||
\x8dw\xe9\x00\x00\x00*IDAT\x08\xd7c`\xc0\
|
||||
\x06\xe6|```B0\xa1\x1c\x08\x93\x81\x81\x09\xc1\
|
||||
d``b`H\x11@\xe2 s\x19\x90\x8d@\x02\
|
||||
\x00#\xed\x08\xafd\x9f\x0f\x15\x00\x00\x00\x00IEN\
|
||||
D\xaeB`\x82\
|
||||
\x00\x00\x00\xa0\
|
||||
\x89\
|
||||
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
|
||||
\x00\x00\x06\x00\x00\x00\x09\x08\x04\x00\x00\x00\xbb\x93\x95\x16\
|
||||
\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\
|
||||
\x02bKGD\x00\xff\x87\x8f\xcc\xbf\x00\x00\x00\x09p\
|
||||
HYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\
|
||||
\x00\x00\x00\x07tIME\x07\xdc\x08\x17\x14\x1c\x1f$\
|
||||
\xc6\x09\x17\x00\x00\x00$IDAT\x08\xd7c`@\
|
||||
\x05\xff\xcf\xc3XL\xc8\x5c&dY&d\xc5p\x0e\
|
||||
\xa3!\x9c\xc3h\x88a\x1a\x0a\x00\x00m\x84\x09u7\
|
||||
\x9e\xd9#\x00\x00\x00\x00IEND\xaeB`\x82\
|
||||
\x00\x00\x07\xad\
|
||||
\x89\
|
||||
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
|
||||
|
|
@ -501,186 +684,6 @@ HYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\
|
|||
d``b``4D\xe2 s\x19\x90\x8d@\x02\
|
||||
\x00d@\x09u\x86\xb3\xad\x9c\x00\x00\x00\x00IEN\
|
||||
D\xaeB`\x82\
|
||||
\x00\x00\x00\xa5\
|
||||
\x89\
|
||||
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
|
||||
\x00\x00\x09\x00\x00\x00\x06\x08\x04\x00\x00\x00\xbb\xce|N\
|
||||
\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\
|
||||
\x02bKGD\x00\x9cS4\xfc]\x00\x00\x00\x09p\
|
||||
HYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\
|
||||
\x00\x00\x00\x07tIME\x07\xdc\x08\x17\x0b\x02\x04m\
|
||||
\x98\x1bi\x00\x00\x00)IDAT\x08\xd7c`\xc0\
|
||||
\x00\x8c\x0c\x0c\xff\xcf\xa3\x08\x18220 \x0b2\x1a\
|
||||
200B\x98\x10AFC\x14\x13P\xb5\xa3\x01\x00\
|
||||
\xd6\x10\x07\xd2/H\xdfJ\x00\x00\x00\x00IEND\
|
||||
\xaeB`\x82\
|
||||
\x00\x00\x00\xa0\
|
||||
\x89\
|
||||
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
|
||||
\x00\x00\x06\x00\x00\x00\x09\x08\x04\x00\x00\x00\xbb\x93\x95\x16\
|
||||
\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\
|
||||
\x02bKGD\x00\xff\x87\x8f\xcc\xbf\x00\x00\x00\x09p\
|
||||
HYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\
|
||||
\x00\x00\x00\x07tIME\x07\xdc\x08\x17\x14\x1c\x1f$\
|
||||
\xc6\x09\x17\x00\x00\x00$IDAT\x08\xd7c`@\
|
||||
\x05\xff\xcf\xc3XL\xc8\x5c&dY&d\xc5p\x0e\
|
||||
\xa3!\x9c\xc3h\x88a\x1a\x0a\x00\x00m\x84\x09u7\
|
||||
\x9e\xd9#\x00\x00\x00\x00IEND\xaeB`\x82\
|
||||
\x00\x00\x070\
|
||||
\x89\
|
||||
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
|
||||
\x00\x00\x0a\x00\x00\x00\x07\x08\x06\x00\x00\x001\xac\xdcc\
|
||||
\x00\x00\x04\xb0iTXtXML:com.\
|
||||
adobe.xmp\x00\x00\x00\x00\x00<?\
|
||||
xpacket begin=\x22\xef\
|
||||
\xbb\xbf\x22 id=\x22W5M0MpCe\
|
||||
hiHzreSzNTczkc9d\
|
||||
\x22?>\x0a<x:xmpmeta x\
|
||||
mlns:x=\x22adobe:ns\
|
||||
:meta/\x22 x:xmptk=\
|
||||
\x22XMP Core 5.5.0\x22\
|
||||
>\x0a <rdf:RDF xmln\
|
||||
s:rdf=\x22http://ww\
|
||||
w.w3.org/1999/02\
|
||||
/22-rdf-syntax-n\
|
||||
s#\x22>\x0a <rdf:Desc\
|
||||
ription rdf:abou\
|
||||
t=\x22\x22\x0a xmlns:e\
|
||||
xif=\x22http://ns.a\
|
||||
dobe.com/exif/1.\
|
||||
0/\x22\x0a xmlns:ti\
|
||||
ff=\x22http://ns.ad\
|
||||
obe.com/tiff/1.0\
|
||||
/\x22\x0a xmlns:pho\
|
||||
toshop=\x22http://n\
|
||||
s.adobe.com/phot\
|
||||
oshop/1.0/\x22\x0a \
|
||||
xmlns:xmp=\x22http:\
|
||||
//ns.adobe.com/x\
|
||||
ap/1.0/\x22\x0a xml\
|
||||
ns:xmpMM=\x22http:/\
|
||||
/ns.adobe.com/xa\
|
||||
p/1.0/mm/\x22\x0a x\
|
||||
mlns:stEvt=\x22http\
|
||||
://ns.adobe.com/\
|
||||
xap/1.0/sType/Re\
|
||||
sourceEvent#\x22\x0a \
|
||||
exif:PixelXDime\
|
||||
nsion=\x2210\x22\x0a ex\
|
||||
if:PixelYDimensi\
|
||||
on=\x227\x22\x0a exif:C\
|
||||
olorSpace=\x221\x22\x0a \
|
||||
tiff:ImageWidth\
|
||||
=\x2210\x22\x0a tiff:Im\
|
||||
ageLength=\x227\x22\x0a \
|
||||
tiff:Resolution\
|
||||
Unit=\x222\x22\x0a tiff\
|
||||
:XResolution=\x2272\
|
||||
.0\x22\x0a tiff:YRes\
|
||||
olution=\x2272.0\x22\x0a \
|
||||
photoshop:Colo\
|
||||
rMode=\x223\x22\x0a pho\
|
||||
toshop:ICCProfil\
|
||||
e=\x22sRGB IEC61966\
|
||||
-2.1\x22\x0a xmp:Mod\
|
||||
ifyDate=\x222021-05\
|
||||
-31T12:33:14+02:\
|
||||
00\x22\x0a xmp:Metad\
|
||||
ataDate=\x222021-05\
|
||||
-31T12:33:14+02:\
|
||||
00\x22>\x0a <xmpMM:H\
|
||||
istory>\x0a <rdf\
|
||||
:Seq>\x0a <rdf:\
|
||||
li\x0a stEvt:a\
|
||||
ction=\x22produced\x22\
|
||||
\x0a stEvt:sof\
|
||||
twareAgent=\x22Affi\
|
||||
nity Designer 1.\
|
||||
9.2\x22\x0a stEvt\
|
||||
:when=\x222021-05-3\
|
||||
1T12:33:14+02:00\
|
||||
\x22/>\x0a </rdf:Se\
|
||||
q>\x0a </xmpMM:Hi\
|
||||
story>\x0a </rdf:D\
|
||||
escription>\x0a </r\
|
||||
df:RDF>\x0a</x:xmpm\
|
||||
eta>\x0a<?xpacket e\
|
||||
nd=\x22r\x22?>H\x8b[^\x00\x00\x01\x83\
|
||||
iCCPsRGB IEC6196\
|
||||
6-2.1\x00\x00(\x91u\x91\xcf+DQ\x14\
|
||||
\xc7?fh\xfc\x18\x8dba1e\x12\x16B\x83\x12\
|
||||
\x1b\x8b\x99\x18\x0a\x8b\x99Q~mf\x9ey3j\xde\
|
||||
x\xbd7\xd2d\xabl\xa7(\xb1\xf1k\xc1_\xc0V\
|
||||
Y+E\xa4d\xa7\xac\x89\x0dz\xce\x9bQ#\x99s\
|
||||
;\xf7|\xee\xf7\xdes\xba\xf7\x5cpD\xd3\x8afV\
|
||||
\xfaA\xcbd\x8dp(\xe0\x9b\x99\x9d\xf3\xb9\x9e\xa8\xa2\
|
||||
\x85\x1a:\xf1\xc6\x14S\x9f\x8c\x8cF)k\xef\xb7T\
|
||||
\xd8\xf1\xba\xdb\xaeU\xfe\xdc\xbfV\xb7\x980\x15\xa8\xa8\
|
||||
\x16\x1eVt#+<&<\xb1\x9a\xd5m\xde\x12n\
|
||||
RR\xb1E\xe1\x13\xe1.C.(|c\xeb\xf1\x22\
|
||||
?\xdb\x9c,\xf2\xa7\xcdF4\x1c\x04G\x83\xb0/\xf9\
|
||||
\x8b\xe3\xbfXI\x19\x9a\xb0\xbc\x9c6-\xbd\xa2\xfc\xdc\
|
||||
\xc7~\x89;\x91\x99\x8eHl\x15\xf7b\x12&D\x00\
|
||||
\x1f\xe3\x8c\x10d\x80^\x86d\x1e\xa0\x9b>zdE\
|
||||
\x99|\x7f!\x7f\x8ae\xc9Ud\xd6\xc9a\xb0D\x92\
|
||||
\x14Y\xbaD]\x91\xea\x09\x89\xaa\xe8\x09\x19irv\
|
||||
\xff\xff\xf6\xd5T\xfb\xfb\x8a\xd5\xdd\x01\xa8z\xb4\xac\xd7\
|
||||
vpm\xc2W\xde\xb2>\x0e,\xeb\xeb\x10\x9c\x0fp\
|
||||
\x9e)\xe5/\xef\xc3\xe0\x9b\xe8\xf9\x92\xd6\xb6\x07\x9eu\
|
||||
8\xbd(i\xf1m8\xdb\x80\xe6{=f\xc4\x0a\x92\
|
||||
S\xdc\xa1\xaa\xf0r\x0c\xf5\xb3\xd0x\x05\xb5\xf3\xc5\x9e\
|
||||
\xfd\xecst\x07\xd15\xf9\xaaK\xd8\xd9\x85\x0e9\xef\
|
||||
Y\xf8\x06\x8e\xfdg\xf8\xfd\x8a\x18\x97\x00\x00\x00\x09p\
|
||||
HYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\
|
||||
\x00\x00\x00\x97IDAT\x18\x95m\xcf\xb1j\x02A\
|
||||
\x14\x85\xe1o\xb7\xb6\xd0'H=Vi\x03\xb1\xb4H\
|
||||
;l\xa5\xf19\xf6Y\x02VB\xbaa\x0a\x0b;\x1b\
|
||||
\x1bkA\x18\x02)m\xe3\xbe\x82\xcd\x06\x16\xd9\xdb\xdd\
|
||||
\x9f\xff\x5c\xee\xa9b*\x13Ls\x13nF&\xa6\xf2\
|
||||
\x82\xaeF\x8b\xdf\x98\xca\xfb\x88\xb4\xc0\x0f\xda\x1a[t\
|
||||
\xd8\xc7T\xc2@\x9ac\x8f?|U=|\xc5\x09w\
|
||||
\xbc\xa1\xc2\x193,r\x13.\xd5\xe0\xc2\x12\x07\x5cQ\
|
||||
#\xe0#7\xe1\xa8O\x0e\x7f\xda`\xd7\xaf\x9f\xb9\x09\
|
||||
\xdfc\x05\xff\xe5uLe\xf5\xcc\x1f\x0d3,\x83\xb6\
|
||||
\x06D\x83\x00\x00\x00\x00IEND\xaeB`\x82\
|
||||
\x00\x00\x00\xa6\
|
||||
\x89\
|
||||
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
|
||||
\x00\x00\x09\x00\x00\x00\x06\x08\x04\x00\x00\x00\xbb\xce|N\
|
||||
\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\
|
||||
\x02bKGD\x00\xff\x87\x8f\xcc\xbf\x00\x00\x00\x09p\
|
||||
HYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\
|
||||
\x00\x00\x00\x07tIME\x07\xdc\x08\x17\x08\x15;\xdc\
|
||||
;\x0c\x9b\x00\x00\x00*IDAT\x08\xd7c`\xc0\
|
||||
\x00\x8c\x0c\x0cs> \x0b\xa4\x08020 \x0b\xa6\
|
||||
\x08000B\x98\x10\xc1\x14\x01\x14\x13P\xb5\xa3\x01\
|
||||
\x00\xc6\xb9\x07\x90]f\x1f\x83\x00\x00\x00\x00IEN\
|
||||
D\xaeB`\x82\
|
||||
\x00\x00\x00\xa0\
|
||||
\x89\
|
||||
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
|
||||
\x00\x00\x06\x00\x00\x00\x09\x08\x04\x00\x00\x00\xbb\x93\x95\x16\
|
||||
\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\
|
||||
\x02bKGD\x00\xff\x87\x8f\xcc\xbf\x00\x00\x00\x09p\
|
||||
HYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\
|
||||
\x00\x00\x00\x07tIME\x07\xdc\x08\x17\x14\x1c\x1f$\
|
||||
\xc6\x09\x17\x00\x00\x00$IDAT\x08\xd7c`@\
|
||||
\x05\xff\xcf\xc3XL\xc8\x5c&dY&d\xc5p\x0e\
|
||||
\xa3!\x9c\xc3h\x88a\x1a\x0a\x00\x00m\x84\x09u7\
|
||||
\x9e\xd9#\x00\x00\x00\x00IEND\xaeB`\x82\
|
||||
\x00\x00\x00\xa6\
|
||||
\x89\
|
||||
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
|
||||
\x00\x00\x09\x00\x00\x00\x06\x08\x04\x00\x00\x00\xbb\xce|N\
|
||||
\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\
|
||||
\x02bKGD\x00\xff\x87\x8f\xcc\xbf\x00\x00\x00\x09p\
|
||||
HYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\
|
||||
\x00\x00\x00\x07tIME\x07\xdc\x08\x17\x08\x15;\xdc\
|
||||
;\x0c\x9b\x00\x00\x00*IDAT\x08\xd7c`\xc0\
|
||||
\x00\x8c\x0c\x0cs> \x0b\xa4\x08020 \x0b\xa6\
|
||||
\x08000B\x98\x10\xc1\x14\x01\x14\x13P\xb5\xa3\x01\
|
||||
\x00\xc6\xb9\x07\x90]f\x1f\x83\x00\x00\x00\x00IEN\
|
||||
D\xaeB`\x82\
|
||||
"
|
||||
|
||||
qt_resource_name = b"\
|
||||
|
|
@ -692,62 +695,6 @@ qt_resource_name = b"\
|
|||
\x07\x03}\xc3\
|
||||
\x00i\
|
||||
\x00m\x00a\x00g\x00e\x00s\
|
||||
\x00\x15\
|
||||
\x0f\xf3\xc0\x07\
|
||||
\x00u\
|
||||
\x00p\x00_\x00a\x00r\x00r\x00o\x00w\x00_\x00d\x00i\x00s\x00a\x00b\x00l\x00e\x00d\
|
||||
\x00.\x00p\x00n\x00g\
|
||||
\x00\x12\
|
||||
\x01.\x03'\
|
||||
\x00c\
|
||||
\x00o\x00m\x00b\x00o\x00b\x00o\x00x\x00_\x00a\x00r\x00r\x00o\x00w\x00.\x00p\x00n\
|
||||
\x00g\
|
||||
\x00\x0e\
|
||||
\x04\xa2\xfc\xa7\
|
||||
\x00d\
|
||||
\x00o\x00w\x00n\x00_\x00a\x00r\x00r\x00o\x00w\x00.\x00p\x00n\x00g\
|
||||
\x00\x1b\
|
||||
\x03Z2'\
|
||||
\x00c\
|
||||
\x00o\x00m\x00b\x00o\x00b\x00o\x00x\x00_\x00a\x00r\x00r\x00o\x00w\x00_\x00d\x00i\
|
||||
\x00s\x00a\x00b\x00l\x00e\x00d\x00.\x00p\x00n\x00g\
|
||||
\x00\x18\
|
||||
\x03\x8e\xdeg\
|
||||
\x00r\
|
||||
\x00i\x00g\x00h\x00t\x00_\x00a\x00r\x00r\x00o\x00w\x00_\x00d\x00i\x00s\x00a\x00b\
|
||||
\x00l\x00e\x00d\x00.\x00p\x00n\x00g\
|
||||
\x00\x11\
|
||||
\x00\xb8\x8c\x07\
|
||||
\x00l\
|
||||
\x00e\x00f\x00t\x00_\x00a\x00r\x00r\x00o\x00w\x00_\x00o\x00n\x00.\x00p\x00n\x00g\
|
||||
\
|
||||
\x00\x0f\
|
||||
\x01s\x8b\x07\
|
||||
\x00u\
|
||||
\x00p\x00_\x00a\x00r\x00r\x00o\x00w\x00_\x00o\x00n\x00.\x00p\x00n\x00g\
|
||||
\x00\x0c\
|
||||
\x06\xe6\xe6g\
|
||||
\x00u\
|
||||
\x00p\x00_\x00a\x00r\x00r\x00o\x00w\x00.\x00p\x00n\x00g\
|
||||
\x00\x0f\
|
||||
\x06S%\xa7\
|
||||
\x00b\
|
||||
\x00r\x00a\x00n\x00c\x00h\x00_\x00o\x00p\x00e\x00n\x00.\x00p\x00n\x00g\
|
||||
\x00\x17\
|
||||
\x0ce\xce\x07\
|
||||
\x00l\
|
||||
\x00e\x00f\x00t\x00_\x00a\x00r\x00r\x00o\x00w\x00_\x00d\x00i\x00s\x00a\x00b\x00l\
|
||||
\x00e\x00d\x00.\x00p\x00n\x00g\
|
||||
\x00\x14\
|
||||
\x04^-\xa7\
|
||||
\x00b\
|
||||
\x00r\x00a\x00n\x00c\x00h\x00_\x00c\x00l\x00o\x00s\x00e\x00d\x00_\x00o\x00n\x00.\
|
||||
\x00p\x00n\x00g\
|
||||
\x00\x11\
|
||||
\x0b\xda0\xa7\
|
||||
\x00b\
|
||||
\x00r\x00a\x00n\x00c\x00h\x00_\x00c\x00l\x00o\x00s\x00e\x00d\x00.\x00p\x00n\x00g\
|
||||
\
|
||||
\x00\x0e\
|
||||
\x0e\xde\xfa\xc7\
|
||||
\x00l\
|
||||
|
|
@ -757,87 +704,121 @@ qt_resource_name = b"\
|
|||
\x00d\
|
||||
\x00o\x00w\x00n\x00_\x00a\x00r\x00r\x00o\x00w\x00_\x00o\x00n\x00.\x00p\x00n\x00g\
|
||||
\
|
||||
\x00\x0f\
|
||||
\x02\x9f\x05\x87\
|
||||
\x00r\
|
||||
\x00i\x00g\x00h\x00t\x00_\x00a\x00r\x00r\x00o\x00w\x00.\x00p\x00n\x00g\
|
||||
\x00\x12\
|
||||
\x01.\x03'\
|
||||
\x00c\
|
||||
\x00o\x00m\x00b\x00o\x00b\x00o\x00x\x00_\x00a\x00r\x00r\x00o\x00w\x00.\x00p\x00n\
|
||||
\x00g\
|
||||
\x00\x12\
|
||||
\x05\x8f\x9d\x07\
|
||||
\x00b\
|
||||
\x00r\x00a\x00n\x00c\x00h\x00_\x00o\x00p\x00e\x00n\x00_\x00o\x00n\x00.\x00p\x00n\
|
||||
\x00g\
|
||||
\x00\x17\
|
||||
\x0c\xabQ\x07\
|
||||
\x00d\
|
||||
\x00o\x00w\x00n\x00_\x00a\x00r\x00r\x00o\x00w\x00_\x00d\x00i\x00s\x00a\x00b\x00l\
|
||||
\x00e\x00d\x00.\x00p\x00n\x00g\
|
||||
\x00\x12\
|
||||
\x03\x8d\x04G\
|
||||
\x00r\
|
||||
\x00i\x00g\x00h\x00t\x00_\x00a\x00r\x00r\x00o\x00w\x00_\x00o\x00n\x00.\x00p\x00n\
|
||||
\x00g\
|
||||
\x00\x0f\
|
||||
\x01s\x8b\x07\
|
||||
\x00u\
|
||||
\x00p\x00_\x00a\x00r\x00r\x00o\x00w\x00_\x00o\x00n\x00.\x00p\x00n\x00g\
|
||||
\x00\x1b\
|
||||
\x03Z2'\
|
||||
\x00c\
|
||||
\x00o\x00m\x00b\x00o\x00b\x00o\x00x\x00_\x00a\x00r\x00r\x00o\x00w\x00_\x00d\x00i\
|
||||
\x00s\x00a\x00b\x00l\x00e\x00d\x00.\x00p\x00n\x00g\
|
||||
\x00\x14\
|
||||
\x04^-\xa7\
|
||||
\x00b\
|
||||
\x00r\x00a\x00n\x00c\x00h\x00_\x00c\x00l\x00o\x00s\x00e\x00d\x00_\x00o\x00n\x00.\
|
||||
\x00p\x00n\x00g\
|
||||
\x00\x0c\
|
||||
\x06\xe6\xe6g\
|
||||
\x00u\
|
||||
\x00p\x00_\x00a\x00r\x00r\x00o\x00w\x00.\x00p\x00n\x00g\
|
||||
\x00\x17\
|
||||
\x0c\xabQ\x07\
|
||||
\x00d\
|
||||
\x00o\x00w\x00n\x00_\x00a\x00r\x00r\x00o\x00w\x00_\x00d\x00i\x00s\x00a\x00b\x00l\
|
||||
\x00e\x00d\x00.\x00p\x00n\x00g\
|
||||
\x00\x15\
|
||||
\x03'rg\
|
||||
\x00c\
|
||||
\x00o\x00m\x00b\x00o\x00b\x00o\x00x\x00_\x00a\x00r\x00r\x00o\x00w\x00_\x00o\x00n\
|
||||
\x00.\x00p\x00n\x00g\
|
||||
\x00\x0e\
|
||||
\x04\xa2\xfc\xa7\
|
||||
\x00d\
|
||||
\x00o\x00w\x00n\x00_\x00a\x00r\x00r\x00o\x00w\x00.\x00p\x00n\x00g\
|
||||
\x00\x18\
|
||||
\x03\x8e\xdeg\
|
||||
\x00r\
|
||||
\x00i\x00g\x00h\x00t\x00_\x00a\x00r\x00r\x00o\x00w\x00_\x00d\x00i\x00s\x00a\x00b\
|
||||
\x00l\x00e\x00d\x00.\x00p\x00n\x00g\
|
||||
\x00\x15\
|
||||
\x0f\xf3\xc0\x07\
|
||||
\x00u\
|
||||
\x00p\x00_\x00a\x00r\x00r\x00o\x00w\x00_\x00d\x00i\x00s\x00a\x00b\x00l\x00e\x00d\
|
||||
\x00.\x00p\x00n\x00g\
|
||||
\x00\x0f\
|
||||
\x06S%\xa7\
|
||||
\x00b\
|
||||
\x00r\x00a\x00n\x00c\x00h\x00_\x00o\x00p\x00e\x00n\x00.\x00p\x00n\x00g\
|
||||
\x00\x17\
|
||||
\x0ce\xce\x07\
|
||||
\x00l\
|
||||
\x00e\x00f\x00t\x00_\x00a\x00r\x00r\x00o\x00w\x00_\x00d\x00i\x00s\x00a\x00b\x00l\
|
||||
\x00e\x00d\x00.\x00p\x00n\x00g\
|
||||
\x00\x0f\
|
||||
\x02\x9f\x05\x87\
|
||||
\x00r\
|
||||
\x00i\x00g\x00h\x00t\x00_\x00a\x00r\x00r\x00o\x00w\x00.\x00p\x00n\x00g\
|
||||
\x00\x11\
|
||||
\x0b\xda0\xa7\
|
||||
\x00b\
|
||||
\x00r\x00a\x00n\x00c\x00h\x00_\x00c\x00l\x00o\x00s\x00e\x00d\x00.\x00p\x00n\x00g\
|
||||
\
|
||||
\x00\x11\
|
||||
\x00\xb8\x8c\x07\
|
||||
\x00l\
|
||||
\x00e\x00f\x00t\x00_\x00a\x00r\x00r\x00o\x00w\x00_\x00o\x00n\x00.\x00p\x00n\x00g\
|
||||
\
|
||||
"
|
||||
|
||||
qt_resource_struct = b"\
|
||||
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\
|
||||
\x00\x00\x00\x00\x00\x00\x00\x00\
|
||||
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\
|
||||
\x00\x00\x00\x00\x00\x00\x00\x00\
|
||||
\x00\x00\x00\x16\x00\x02\x00\x00\x00\x13\x00\x00\x00\x03\
|
||||
\x00\x00\x00\x00\x00\x00\x00\x00\
|
||||
\x00\x00\x01\x16\x00\x00\x00\x00\x00\x01\x00\x00\x03C\
|
||||
\x00\x00\x01vA\x9d\xa25\
|
||||
\x00\x00\x02P\x00\x00\x00\x00\x00\x01\x00\x00\x1d!\
|
||||
\x00\x00\x01vA\x9d\xa25\
|
||||
\x00\x00\x00X\x00\x00\x00\x00\x00\x01\x00\x00\x00\xa3\
|
||||
\x00\x00\x01y\xb4r\xcc\x9c\
|
||||
\x00\x00\x01>\x00\x00\x00\x00\x00\x01\x00\x00\x03\xed\
|
||||
\x00\x00\x01vA\x9d\xa29\
|
||||
\x00\x00\x02x\x00\x00\x00\x00\x00\x01\x00\x00\x1d\xca\
|
||||
\x00\x00\x01vA\x9d\xa27\
|
||||
\x00\x00\x03$\x00\x00\x00\x00\x00\x01\x00\x00&\xf0\
|
||||
\x00\x00\x01y\xb4r\xcc\x9c\
|
||||
\x00\x00\x00\xa4\x00\x00\x00\x00\x00\x01\x00\x00\x01\xf6\
|
||||
\x00\x00\x01y\xb4r\xcc\x9c\
|
||||
\x00\x00\x02\xfa\x00\x00\x00\x00\x00\x01\x00\x00&L\
|
||||
\x00\x00\x01vA\x9d\xa27\
|
||||
\x00\x00\x00\xe0\x00\x00\x00\x00\x00\x01\x00\x00\x02\x9f\
|
||||
\x00\x00\x01vA\x9d\xa27\
|
||||
\x00\x00\x01\xd8\x00\x00\x00\x00\x00\x01\x00\x00\x0c\xe5\
|
||||
\x00\x00\x01y\xc2\x05+`\
|
||||
\x00\x00\x00\x82\x00\x00\x00\x00\x00\x01\x00\x00\x01M\
|
||||
\x00\x00\x01vA\x9d\xa25\
|
||||
\x00\x00\x02\x9c\x00\x00\x00\x00\x00\x01\x00\x00\x1en\
|
||||
\x00\x00\x01y\xc1\xfc\x16\x91\
|
||||
\x00\x00\x01\x80\x00\x00\x00\x00\x00\x01\x00\x00\x051\
|
||||
\x00\x00\x01y\xc1\xf9Kx\
|
||||
\x00\x00\x01b\x00\x00\x00\x00\x00\x01\x00\x00\x04\x8f\
|
||||
\x00\x00\x01vA\x9d\xa29\
|
||||
\x00\x00\x02\x06\x00\x00\x00\x00\x00\x01\x00\x00\x14\xc6\
|
||||
\x00\x00\x01y\xc2\x05\x91*\
|
||||
\x00\x00\x01\xa4\x00\x00\x00\x00\x00\x01\x00\x00\x0c;\
|
||||
\x00\x00\x01vA\x9d\xa25\
|
||||
\x00\x00\x02\xc6\x00\x00\x00\x00\x00\x01\x00\x00%\xa2\
|
||||
\x00\x00\x01vA\x9d\xa25\
|
||||
\x00\x00\x02.\x00\x00\x00\x00\x00\x01\x00\x00\x1cw\
|
||||
\x00\x00\x01vA\x9d\xa25\
|
||||
\x00\x00\x03,\x00\x00\x00\x00\x00\x01\x00\x00&\xf0\
|
||||
\x00\x00\x00J\x00\x00\x00\x00\x00\x01\x00\x00\x00\xaa\
|
||||
\x00\x00\x00r\x00\x00\x00\x00\x00\x01\x00\x00\x01S\
|
||||
\x00\x00\x00\xf0\x00\x00\x00\x00\x00\x01\x00\x00\x09\xd5\
|
||||
\x00\x00\x02\xe0\x00\x00\x00\x00\x00\x01\x00\x00\x1e\x9b\
|
||||
\x00\x00\x01\xd0\x00\x00\x00\x00\x00\x01\x00\x00\x14M\
|
||||
\x00\x00\x01\x14\x00\x00\x00\x00\x00\x01\x00\x00\x0aw\
|
||||
\x00\x00\x00\xc6\x00\x00\x00\x00\x00\x01\x00\x00\x091\
|
||||
\x00\x00\x02\x22\x00\x00\x00\x00\x00\x01\x00\x00\x15\xa0\
|
||||
\x00\x00\x01P\x00\x00\x00\x00\x00\x01\x00\x00\x0b \
|
||||
\x00\x00\x02\x00\x00\x00\x00\x00\x00\x01\x00\x00\x14\xf7\
|
||||
\x00\x00\x00\x9c\x00\x00\x00\x00\x00\x01\x00\x00\x01\xfd\
|
||||
\x00\x00\x02\x88\x00\x00\x00\x00\x00\x01\x00\x00\x16\xe7\
|
||||
\x00\x00\x01~\x00\x00\x00\x00\x00\x01\x00\x00\x13\x01\
|
||||
\x00\x00\x03\x04\x00\x00\x00\x00\x00\x01\x00\x00\x1f?\
|
||||
\x00\x00\x02\xac\x00\x00\x00\x00\x00\x01\x00\x00\x1d\xf1\
|
||||
\x00\x00\x01\x9c\x00\x00\x00\x00\x00\x01\x00\x00\x13\xa3\
|
||||
\x00\x00\x00(\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
|
||||
\x00\x00\x01vA\x9d\xa29\
|
||||
\x00\x00\x02X\x00\x00\x00\x00\x00\x01\x00\x00\x16D\
|
||||
"
|
||||
|
||||
|
||||
def qInitResources():
|
||||
QtCore.qRegisterResourceData(
|
||||
0x03, qt_resource_struct, qt_resource_name, qt_resource_data
|
||||
0x01, qt_resource_struct, qt_resource_name, qt_resource_data
|
||||
)
|
||||
|
||||
|
||||
def qCleanupResources():
|
||||
QtCore.qUnregisterResourceData(
|
||||
0x03, qt_resource_struct, qt_resource_name, qt_resource_data
|
||||
0x01, qt_resource_struct, qt_resource_name, qt_resource_data
|
||||
)
|
||||
|
|
|
|||
|
|
@ -19,5 +19,6 @@
|
|||
<file>images/up_arrow.png</file>
|
||||
<file>images/up_arrow_disabled.png</file>
|
||||
<file>images/up_arrow_on.png</file>
|
||||
<file>images/transparent.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
|
|
|||
|
|
@ -200,12 +200,28 @@ QComboBox::down-arrow, QComboBox::down-arrow:on, QComboBox::down-arrow:hover, QC
|
|||
}
|
||||
|
||||
/* Splitter */
|
||||
QSplitter {
|
||||
border: none;
|
||||
QSplitter::handle {
|
||||
border: 3px solid transparent;
|
||||
}
|
||||
|
||||
QSplitter::handle {
|
||||
border: 1px dotted {color:bg-menu-separator};
|
||||
QSplitter::handle:horizontal {
|
||||
/* must be single like because of Nuke*/
|
||||
background: qlineargradient(x1:0, y1:0, x2:1, y2:0,stop:0.3 rgba(0, 0, 0, 0),stop:0.5 {color:bg-splitter},stop:0.7 rgba(0, 0, 0, 0));
|
||||
}
|
||||
|
||||
QSplitter::handle:vertical {
|
||||
/* must be single like because of Nuke*/
|
||||
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,stop:0.3 rgba(0, 0, 0, 0),stop:0.5 {color:bg-splitter},stop:0.7 rgba(0, 0, 0, 0));
|
||||
}
|
||||
|
||||
QSplitter::handle:horizontal:hover {
|
||||
/* must be single like because of Nuke*/
|
||||
background: qlineargradient(x1:0, y1:0, x2:1, y2:0,stop:0.3 rgba(0, 0, 0, 0),stop:0.5 {color:bg-splitter-hover},stop:0.7 rgba(0, 0, 0, 0));
|
||||
}
|
||||
|
||||
QSplitter::handle:vertical:hover {
|
||||
/* must be single like because of Nuke*/
|
||||
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,stop:0.3 rgba(0, 0, 0, 0),stop:0.5 {color:bg-splitter-hover},stop:0.7 rgba(0, 0, 0, 0));
|
||||
}
|
||||
|
||||
/* SLider */
|
||||
|
|
@ -232,18 +248,15 @@ QSlider::groove:focus {
|
|||
border-color: {color:border-focus};
|
||||
}
|
||||
QSlider::handle {
|
||||
background: qlineargradient(
|
||||
x1: 0, y1: 0.5,
|
||||
x2: 1, y2: 0.5,
|
||||
stop: 0 {palette:blue-base},
|
||||
stop: 1 {palette:green-base}
|
||||
);
|
||||
/* must be single like because of Nuke*/
|
||||
background: qlineargradient(x1: 0, y1: 0.5, x2: 1, y2: 0.5,stop: 0 {palette:blue-base},stop: 1 {palette:green-base});
|
||||
border: 1px solid #5c5c5c;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
QSlider::handle:horizontal {
|
||||
margin: -2px 0;
|
||||
}
|
||||
|
|
@ -252,12 +265,8 @@ QSlider::handle:vertical {
|
|||
}
|
||||
|
||||
QSlider::handle:disabled {
|
||||
background: qlineargradient(
|
||||
x1:0, y1:0,
|
||||
x2:1, y2:1,
|
||||
stop:0 {color:bg-buttons},
|
||||
stop:1 {color:bg-buttons-disabled}
|
||||
);
|
||||
/* must be single like because of Nuke*/
|
||||
background: qlineargradient(x1:0, y1:0,x2:1, y2:1,stop:0 {color:bg-buttons},stop:1 {color:bg-buttons-disabled});
|
||||
}
|
||||
|
||||
/* Tab widget*/
|
||||
|
|
@ -275,19 +284,15 @@ QTabBar::tab {
|
|||
border-left: 3px solid transparent;
|
||||
border-top: 1px solid {color:border};
|
||||
border-right: 1px solid {color:border};
|
||||
background: qlineargradient(
|
||||
x1: 0, y1: 1, x2: 0, y2: 0,
|
||||
stop: 0.5 {color:bg}, stop: 1.0 {color:bg-inputs}
|
||||
);
|
||||
/* must be single like because of Nuke*/
|
||||
background: qlineargradient(x1: 0, y1: 1, x2: 0, y2: 0,stop: 0.5 {color:bg}, stop: 1.0 {color:bg-inputs});
|
||||
}
|
||||
|
||||
QTabBar::tab:selected {
|
||||
background: {color:grey-lighter};
|
||||
border-left: 3px solid {color:border-focus};
|
||||
background: qlineargradient(
|
||||
x1: 0, y1: 1, x2: 0, y2: 0,
|
||||
stop: 0.5 {color:bg}, stop: 1.0 {color:border}
|
||||
);
|
||||
/* must be single like because of Nuke*/
|
||||
background: qlineargradient(x1: 0, y1: 1, x2: 0, y2: 0,stop: 0.5 {color:bg}, stop: 1.0 {color:border});
|
||||
}
|
||||
|
||||
QTabBar::tab:!selected {
|
||||
|
|
@ -335,6 +340,15 @@ QHeaderView::section:first {
|
|||
QHeaderView::section:last {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
QHeaderView::down-arrow {
|
||||
image: url(:/openpype/images/down_arrow.png);
|
||||
}
|
||||
|
||||
QHeaderView::up-arrow {
|
||||
image: url(:/openpype/images/up_arrow.png);
|
||||
}
|
||||
|
||||
/* Views QListView QTreeView QTableView */
|
||||
QAbstractItemView {
|
||||
border: 0px solid {color:border};
|
||||
|
|
@ -393,23 +407,42 @@ QAbstractItemView::branch:open:has-children:has-siblings {
|
|||
QAbstractItemView::branch:open:has-children:!has-siblings:hover,
|
||||
QAbstractItemView::branch:open:has-children:has-siblings:hover {
|
||||
border-image: none;
|
||||
image: url(:/openpype/images//branch_open_on.png);
|
||||
image: url(:/openpype/images/branch_open_on.png);
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
QAbstractItemView::branch:has-children:!has-siblings:closed,
|
||||
QAbstractItemView::branch:closed:has-children:has-siblings {
|
||||
border-image: none;
|
||||
image: url(:/openpype/images//branch_closed.png);
|
||||
image: url(:/openpype/images/branch_closed.png);
|
||||
background: transparent;
|
||||
}
|
||||
QAbstractItemView::branch:has-children:!has-siblings:closed:hover,
|
||||
QAbstractItemView::branch:closed:has-children:has-siblings:hover {
|
||||
border-image: none;
|
||||
image: url(:/openpype/images//branch_closed_on.png);
|
||||
image: url(:/openpype/images/branch_closed_on.png);
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
QAbstractItemView::branch:has-siblings:!adjoins-item {
|
||||
border-image: none;
|
||||
image: url(:/openpype/images/transparent.png);
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
QAbstractItemView::branch:has-siblings:adjoins-item {
|
||||
border-image: none;
|
||||
image: url(:/openpype/images/transparent.png);
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
QAbstractItemView::branch:!has-children:!has-siblings:adjoins-item {
|
||||
border-image: none;
|
||||
image: url(:/openpype/images/transparent.png);
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
|
||||
/* Progress bar */
|
||||
QProgressBar {
|
||||
border: 1px solid {color:border};
|
||||
|
|
@ -425,12 +458,8 @@ QProgressBar:vertical {
|
|||
}
|
||||
|
||||
QProgressBar::chunk {
|
||||
background: qlineargradient(
|
||||
x1: 0, y1: 0.5,
|
||||
x2: 1, y2: 0.5,
|
||||
stop: 0 {palette:blue-base},
|
||||
stop: 1 {palette:green-base}
|
||||
);
|
||||
/* must be single like because of Nuke*/
|
||||
background: qlineargradient(x1: 0, y1: 0.5,x2: 1, y2: 0.5,stop: 0 {palette:blue-base},stop: 1 {palette:green-base});
|
||||
}
|
||||
|
||||
/* Scroll bars */
|
||||
|
|
@ -629,3 +658,16 @@ QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {
|
|||
#PythonInterpreterOutput, #PythonCodeEditor {
|
||||
font-family: "Roboto Mono";
|
||||
}
|
||||
|
||||
#SubsetView::item, #RepresentationView:item {
|
||||
padding: 5px 1px;
|
||||
border: 0px;
|
||||
}
|
||||
|
||||
#OptionalActionBody, #OptionalActionOption {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
#OptionalActionBody[state="hover"], #OptionalActionOption[state="hover"] {
|
||||
background: {color:bg-view-hover};
|
||||
}
|
||||
|
|
|
|||
14
openpype/tools/experimental_tools/__init__.py
Normal file
14
openpype/tools/experimental_tools/__init__.py
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
from .tools_def import (
|
||||
ExperimentalTools,
|
||||
LOCAL_EXPERIMENTAL_KEY
|
||||
)
|
||||
|
||||
from .dialog import ExperimentalToolsDialog
|
||||
|
||||
|
||||
__all__ = (
|
||||
"ExperimentalTools",
|
||||
"LOCAL_EXPERIMENTAL_KEY",
|
||||
|
||||
"ExperimentalToolsDialog"
|
||||
)
|
||||
212
openpype/tools/experimental_tools/dialog.py
Normal file
212
openpype/tools/experimental_tools/dialog.py
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
from Qt import QtWidgets, QtCore, QtGui
|
||||
|
||||
from openpype.style import (
|
||||
load_stylesheet,
|
||||
app_icon_path
|
||||
)
|
||||
|
||||
from .tools_def import ExperimentalTools
|
||||
|
||||
|
||||
class ToolButton(QtWidgets.QPushButton):
|
||||
triggered = QtCore.Signal(str)
|
||||
|
||||
def __init__(self, identifier, *args, **kwargs):
|
||||
super(ToolButton, self).__init__(*args, **kwargs)
|
||||
self._identifier = identifier
|
||||
|
||||
self.clicked.connect(self._on_click)
|
||||
|
||||
def _on_click(self):
|
||||
self.triggered.emit(self._identifier)
|
||||
|
||||
|
||||
class ExperimentalToolsDialog(QtWidgets.QDialog):
|
||||
refresh_interval = 3000
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super(ExperimentalToolsDialog, self).__init__(parent)
|
||||
self.setWindowTitle("OpenPype Experimental tools")
|
||||
icon = QtGui.QIcon(app_icon_path())
|
||||
self.setWindowIcon(icon)
|
||||
|
||||
# Widgets for cases there are not available experimental tools
|
||||
empty_widget = QtWidgets.QWidget(self)
|
||||
|
||||
empty_label = QtWidgets.QLabel(
|
||||
"There are no experimental tools available...", empty_widget
|
||||
)
|
||||
|
||||
empty_btns_layout = QtWidgets.QHBoxLayout()
|
||||
ok_btn = QtWidgets.QPushButton("OK", empty_widget)
|
||||
|
||||
empty_btns_layout.setContentsMargins(0, 0, 0, 0)
|
||||
empty_btns_layout.addStretch(1)
|
||||
empty_btns_layout.addWidget(ok_btn, 0)
|
||||
|
||||
empty_layout = QtWidgets.QVBoxLayout(empty_widget)
|
||||
empty_layout.setContentsMargins(0, 0, 0, 0)
|
||||
empty_layout.addWidget(empty_label)
|
||||
empty_layout.addStretch(1)
|
||||
empty_layout.addLayout(empty_btns_layout)
|
||||
|
||||
# Content of Experimental tools
|
||||
|
||||
# Layout where buttons are added
|
||||
content_layout = QtWidgets.QVBoxLayout()
|
||||
content_layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
# Separator line
|
||||
separator_widget = QtWidgets.QWidget(self)
|
||||
separator_widget.setObjectName("Separator")
|
||||
separator_widget.setMinimumHeight(2)
|
||||
separator_widget.setMaximumHeight(2)
|
||||
|
||||
# Label describing how to turn off tools
|
||||
tool_btns_widget = QtWidgets.QWidget(self)
|
||||
tool_btns_label = QtWidgets.QLabel(
|
||||
(
|
||||
"You can enable these features in"
|
||||
"<br><b>OpenPype tray -> Settings -> Experimental tools</b>"
|
||||
),
|
||||
tool_btns_widget
|
||||
)
|
||||
tool_btns_label.setAlignment(QtCore.Qt.AlignCenter)
|
||||
|
||||
tool_btns_layout = QtWidgets.QVBoxLayout(tool_btns_widget)
|
||||
tool_btns_layout.setContentsMargins(0, 0, 0, 0)
|
||||
tool_btns_layout.addLayout(content_layout)
|
||||
tool_btns_layout.addStretch(1)
|
||||
tool_btns_layout.addWidget(separator_widget, 0)
|
||||
tool_btns_layout.addWidget(tool_btns_label, 0)
|
||||
|
||||
experimental_tools = ExperimentalTools()
|
||||
|
||||
# Main layout
|
||||
layout = QtWidgets.QVBoxLayout(self)
|
||||
layout.addWidget(empty_widget, 1)
|
||||
layout.addWidget(tool_btns_widget, 1)
|
||||
|
||||
refresh_timer = QtCore.QTimer()
|
||||
refresh_timer.setInterval(self.refresh_interval)
|
||||
refresh_timer.timeout.connect(self._on_refresh_timeout)
|
||||
|
||||
ok_btn.clicked.connect(self._on_ok_click)
|
||||
|
||||
self._empty_widget = empty_widget
|
||||
self._tool_btns_widget = tool_btns_widget
|
||||
self._content_layout = content_layout
|
||||
|
||||
self._experimental_tools = experimental_tools
|
||||
self._buttons_by_tool_identifier = {}
|
||||
|
||||
self._refresh_timer = refresh_timer
|
||||
|
||||
# Is dialog first shown
|
||||
self._first_show = True
|
||||
# Trigger refresh when window get's activity
|
||||
self._refresh_on_active = True
|
||||
# Is window active
|
||||
self._window_is_active = False
|
||||
|
||||
def refresh(self):
|
||||
self._experimental_tools.refresh_availability()
|
||||
|
||||
buttons_to_remove = set(self._buttons_by_tool_identifier.keys())
|
||||
for idx, tool in enumerate(self._experimental_tools.tools):
|
||||
identifier = tool.identifier
|
||||
if identifier in buttons_to_remove:
|
||||
buttons_to_remove.remove(identifier)
|
||||
is_new = False
|
||||
button = self._buttons_by_tool_identifier[identifier]
|
||||
else:
|
||||
is_new = True
|
||||
button = ToolButton(identifier, self._tool_btns_widget)
|
||||
button.triggered.connect(self._on_btn_trigger)
|
||||
self._buttons_by_tool_identifier[identifier] = button
|
||||
self._content_layout.insertWidget(idx, button)
|
||||
|
||||
if button.text() != tool.label:
|
||||
button.setText(tool.label)
|
||||
|
||||
if tool.enabled:
|
||||
button.setToolTip(tool.tooltip)
|
||||
|
||||
elif is_new or button.isEnabled():
|
||||
button.setToolTip((
|
||||
"You can enable this tool in local settings."
|
||||
"\n\nOpenPype Tray > Settings > Experimental Tools"
|
||||
))
|
||||
|
||||
if tool.enabled != button.isEnabled():
|
||||
button.setEnabled(tool.enabled)
|
||||
|
||||
for identifier in buttons_to_remove:
|
||||
button = self._buttons_by_tool_identifier.pop(identifier)
|
||||
button.setVisible(False)
|
||||
idx = self._content_layout.indexOf(button)
|
||||
self._content_layout.takeAt(idx)
|
||||
button.deleteLater()
|
||||
|
||||
self._set_visibility()
|
||||
|
||||
def _is_content_visible(self):
|
||||
return len(self._buttons_by_tool_identifier) > 0
|
||||
|
||||
def _set_visibility(self):
|
||||
content_visible = self._is_content_visible()
|
||||
self._tool_btns_widget.setVisible(content_visible)
|
||||
self._empty_widget.setVisible(not content_visible)
|
||||
|
||||
def _on_ok_click(self):
|
||||
self.close()
|
||||
|
||||
def _on_btn_trigger(self, identifier):
|
||||
tool = self._experimental_tools.tools_by_identifier.get(identifier)
|
||||
if tool is not None:
|
||||
tool.execute()
|
||||
|
||||
def showEvent(self, event):
|
||||
super(ExperimentalToolsDialog, self).showEvent(event)
|
||||
|
||||
if self._refresh_on_active:
|
||||
# Start/Restart timer
|
||||
self._refresh_timer.start()
|
||||
# Refresh
|
||||
self.refresh()
|
||||
|
||||
elif not self._refresh_timer.isActive():
|
||||
self._refresh_timer.start()
|
||||
|
||||
if self._first_show:
|
||||
self._first_show = False
|
||||
# Set stylesheet
|
||||
self.setStyleSheet(load_stylesheet())
|
||||
# Resize dialog if there is not content
|
||||
if not self._is_content_visible():
|
||||
size = self.size()
|
||||
size.setWidth(size.width() + size.width() / 3)
|
||||
self.resize(size)
|
||||
|
||||
def changeEvent(self, event):
|
||||
if event.type() == QtCore.QEvent.ActivationChange:
|
||||
self._window_is_active = self.isActiveWindow()
|
||||
if self._window_is_active and self._refresh_on_active:
|
||||
self._refresh_timer.start()
|
||||
self.refresh()
|
||||
|
||||
super(ExperimentalToolsDialog, self).changeEvent(event)
|
||||
|
||||
def _on_refresh_timeout(self):
|
||||
# Stop timer if window is not visible
|
||||
if not self.isVisible():
|
||||
self._refresh_on_active = True
|
||||
self._refresh_timer.stop()
|
||||
|
||||
# Skip refreshing if window is not active
|
||||
elif not self._window_is_active:
|
||||
self._refresh_on_active = True
|
||||
|
||||
# Window is active and visible so we're refreshing buttons
|
||||
else:
|
||||
self.refresh()
|
||||
142
openpype/tools/experimental_tools/tools_def.py
Normal file
142
openpype/tools/experimental_tools/tools_def.py
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
import os
|
||||
from openpype.settings import get_local_settings
|
||||
|
||||
# Constant key under which local settings are stored
|
||||
LOCAL_EXPERIMENTAL_KEY = "experimental_tools"
|
||||
|
||||
|
||||
class ExperimentalTool:
|
||||
"""Definition of experimental tool.
|
||||
|
||||
Definition is used in local settings and in experimental tools dialog.
|
||||
|
||||
Args:
|
||||
identifier (str): String identifier of tool (unique).
|
||||
label (str): Label shown in UI.
|
||||
callback (function): Callback for UI button.
|
||||
tooltip (str): Tooltip showed on button.
|
||||
hosts_filter (list): List of host names for which is tool available.
|
||||
Some tools may not be available in all hosts.
|
||||
"""
|
||||
def __init__(
|
||||
self, identifier, label, callback, tooltip, hosts_filter=None
|
||||
):
|
||||
self.identifier = identifier
|
||||
self.label = label
|
||||
self.callback = callback
|
||||
self.tooltip = tooltip
|
||||
self.hosts_filter = hosts_filter
|
||||
self._enabled = True
|
||||
|
||||
def is_available_for_host(self, host_name):
|
||||
if self.hosts_filter:
|
||||
return host_name in self.hosts_filter
|
||||
return True
|
||||
|
||||
@property
|
||||
def enabled(self):
|
||||
"""Is tool enabled and button is clickable."""
|
||||
return self._enabled
|
||||
|
||||
def set_enabled(self, enabled=True):
|
||||
"""Change if tool is enabled."""
|
||||
self._enabled = enabled
|
||||
|
||||
def execute(self):
|
||||
"""Trigger registerd callback."""
|
||||
self.callback()
|
||||
|
||||
|
||||
class ExperimentalTools:
|
||||
"""Wrapper around experimental tools.
|
||||
|
||||
To add/remove experimental tool just add/remove tool to
|
||||
`experimental_tools` variable in __init__ function.
|
||||
|
||||
Args:
|
||||
parent (QtWidgets.QWidget): Parent widget for tools.
|
||||
host_name (str): Name of host in which context we're now. Environment
|
||||
value 'AVALON_APP' is used when not passed.
|
||||
filter_hosts (bool): Should filter tools. By default is set to 'True'
|
||||
when 'host_name' is passed. Is always set to 'False' if 'host_name'
|
||||
is not defined.
|
||||
"""
|
||||
def __init__(self, parent=None, host_name=None, filter_hosts=None):
|
||||
# Definition of experimental tools
|
||||
experimental_tools = []
|
||||
|
||||
# --- Example tool (callback will just print on click) ---
|
||||
# def example_callback(*args):
|
||||
# print("Triggered tool")
|
||||
#
|
||||
# experimental_tools = [
|
||||
# ExperimentalTool(
|
||||
# "example",
|
||||
# "Example experimental tool",
|
||||
# example_callback,
|
||||
# "Example tool tooltip."
|
||||
# )
|
||||
# ]
|
||||
|
||||
# Try to get host name from env variable `AVALON_APP`
|
||||
if not host_name:
|
||||
host_name = os.environ.get("AVALON_APP")
|
||||
|
||||
# Decide if filtering by host name should happen
|
||||
if filter_hosts is None:
|
||||
filter_hosts = host_name is not None
|
||||
|
||||
if filter_hosts and not host_name:
|
||||
filter_hosts = False
|
||||
|
||||
# Filter tools by host name
|
||||
if filter_hosts:
|
||||
experimental_tools = [
|
||||
tool
|
||||
for tool in experimental_tools
|
||||
if tool.is_available_for_host(host_name)
|
||||
]
|
||||
|
||||
# Store tools by identifier
|
||||
tools_by_identifier = {}
|
||||
for tool in experimental_tools:
|
||||
if tool.identifier in tools_by_identifier:
|
||||
raise KeyError((
|
||||
"Duplicated experimental tool identifier \"{}\""
|
||||
).format(tool.identifier))
|
||||
tools_by_identifier[tool.identifier] = tool
|
||||
|
||||
self._tools_by_identifier = tools_by_identifier
|
||||
self._tools = experimental_tools
|
||||
self._parent_widget = parent
|
||||
|
||||
@property
|
||||
def tools(self):
|
||||
"""Tools in list.
|
||||
|
||||
Returns:
|
||||
list: Tools filtered by host name if filtering was enabled
|
||||
on initialization.
|
||||
"""
|
||||
return self._tools
|
||||
|
||||
@property
|
||||
def tools_by_identifier(self):
|
||||
"""Tools by their identifier.
|
||||
|
||||
Returns:
|
||||
dict: Tools by identifier filtered by host name if filtering
|
||||
was enabled on initialization.
|
||||
"""
|
||||
return self._tools_by_identifier
|
||||
|
||||
def refresh_availability(self):
|
||||
"""Reload local settings and check if any tool changed ability."""
|
||||
local_settings = get_local_settings()
|
||||
experimental_settings = (
|
||||
local_settings.get(LOCAL_EXPERIMENTAL_KEY)
|
||||
) or {}
|
||||
|
||||
for identifier, eperimental_tool in self.tools_by_identifier.items():
|
||||
enabled = experimental_settings.get(identifier, False)
|
||||
eperimental_tool.set_enabled(enabled)
|
||||
|
|
@ -2,8 +2,8 @@ import sys
|
|||
|
||||
from Qt import QtWidgets, QtCore, QtGui
|
||||
|
||||
from avalon import style
|
||||
from avalon.api import AvalonMongoDB
|
||||
from openpype import style
|
||||
from openpype.tools.utils import lib as tools_lib
|
||||
from openpype.tools.loader.widgets import (
|
||||
ThumbnailWidget,
|
||||
|
|
@ -28,155 +28,182 @@ class LibraryLoaderWindow(QtWidgets.QDialog):
|
|||
tool_title = "Library Loader 0.5"
|
||||
tool_name = "library_loader"
|
||||
|
||||
message_timeout = 5000
|
||||
|
||||
def __init__(
|
||||
self, parent=None, icon=None, show_projects=False, show_libraries=True
|
||||
):
|
||||
super(LibraryLoaderWindow, self).__init__(parent)
|
||||
|
||||
self._initial_refresh = False
|
||||
self._ignore_project_change = False
|
||||
|
||||
# Enable minimize and maximize for app
|
||||
# Window modifications
|
||||
self.setWindowTitle(self.tool_title)
|
||||
window_flags = QtCore.Qt.Window
|
||||
if not parent:
|
||||
window_flags |= QtCore.Qt.WindowStaysOnTopHint
|
||||
self.setWindowFlags(window_flags)
|
||||
self.setFocusPolicy(QtCore.Qt.StrongFocus)
|
||||
if icon is not None:
|
||||
self.setWindowIcon(icon)
|
||||
# self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
|
||||
|
||||
body = QtWidgets.QWidget()
|
||||
footer = QtWidgets.QWidget()
|
||||
footer.setFixedHeight(20)
|
||||
icon = QtGui.QIcon(style.app_icon_path())
|
||||
self.setWindowIcon(icon)
|
||||
|
||||
container = QtWidgets.QWidget()
|
||||
self._first_show = True
|
||||
self._initial_refresh = False
|
||||
self._ignore_project_change = False
|
||||
|
||||
self.dbcon = AvalonMongoDB()
|
||||
self.dbcon.install()
|
||||
self.dbcon.Session["AVALON_PROJECT"] = None
|
||||
dbcon = AvalonMongoDB()
|
||||
dbcon.install()
|
||||
dbcon.Session["AVALON_PROJECT"] = None
|
||||
|
||||
self.dbcon = dbcon
|
||||
|
||||
self.show_projects = show_projects
|
||||
self.show_libraries = show_libraries
|
||||
|
||||
# Groups config
|
||||
self.groups_config = tools_lib.GroupsConfig(self.dbcon)
|
||||
self.family_config_cache = tools_lib.FamilyConfigCache(self.dbcon)
|
||||
self.groups_config = tools_lib.GroupsConfig(dbcon)
|
||||
self.family_config_cache = tools_lib.FamilyConfigCache(dbcon)
|
||||
|
||||
assets = AssetWidget(
|
||||
self.dbcon, multiselection=True, parent=self
|
||||
# UI initialization
|
||||
main_splitter = QtWidgets.QSplitter(self)
|
||||
|
||||
# --- Left part ---
|
||||
left_side_splitter = QtWidgets.QSplitter(main_splitter)
|
||||
left_side_splitter.setOrientation(QtCore.Qt.Vertical)
|
||||
|
||||
# Project combobox
|
||||
projects_combobox = QtWidgets.QComboBox(left_side_splitter)
|
||||
combobox_delegate = QtWidgets.QStyledItemDelegate(self)
|
||||
projects_combobox.setItemDelegate(combobox_delegate)
|
||||
|
||||
# Assets widget
|
||||
assets_widget = AssetWidget(
|
||||
dbcon, multiselection=True, parent=left_side_splitter
|
||||
)
|
||||
families = FamilyListView(
|
||||
self.dbcon, self.family_config_cache, parent=self
|
||||
|
||||
# Families widget
|
||||
families_filter_view = FamilyListView(
|
||||
dbcon, self.family_config_cache, left_side_splitter
|
||||
)
|
||||
subsets = LibrarySubsetWidget(
|
||||
self.dbcon,
|
||||
left_side_splitter.addWidget(projects_combobox)
|
||||
left_side_splitter.addWidget(assets_widget)
|
||||
left_side_splitter.addWidget(families_filter_view)
|
||||
left_side_splitter.setStretchFactor(1, 65)
|
||||
left_side_splitter.setStretchFactor(2, 35)
|
||||
|
||||
# --- Middle part ---
|
||||
# Subsets widget
|
||||
subsets_widget = LibrarySubsetWidget(
|
||||
dbcon,
|
||||
self.groups_config,
|
||||
self.family_config_cache,
|
||||
tool_name=self.tool_name,
|
||||
parent=self
|
||||
)
|
||||
|
||||
version = VersionWidget(self.dbcon)
|
||||
thumbnail = ThumbnailWidget(self.dbcon)
|
||||
|
||||
# Project
|
||||
self.combo_projects = QtWidgets.QComboBox()
|
||||
|
||||
# Create splitter to show / hide family filters
|
||||
asset_filter_splitter = QtWidgets.QSplitter()
|
||||
asset_filter_splitter.setOrientation(QtCore.Qt.Vertical)
|
||||
asset_filter_splitter.addWidget(self.combo_projects)
|
||||
asset_filter_splitter.addWidget(assets)
|
||||
asset_filter_splitter.addWidget(families)
|
||||
asset_filter_splitter.setStretchFactor(1, 65)
|
||||
asset_filter_splitter.setStretchFactor(2, 35)
|
||||
|
||||
manager = ModulesManager()
|
||||
sync_server = manager.modules_by_name["sync_server"]
|
||||
|
||||
representations = RepresentationWidget(self.dbcon)
|
||||
thumb_ver_splitter = QtWidgets.QSplitter()
|
||||
# --- Right part ---
|
||||
thumb_ver_splitter = QtWidgets.QSplitter(main_splitter)
|
||||
thumb_ver_splitter.setOrientation(QtCore.Qt.Vertical)
|
||||
thumb_ver_splitter.addWidget(thumbnail)
|
||||
thumb_ver_splitter.addWidget(version)
|
||||
if sync_server.enabled:
|
||||
thumb_ver_splitter.addWidget(representations)
|
||||
|
||||
thumbnail_widget = ThumbnailWidget(dbcon, parent=thumb_ver_splitter)
|
||||
version_info_widget = VersionWidget(dbcon, parent=thumb_ver_splitter)
|
||||
|
||||
thumb_ver_splitter.addWidget(thumbnail_widget)
|
||||
thumb_ver_splitter.addWidget(version_info_widget)
|
||||
|
||||
thumb_ver_splitter.setStretchFactor(0, 30)
|
||||
thumb_ver_splitter.setStretchFactor(1, 35)
|
||||
|
||||
container_layout = QtWidgets.QHBoxLayout(container)
|
||||
container_layout.setContentsMargins(0, 0, 0, 0)
|
||||
split = QtWidgets.QSplitter()
|
||||
split.addWidget(asset_filter_splitter)
|
||||
split.addWidget(subsets)
|
||||
split.addWidget(thumb_ver_splitter)
|
||||
split.setSizes([180, 950, 200])
|
||||
container_layout.addWidget(split)
|
||||
manager = ModulesManager()
|
||||
sync_server = manager.modules_by_name.get("sync_server")
|
||||
sync_server_enabled = False
|
||||
if sync_server is not None:
|
||||
sync_server_enabled = sync_server.enabled
|
||||
|
||||
body_layout = QtWidgets.QHBoxLayout(body)
|
||||
body_layout.addWidget(container)
|
||||
body_layout.setContentsMargins(0, 0, 0, 0)
|
||||
repres_widget = None
|
||||
if sync_server_enabled:
|
||||
repres_widget = RepresentationWidget(
|
||||
dbcon, self.tool_name, parent=thumb_ver_splitter
|
||||
)
|
||||
thumb_ver_splitter.addWidget(repres_widget)
|
||||
|
||||
message = QtWidgets.QLabel()
|
||||
message.hide()
|
||||
main_splitter.addWidget(left_side_splitter)
|
||||
main_splitter.addWidget(subsets_widget)
|
||||
main_splitter.addWidget(thumb_ver_splitter)
|
||||
if sync_server_enabled:
|
||||
main_splitter.setSizes([250, 1000, 550])
|
||||
else:
|
||||
main_splitter.setSizes([250, 850, 200])
|
||||
|
||||
footer_layout = QtWidgets.QVBoxLayout(footer)
|
||||
footer_layout.addWidget(message)
|
||||
# --- Footer ---
|
||||
footer_widget = QtWidgets.QWidget(self)
|
||||
footer_widget.setFixedHeight(20)
|
||||
|
||||
message_label = QtWidgets.QLabel(footer_widget)
|
||||
|
||||
footer_layout = QtWidgets.QVBoxLayout(footer_widget)
|
||||
footer_layout.setContentsMargins(0, 0, 0, 0)
|
||||
footer_layout.addWidget(message_label)
|
||||
|
||||
layout = QtWidgets.QVBoxLayout(self)
|
||||
layout.addWidget(body)
|
||||
layout.addWidget(footer)
|
||||
layout.addWidget(main_splitter)
|
||||
layout.addWidget(footer_widget)
|
||||
|
||||
self.data = {
|
||||
"widgets": {
|
||||
"families": families,
|
||||
"assets": assets,
|
||||
"subsets": subsets,
|
||||
"version": version,
|
||||
"thumbnail": thumbnail,
|
||||
"representations": representations
|
||||
},
|
||||
"label": {
|
||||
"message": message,
|
||||
},
|
||||
"state": {
|
||||
"assetIds": None
|
||||
}
|
||||
}
|
||||
|
||||
families.active_changed.connect(subsets.set_family_filters)
|
||||
assets.selection_changed.connect(self.on_assetschanged)
|
||||
assets.refresh_triggered.connect(self.on_assetschanged)
|
||||
assets.view.clicked.connect(self.on_assetview_click)
|
||||
subsets.active_changed.connect(self.on_subsetschanged)
|
||||
subsets.version_changed.connect(self.on_versionschanged)
|
||||
subsets.refreshed.connect(self._on_subset_refresh)
|
||||
self.combo_projects.currentTextChanged.connect(self.on_project_change)
|
||||
message_timer = QtCore.QTimer()
|
||||
message_timer.setInterval(self.message_timeout)
|
||||
message_timer.setSingleShot(True)
|
||||
|
||||
message_timer.timeout.connect(self._on_message_timeout)
|
||||
|
||||
families_filter_view.active_changed.connect(
|
||||
self._on_family_filter_change
|
||||
)
|
||||
assets_widget.selection_changed.connect(self.on_assetschanged)
|
||||
assets_widget.refresh_triggered.connect(self.on_assetschanged)
|
||||
assets_widget.view.clicked.connect(self.on_assetview_click)
|
||||
subsets_widget.active_changed.connect(self.on_subsetschanged)
|
||||
subsets_widget.version_changed.connect(self.on_versionschanged)
|
||||
subsets_widget.refreshed.connect(self._on_subset_refresh)
|
||||
projects_combobox.currentTextChanged.connect(self.on_project_change)
|
||||
|
||||
self.sync_server = sync_server
|
||||
self._sync_server_enabled = sync_server_enabled
|
||||
|
||||
# Set default thumbnail on start
|
||||
thumbnail.set_thumbnail(None)
|
||||
self._combobox_delegate = combobox_delegate
|
||||
self._projects_combobox = projects_combobox
|
||||
self._assets_widget = assets_widget
|
||||
self._families_filter_view = families_filter_view
|
||||
|
||||
# Defaults
|
||||
if sync_server.enabled:
|
||||
split.setSizes([250, 1000, 550])
|
||||
self.resize(1800, 900)
|
||||
else:
|
||||
split.setSizes([250, 850, 200])
|
||||
self.resize(1300, 700)
|
||||
self._subsets_widget = subsets_widget
|
||||
|
||||
self._version_info_widget = version_info_widget
|
||||
self._thumbnail_widget = thumbnail_widget
|
||||
self._repres_widget = repres_widget
|
||||
|
||||
self._message_label = message_label
|
||||
self._message_timer = message_timer
|
||||
|
||||
def showEvent(self, event):
|
||||
super(LibraryLoaderWindow, self).showEvent(event)
|
||||
if self._first_show:
|
||||
self._first_show = False
|
||||
self.setStyleSheet(style.load_stylesheet())
|
||||
if self._sync_server_enabled:
|
||||
self.resize(1800, 900)
|
||||
else:
|
||||
self.resize(1300, 700)
|
||||
|
||||
if not self._initial_refresh:
|
||||
self._initial_refresh = True
|
||||
self.refresh()
|
||||
|
||||
def on_assetview_click(self, *args):
|
||||
subsets_widget = self.data["widgets"]["subsets"]
|
||||
selection_model = subsets_widget.view.selectionModel()
|
||||
selection_model = self._subsets_widget.view.selectionModel()
|
||||
if selection_model.selectedIndexes():
|
||||
selection_model.clearSelection()
|
||||
|
||||
|
|
@ -187,7 +214,7 @@ class LibraryLoaderWindow(QtWidgets.QDialog):
|
|||
self._ignore_project_change = True
|
||||
|
||||
# Cleanup
|
||||
self.combo_projects.clear()
|
||||
self._projects_combobox.clear()
|
||||
|
||||
# Fill combobox with projects
|
||||
select_project_item = QtGui.QStandardItem("< Select project >")
|
||||
|
|
@ -202,18 +229,18 @@ class LibraryLoaderWindow(QtWidgets.QDialog):
|
|||
item.setData(project_name, QtCore.Qt.UserRole + 1)
|
||||
combobox_items.append(item)
|
||||
|
||||
root_item = self.combo_projects.model().invisibleRootItem()
|
||||
root_item = self._projects_combobox.model().invisibleRootItem()
|
||||
root_item.appendRows(combobox_items)
|
||||
|
||||
index = 0
|
||||
self._ignore_project_change = False
|
||||
|
||||
if old_project_name:
|
||||
index = self.combo_projects.findText(
|
||||
index = self._projects_combobox.findText(
|
||||
old_project_name, QtCore.Qt.MatchFixedString
|
||||
)
|
||||
|
||||
self.combo_projects.setCurrentIndex(index)
|
||||
self._projects_combobox.setCurrentIndex(index)
|
||||
|
||||
def get_filtered_projects(self):
|
||||
projects = list()
|
||||
|
|
@ -231,8 +258,8 @@ class LibraryLoaderWindow(QtWidgets.QDialog):
|
|||
if self._ignore_project_change:
|
||||
return
|
||||
|
||||
row = self.combo_projects.currentIndex()
|
||||
index = self.combo_projects.model().index(row, 0)
|
||||
row = self._projects_combobox.currentIndex()
|
||||
index = self._projects_combobox.model().index(row, 0)
|
||||
project_name = index.data(QtCore.Qt.UserRole + 1)
|
||||
|
||||
self.dbcon.Session["AVALON_PROJECT"] = project_name
|
||||
|
|
@ -245,11 +272,9 @@ class LibraryLoaderWindow(QtWidgets.QDialog):
|
|||
"Config `%s` has no function `install`" % _config.__name__
|
||||
)
|
||||
|
||||
subsets = self.data["widgets"]["subsets"]
|
||||
representations = self.data["widgets"]["representations"]
|
||||
|
||||
subsets.on_project_change(self.dbcon.Session["AVALON_PROJECT"])
|
||||
representations.on_project_change(self.dbcon.Session["AVALON_PROJECT"])
|
||||
self._subsets_widget.on_project_change(project_name)
|
||||
if self._repres_widget:
|
||||
self._repres_widget.on_project_change(project_name)
|
||||
|
||||
self.family_config_cache.refresh()
|
||||
self.groups_config.refresh()
|
||||
|
|
@ -263,13 +288,7 @@ class LibraryLoaderWindow(QtWidgets.QDialog):
|
|||
|
||||
@property
|
||||
def current_project(self):
|
||||
if (
|
||||
not self.dbcon.active_project() or
|
||||
self.dbcon.active_project() == ""
|
||||
):
|
||||
return None
|
||||
|
||||
return self.dbcon.active_project()
|
||||
return self.dbcon.active_project() or None
|
||||
|
||||
# -------------------------------
|
||||
# Delay calling blocking methods
|
||||
|
|
@ -292,12 +311,11 @@ class LibraryLoaderWindow(QtWidgets.QDialog):
|
|||
tools_lib.schedule(self._versionschanged, 150, channel="mongo")
|
||||
|
||||
def _on_subset_refresh(self, has_item):
|
||||
subsets_widget = self.data["widgets"]["subsets"]
|
||||
families_view = self.data["widgets"]["families"]
|
||||
|
||||
subsets_widget.set_loading_state(loading=False, empty=not has_item)
|
||||
families = subsets_widget.get_subsets_families()
|
||||
families_view.set_enabled_families(families)
|
||||
self._subsets_widget.set_loading_state(
|
||||
loading=False, empty=not has_item
|
||||
)
|
||||
families = self._subsets_widget.get_subsets_families()
|
||||
self._families_filter_view.set_enabled_families(families)
|
||||
|
||||
def set_context(self, context, refresh=True):
|
||||
self.echo("Setting context: {}".format(context))
|
||||
|
|
@ -307,6 +325,9 @@ class LibraryLoaderWindow(QtWidgets.QDialog):
|
|||
)
|
||||
|
||||
# ------------------------------
|
||||
def _on_family_filter_change(self, families):
|
||||
self._subsets_widget.set_family_filters(families)
|
||||
|
||||
def _refresh(self):
|
||||
if not self._initial_refresh:
|
||||
self._initial_refresh = True
|
||||
|
|
@ -322,74 +343,69 @@ class LibraryLoaderWindow(QtWidgets.QDialog):
|
|||
)
|
||||
assert project_doc, "This is a bug"
|
||||
|
||||
assets_widget = self.data["widgets"]["assets"]
|
||||
families_view = self.data["widgets"]["families"]
|
||||
families_view.set_enabled_families(set())
|
||||
families_view.refresh()
|
||||
self._families_filter_view.set_enabled_families(set())
|
||||
self._families_filter_view.refresh()
|
||||
|
||||
assets_widget.model.stop_fetch_thread()
|
||||
assets_widget.refresh()
|
||||
assets_widget.setFocus()
|
||||
self._assets_widget.model.stop_fetch_thread()
|
||||
self._assets_widget.refresh()
|
||||
self._assets_widget.setFocus()
|
||||
|
||||
def clear_assets_underlines(self):
|
||||
last_asset_ids = self.data["state"]["assetIds"]
|
||||
if not last_asset_ids:
|
||||
return
|
||||
|
||||
assets_widget = self.data["widgets"]["assets"]
|
||||
id_role = assets_widget.model.ObjectIdRole
|
||||
assets_model = self._assets_widget.model
|
||||
id_role = assets_model.ObjectIdRole
|
||||
|
||||
for index in tools_lib.iter_model_rows(assets_widget.model, 0):
|
||||
for index in tools_lib.iter_model_rows(assets_model, 0):
|
||||
if index.data(id_role) not in last_asset_ids:
|
||||
continue
|
||||
|
||||
assets_widget.model.setData(
|
||||
index, [], assets_widget.model.subsetColorsRole
|
||||
assets_model.setData(
|
||||
index, [], assets_model.subsetColorsRole
|
||||
)
|
||||
|
||||
def _assetschanged(self):
|
||||
"""Selected assets have changed"""
|
||||
assets_widget = self.data["widgets"]["assets"]
|
||||
subsets_widget = self.data["widgets"]["subsets"]
|
||||
subsets_model = subsets_widget.model
|
||||
subsets_model = self._subsets_widget.model
|
||||
|
||||
subsets_model.clear()
|
||||
self.clear_assets_underlines()
|
||||
|
||||
if not self.dbcon.Session.get("AVALON_PROJECT"):
|
||||
subsets_widget.set_loading_state(
|
||||
self._subsets_widget.set_loading_state(
|
||||
loading=False,
|
||||
empty=True
|
||||
)
|
||||
return
|
||||
|
||||
# filter None docs they are silo
|
||||
asset_docs = assets_widget.get_selected_assets()
|
||||
asset_docs = self._assets_widget.get_selected_assets()
|
||||
if len(asset_docs) == 0:
|
||||
return
|
||||
|
||||
asset_ids = [asset_doc["_id"] for asset_doc in asset_docs]
|
||||
# Start loading
|
||||
subsets_widget.set_loading_state(
|
||||
self._subsets_widget.set_loading_state(
|
||||
loading=bool(asset_ids),
|
||||
empty=True
|
||||
)
|
||||
|
||||
subsets_model.set_assets(asset_ids)
|
||||
subsets_widget.view.setColumnHidden(
|
||||
self._subsets_widget.view.setColumnHidden(
|
||||
subsets_model.Columns.index("asset"),
|
||||
len(asset_ids) < 2
|
||||
)
|
||||
|
||||
# Clear the version information on asset change
|
||||
self.data["widgets"]["version"].set_version(None)
|
||||
self.data["widgets"]["thumbnail"].set_thumbnail(asset_docs)
|
||||
self._version_info_widget.set_version(None)
|
||||
self._thumbnail_widget.set_thumbnail(asset_docs)
|
||||
|
||||
self.data["state"]["assetIds"] = asset_ids
|
||||
|
||||
representations = self.data["widgets"]["representations"]
|
||||
# reset repre list
|
||||
representations.set_version_ids([])
|
||||
self._repres_widget.set_version_ids([])
|
||||
|
||||
def _subsetschanged(self):
|
||||
asset_ids = self.data["state"]["assetIds"]
|
||||
|
|
@ -398,8 +414,9 @@ class LibraryLoaderWindow(QtWidgets.QDialog):
|
|||
self._versionschanged()
|
||||
return
|
||||
|
||||
subsets = self.data["widgets"]["subsets"]
|
||||
selected_subsets = subsets.selected_subsets(_merged=True, _other=False)
|
||||
selected_subsets = self._subsets_widget.selected_subsets(
|
||||
_merged=True, _other=False
|
||||
)
|
||||
|
||||
asset_models = {}
|
||||
asset_ids = []
|
||||
|
|
@ -420,26 +437,24 @@ class LibraryLoaderWindow(QtWidgets.QDialog):
|
|||
|
||||
self.clear_assets_underlines()
|
||||
|
||||
assets_widget = self.data["widgets"]["assets"]
|
||||
indexes = assets_widget.view.selectionModel().selectedRows()
|
||||
indexes = self._assets_widget.view.selectionModel().selectedRows()
|
||||
|
||||
assets_model = self._assets_widget.model
|
||||
for index in indexes:
|
||||
id = index.data(assets_widget.model.ObjectIdRole)
|
||||
id = index.data(assets_model.ObjectIdRole)
|
||||
if id not in asset_models:
|
||||
continue
|
||||
|
||||
assets_widget.model.setData(
|
||||
index, asset_models[id], assets_widget.model.subsetColorsRole
|
||||
assets_model.setData(
|
||||
index, asset_models[id], assets_model.subsetColorsRole
|
||||
)
|
||||
# Trigger repaint
|
||||
assets_widget.view.updateGeometries()
|
||||
self._assets_widget.view.updateGeometries()
|
||||
# Set version in Version Widget
|
||||
self._versionschanged()
|
||||
|
||||
def _versionschanged(self):
|
||||
|
||||
subsets = self.data["widgets"]["subsets"]
|
||||
selection = subsets.view.selectionModel()
|
||||
selection = self._subsets_widget.view.selectionModel()
|
||||
|
||||
# Active must be in the selected rows otherwise we
|
||||
# assume it's not actually an "active" current index.
|
||||
|
|
@ -448,7 +463,7 @@ class LibraryLoaderWindow(QtWidgets.QDialog):
|
|||
active = selection.currentIndex()
|
||||
rows = selection.selectedRows(column=active.column())
|
||||
if active and active in rows:
|
||||
item = active.data(subsets.model.ItemRole)
|
||||
item = active.data(self._subsets_widget.model.ItemRole)
|
||||
if (
|
||||
item is not None
|
||||
and not (item.get("isGroup") or item.get("isMerged"))
|
||||
|
|
@ -460,7 +475,7 @@ class LibraryLoaderWindow(QtWidgets.QDialog):
|
|||
for index in rows:
|
||||
if not index or not index.isValid():
|
||||
continue
|
||||
item = index.data(subsets.model.ItemRole)
|
||||
item = index.data(self._subsets_widget.model.ItemRole)
|
||||
if (
|
||||
item is None
|
||||
or item.get("isGroup")
|
||||
|
|
@ -469,20 +484,18 @@ class LibraryLoaderWindow(QtWidgets.QDialog):
|
|||
continue
|
||||
version_docs.append(item["version_document"])
|
||||
|
||||
self.data["widgets"]["version"].set_version(version_doc)
|
||||
self._version_info_widget.set_version(version_doc)
|
||||
|
||||
thumbnail_docs = version_docs
|
||||
if not thumbnail_docs:
|
||||
assets_widget = self.data["widgets"]["assets"]
|
||||
asset_docs = assets_widget.get_selected_assets()
|
||||
asset_docs = self._assets_widget.get_selected_assets()
|
||||
if len(asset_docs) > 0:
|
||||
thumbnail_docs = asset_docs
|
||||
|
||||
self.data["widgets"]["thumbnail"].set_thumbnail(thumbnail_docs)
|
||||
self._thumbnail_widget.set_thumbnail(thumbnail_docs)
|
||||
|
||||
representations = self.data["widgets"]["representations"]
|
||||
version_ids = [doc["_id"] for doc in version_docs or []]
|
||||
representations.set_version_ids(version_ids)
|
||||
self._repres_widget.set_version_ids(version_ids)
|
||||
|
||||
def _set_context(self, context, refresh=True):
|
||||
"""Set the selection in the interface using a context.
|
||||
|
|
@ -510,16 +523,15 @@ class LibraryLoaderWindow(QtWidgets.QDialog):
|
|||
# scheduled refresh and the silo tabs are not shown.
|
||||
self._refresh_assets()
|
||||
|
||||
asset_widget = self.data["widgets"]["assets"]
|
||||
asset_widget.select_assets(asset)
|
||||
self._assets_widget.select_assets(asset)
|
||||
|
||||
def _on_message_timeout(self):
|
||||
self._message_label.setText("")
|
||||
|
||||
def echo(self, message):
|
||||
widget = self.data["label"]["message"]
|
||||
widget.setText(str(message))
|
||||
widget.show()
|
||||
self._message_label.setText(str(message))
|
||||
print(message)
|
||||
|
||||
tools_lib.schedule(widget.hide, 5000, channel="message")
|
||||
self._message_timer.start()
|
||||
|
||||
def closeEvent(self, event):
|
||||
# Kill on holding SHIFT
|
||||
|
|
@ -576,7 +588,6 @@ def show(
|
|||
window = LibraryLoaderWindow(
|
||||
parent, icon, show_projects, show_libraries
|
||||
)
|
||||
window.setStyleSheet(style.load_stylesheet())
|
||||
window.show()
|
||||
|
||||
module.window = window
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import sys
|
||||
|
||||
from Qt import QtWidgets, QtCore
|
||||
from avalon import api, io, style, pipeline
|
||||
from avalon import api, io, pipeline
|
||||
|
||||
from openpype import style
|
||||
from openpype.tools.utils.widgets import AssetWidget
|
||||
|
||||
from openpype.tools.utils import lib
|
||||
|
||||
from .widgets import (
|
||||
|
|
@ -37,6 +37,7 @@ class LoaderWindow(QtWidgets.QDialog):
|
|||
"""Asset loader interface"""
|
||||
|
||||
tool_name = "loader"
|
||||
message_timeout = 5000
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super(LoaderWindow, self).__init__(parent)
|
||||
|
|
@ -57,83 +58,85 @@ class LoaderWindow(QtWidgets.QDialog):
|
|||
self.setWindowFlags(window_flags)
|
||||
self.setFocusPolicy(QtCore.Qt.StrongFocus)
|
||||
|
||||
body = QtWidgets.QWidget()
|
||||
footer = QtWidgets.QWidget()
|
||||
footer.setFixedHeight(20)
|
||||
main_splitter = QtWidgets.QSplitter(self)
|
||||
|
||||
container = QtWidgets.QWidget()
|
||||
# --- Left part ---
|
||||
left_side_splitter = QtWidgets.QSplitter(main_splitter)
|
||||
left_side_splitter.setOrientation(QtCore.Qt.Vertical)
|
||||
|
||||
assets = AssetWidget(io, multiselection=True, parent=self)
|
||||
assets.set_current_asset_btn_visibility(True)
|
||||
# Assets widget
|
||||
assets_widget = AssetWidget(
|
||||
io, multiselection=True, parent=left_side_splitter
|
||||
)
|
||||
assets_widget.set_current_asset_btn_visibility(True)
|
||||
|
||||
families = FamilyListView(io, self.family_config_cache, self)
|
||||
subsets = SubsetWidget(
|
||||
# Families widget
|
||||
families_filter_view = FamilyListView(
|
||||
io, self.family_config_cache, left_side_splitter
|
||||
)
|
||||
left_side_splitter.addWidget(assets_widget)
|
||||
left_side_splitter.addWidget(families_filter_view)
|
||||
left_side_splitter.setStretchFactor(0, 65)
|
||||
left_side_splitter.setStretchFactor(1, 35)
|
||||
|
||||
# --- Middle part ---
|
||||
# Subsets widget
|
||||
subsets_widget = SubsetWidget(
|
||||
io,
|
||||
self.groups_config,
|
||||
self.family_config_cache,
|
||||
tool_name=self.tool_name,
|
||||
parent=self
|
||||
parent=main_splitter
|
||||
)
|
||||
version = VersionWidget(io)
|
||||
thumbnail = ThumbnailWidget(io)
|
||||
representations = RepresentationWidget(io, self.tool_name)
|
||||
|
||||
manager = ModulesManager()
|
||||
sync_server = manager.modules_by_name["sync_server"]
|
||||
|
||||
thumb_ver_splitter = QtWidgets.QSplitter()
|
||||
# --- Right part ---
|
||||
thumb_ver_splitter = QtWidgets.QSplitter(main_splitter)
|
||||
thumb_ver_splitter.setOrientation(QtCore.Qt.Vertical)
|
||||
thumb_ver_splitter.addWidget(thumbnail)
|
||||
thumb_ver_splitter.addWidget(version)
|
||||
if sync_server.enabled:
|
||||
thumb_ver_splitter.addWidget(representations)
|
||||
|
||||
thumbnail_widget = ThumbnailWidget(io, parent=thumb_ver_splitter)
|
||||
version_info_widget = VersionWidget(io, parent=thumb_ver_splitter)
|
||||
|
||||
thumb_ver_splitter.addWidget(thumbnail_widget)
|
||||
thumb_ver_splitter.addWidget(version_info_widget)
|
||||
|
||||
thumb_ver_splitter.setStretchFactor(0, 30)
|
||||
thumb_ver_splitter.setStretchFactor(1, 35)
|
||||
|
||||
# Create splitter to show / hide family filters
|
||||
asset_filter_splitter = QtWidgets.QSplitter()
|
||||
asset_filter_splitter.setOrientation(QtCore.Qt.Vertical)
|
||||
asset_filter_splitter.addWidget(assets)
|
||||
asset_filter_splitter.addWidget(families)
|
||||
asset_filter_splitter.setStretchFactor(0, 65)
|
||||
asset_filter_splitter.setStretchFactor(1, 35)
|
||||
manager = ModulesManager()
|
||||
sync_server = manager.modules_by_name.get("sync_server")
|
||||
sync_server_enabled = False
|
||||
if sync_server is not None:
|
||||
sync_server_enabled = sync_server.enabled
|
||||
|
||||
container_layout = QtWidgets.QHBoxLayout(container)
|
||||
container_layout.setContentsMargins(0, 0, 0, 0)
|
||||
split = QtWidgets.QSplitter()
|
||||
split.addWidget(asset_filter_splitter)
|
||||
split.addWidget(subsets)
|
||||
split.addWidget(thumb_ver_splitter)
|
||||
repres_widget = None
|
||||
if sync_server_enabled:
|
||||
repres_widget = RepresentationWidget(
|
||||
io, self.tool_name, parent=thumb_ver_splitter
|
||||
)
|
||||
thumb_ver_splitter.addWidget(repres_widget)
|
||||
|
||||
container_layout.addWidget(split)
|
||||
main_splitter.addWidget(left_side_splitter)
|
||||
main_splitter.addWidget(subsets_widget)
|
||||
main_splitter.addWidget(thumb_ver_splitter)
|
||||
|
||||
body_layout = QtWidgets.QHBoxLayout(body)
|
||||
body_layout.addWidget(container)
|
||||
body_layout.setContentsMargins(0, 0, 0, 0)
|
||||
if sync_server_enabled:
|
||||
main_splitter.setSizes([250, 1000, 550])
|
||||
else:
|
||||
main_splitter.setSizes([250, 850, 200])
|
||||
|
||||
message = QtWidgets.QLabel()
|
||||
message.hide()
|
||||
footer_widget = QtWidgets.QWidget(self)
|
||||
|
||||
footer_layout = QtWidgets.QVBoxLayout(footer)
|
||||
footer_layout.addWidget(message)
|
||||
message_label = QtWidgets.QLabel(footer_widget)
|
||||
|
||||
footer_layout = QtWidgets.QHBoxLayout(footer_widget)
|
||||
footer_layout.setContentsMargins(0, 0, 0, 0)
|
||||
footer_layout.addWidget(message_label, 1)
|
||||
|
||||
layout = QtWidgets.QVBoxLayout(self)
|
||||
layout.addWidget(body)
|
||||
layout.addWidget(footer)
|
||||
layout.addWidget(main_splitter, 1)
|
||||
layout.addWidget(footer_widget, 0)
|
||||
|
||||
self.data = {
|
||||
"widgets": {
|
||||
"families": families,
|
||||
"assets": assets,
|
||||
"subsets": subsets,
|
||||
"version": version,
|
||||
"thumbnail": thumbnail,
|
||||
"representations": representations
|
||||
},
|
||||
"label": {
|
||||
"message": message,
|
||||
},
|
||||
"state": {
|
||||
"assetIds": None
|
||||
}
|
||||
|
|
@ -142,19 +145,44 @@ class LoaderWindow(QtWidgets.QDialog):
|
|||
overlay_frame = OverlayFrame("Loading...", self)
|
||||
overlay_frame.setVisible(False)
|
||||
|
||||
families.active_changed.connect(subsets.set_family_filters)
|
||||
assets.selection_changed.connect(self.on_assetschanged)
|
||||
assets.refresh_triggered.connect(self.on_assetschanged)
|
||||
assets.view.clicked.connect(self.on_assetview_click)
|
||||
subsets.active_changed.connect(self.on_subsetschanged)
|
||||
subsets.version_changed.connect(self.on_versionschanged)
|
||||
subsets.refreshed.connect(self._on_subset_refresh)
|
||||
message_timer = QtCore.QTimer()
|
||||
message_timer.setInterval(self.message_timeout)
|
||||
message_timer.setSingleShot(True)
|
||||
|
||||
subsets.load_started.connect(self._on_load_start)
|
||||
subsets.load_ended.connect(self._on_load_end)
|
||||
representations.load_started.connect(self._on_load_start)
|
||||
representations.load_ended.connect(self._on_load_end)
|
||||
message_timer.timeout.connect(self._on_message_timeout)
|
||||
|
||||
families_filter_view.active_changed.connect(
|
||||
self._on_family_filter_change
|
||||
)
|
||||
assets_widget.selection_changed.connect(self.on_assetschanged)
|
||||
assets_widget.refresh_triggered.connect(self.on_assetschanged)
|
||||
# TODO do not touch view in asset widget
|
||||
assets_widget.view.clicked.connect(self.on_assetview_click)
|
||||
subsets_widget.active_changed.connect(self.on_subsetschanged)
|
||||
subsets_widget.version_changed.connect(self.on_versionschanged)
|
||||
subsets_widget.refreshed.connect(self._on_subset_refresh)
|
||||
|
||||
subsets_widget.load_started.connect(self._on_load_start)
|
||||
subsets_widget.load_ended.connect(self._on_load_end)
|
||||
if repres_widget:
|
||||
repres_widget.load_started.connect(self._on_load_start)
|
||||
repres_widget.load_ended.connect(self._on_load_end)
|
||||
|
||||
self._sync_server_enabled = sync_server_enabled
|
||||
|
||||
self._assets_widget = assets_widget
|
||||
self._families_filter_view = families_filter_view
|
||||
|
||||
self._subsets_widget = subsets_widget
|
||||
|
||||
self._version_info_widget = version_info_widget
|
||||
self._thumbnail_widget = thumbnail_widget
|
||||
self._repres_widget = repres_widget
|
||||
|
||||
self._message_label = message_label
|
||||
self._message_timer = message_timer
|
||||
|
||||
# TODO add overlay using stack widget
|
||||
self._overlay_frame = overlay_frame
|
||||
|
||||
self.family_config_cache.refresh()
|
||||
|
|
@ -163,13 +191,7 @@ class LoaderWindow(QtWidgets.QDialog):
|
|||
self._refresh()
|
||||
self._assetschanged()
|
||||
|
||||
# Defaults
|
||||
if sync_server.enabled:
|
||||
split.setSizes([250, 1000, 550])
|
||||
self.resize(1800, 900)
|
||||
else:
|
||||
split.setSizes([250, 850, 200])
|
||||
self.resize(1300, 700)
|
||||
self._first_show = True
|
||||
|
||||
def resizeEvent(self, event):
|
||||
super(LoaderWindow, self).resizeEvent(event)
|
||||
|
|
@ -179,13 +201,23 @@ class LoaderWindow(QtWidgets.QDialog):
|
|||
super(LoaderWindow, self).moveEvent(event)
|
||||
self._overlay_frame.move(0, 0)
|
||||
|
||||
def showEvent(self, event):
|
||||
super(LoaderWindow, self).showEvent(event)
|
||||
if self._first_show:
|
||||
self._first_show = False
|
||||
self.setStyleSheet(style.load_stylesheet())
|
||||
if self._sync_server_enabled:
|
||||
self.resize(1800, 900)
|
||||
else:
|
||||
self.resize(1300, 700)
|
||||
|
||||
# -------------------------------
|
||||
# Delay calling blocking methods
|
||||
# -------------------------------
|
||||
|
||||
def on_assetview_click(self, *args):
|
||||
subsets_widget = self.data["widgets"]["subsets"]
|
||||
selection_model = subsets_widget.view.selectionModel()
|
||||
# TODO do not touch inner attributes of subset widget
|
||||
selection_model = self._subsets_widget.view.selectionModel()
|
||||
if selection_model.selectedIndexes():
|
||||
selection_model.clearSelection()
|
||||
|
||||
|
|
@ -219,12 +251,11 @@ class LoaderWindow(QtWidgets.QDialog):
|
|||
self._overlay_frame.setVisible(False)
|
||||
|
||||
def _on_subset_refresh(self, has_item):
|
||||
subsets_widget = self.data["widgets"]["subsets"]
|
||||
families_view = self.data["widgets"]["families"]
|
||||
|
||||
subsets_widget.set_loading_state(loading=False, empty=not has_item)
|
||||
families = subsets_widget.get_subsets_families()
|
||||
families_view.set_enabled_families(families)
|
||||
self._subsets_widget.set_loading_state(
|
||||
loading=False, empty=not has_item
|
||||
)
|
||||
families = self._subsets_widget.get_subsets_families()
|
||||
self._families_filter_view.set_enabled_families(families)
|
||||
|
||||
def _on_load_end(self):
|
||||
# Delay hiding as click events happened during loading should be
|
||||
|
|
@ -232,14 +263,14 @@ class LoaderWindow(QtWidgets.QDialog):
|
|||
QtCore.QTimer.singleShot(100, self._hide_overlay)
|
||||
|
||||
# ------------------------------
|
||||
def _on_family_filter_change(self, families):
|
||||
self._subsets_widget.set_family_filters(families)
|
||||
|
||||
def on_context_task_change(self, *args, **kwargs):
|
||||
assets_widget = self.data["widgets"]["assets"]
|
||||
families_view = self.data["widgets"]["families"]
|
||||
# Refresh families config
|
||||
families_view.refresh()
|
||||
self._families_filter_view.refresh()
|
||||
# Change to context asset on context change
|
||||
assets_widget.select_assets(io.Session["AVALON_ASSET"])
|
||||
self._assets_widget.select_assets(io.Session["AVALON_ASSET"])
|
||||
|
||||
def _refresh(self):
|
||||
"""Load assets from database"""
|
||||
|
|
@ -248,12 +279,10 @@ class LoaderWindow(QtWidgets.QDialog):
|
|||
project = io.find_one({"type": "project"}, {"type": 1})
|
||||
assert project, "Project was not found! This is a bug"
|
||||
|
||||
assets_widget = self.data["widgets"]["assets"]
|
||||
assets_widget.refresh()
|
||||
assets_widget.setFocus()
|
||||
self._assets_widget.refresh()
|
||||
self._assets_widget.setFocus()
|
||||
|
||||
families_view = self.data["widgets"]["families"]
|
||||
families_view.refresh()
|
||||
self._families_filter_view.refresh()
|
||||
|
||||
def clear_assets_underlines(self):
|
||||
"""Clear colors from asset data to remove colored underlines
|
||||
|
|
@ -261,11 +290,12 @@ class LoaderWindow(QtWidgets.QDialog):
|
|||
own selected subsets. These colors must be cleared from asset data
|
||||
on selection change so they match current selection.
|
||||
"""
|
||||
last_asset_ids = self.data["state"]["assetIds"]
|
||||
# TODO do not touch inner attributes of asset widget
|
||||
last_asset_ids = self.data["state"]["assetIds"] or []
|
||||
if not last_asset_ids:
|
||||
return
|
||||
|
||||
assets_widget = self.data["widgets"]["assets"]
|
||||
assets_widget = self._assets_widget
|
||||
id_role = assets_widget.model.ObjectIdRole
|
||||
|
||||
for index in lib.iter_model_rows(assets_widget.model, 0):
|
||||
|
|
@ -278,15 +308,15 @@ class LoaderWindow(QtWidgets.QDialog):
|
|||
|
||||
def _assetschanged(self):
|
||||
"""Selected assets have changed"""
|
||||
assets_widget = self.data["widgets"]["assets"]
|
||||
subsets_widget = self.data["widgets"]["subsets"]
|
||||
subsets_widget = self._subsets_widget
|
||||
# TODO do not touch subset widget inner attributes
|
||||
subsets_model = subsets_widget.model
|
||||
|
||||
subsets_model.clear()
|
||||
self.clear_assets_underlines()
|
||||
|
||||
# filter None docs they are silo
|
||||
asset_docs = assets_widget.get_selected_assets()
|
||||
asset_docs = self._assets_widget.get_selected_assets()
|
||||
|
||||
asset_ids = [asset_doc["_id"] for asset_doc in asset_docs]
|
||||
# Start loading
|
||||
|
|
@ -302,14 +332,14 @@ class LoaderWindow(QtWidgets.QDialog):
|
|||
)
|
||||
|
||||
# Clear the version information on asset change
|
||||
self.data["widgets"]["version"].set_version(None)
|
||||
self.data["widgets"]["thumbnail"].set_thumbnail(asset_docs)
|
||||
self._thumbnail_widget.set_thumbnail(asset_docs)
|
||||
self._version_info_widget.set_version(None)
|
||||
|
||||
self.data["state"]["assetIds"] = asset_ids
|
||||
|
||||
representations = self.data["widgets"]["representations"]
|
||||
# reset repre list
|
||||
representations.set_version_ids([])
|
||||
if self._repres_widget is not None:
|
||||
self._repres_widget.set_version_ids([])
|
||||
|
||||
def _subsetschanged(self):
|
||||
asset_ids = self.data["state"]["assetIds"]
|
||||
|
|
@ -318,8 +348,9 @@ class LoaderWindow(QtWidgets.QDialog):
|
|||
self._versionschanged()
|
||||
return
|
||||
|
||||
subsets = self.data["widgets"]["subsets"]
|
||||
selected_subsets = subsets.selected_subsets(_merged=True, _other=False)
|
||||
selected_subsets = self._subsets_widget.selected_subsets(
|
||||
_merged=True, _other=False
|
||||
)
|
||||
|
||||
asset_models = {}
|
||||
asset_ids = []
|
||||
|
|
@ -340,7 +371,8 @@ class LoaderWindow(QtWidgets.QDialog):
|
|||
|
||||
self.clear_assets_underlines()
|
||||
|
||||
assets_widget = self.data["widgets"]["assets"]
|
||||
# TODO do not use inner attributes of asset widget
|
||||
assets_widget = self._assets_widget
|
||||
indexes = assets_widget.view.selectionModel().selectedRows()
|
||||
|
||||
for index in indexes:
|
||||
|
|
@ -357,7 +389,7 @@ class LoaderWindow(QtWidgets.QDialog):
|
|||
self._versionschanged()
|
||||
|
||||
def _versionschanged(self):
|
||||
subsets = self.data["widgets"]["subsets"]
|
||||
subsets = self._subsets_widget
|
||||
selection = subsets.view.selectionModel()
|
||||
|
||||
# Active must be in the selected rows otherwise we
|
||||
|
|
@ -389,23 +421,24 @@ class LoaderWindow(QtWidgets.QDialog):
|
|||
else:
|
||||
version_docs.append(item["version_document"])
|
||||
|
||||
self.data["widgets"]["version"].set_version(version_doc)
|
||||
self._version_info_widget.set_version(version_doc)
|
||||
|
||||
thumbnail_docs = version_docs
|
||||
assets_widget = self.data["widgets"]["assets"]
|
||||
asset_docs = assets_widget.get_selected_assets()
|
||||
asset_docs = self._assets_widget.get_selected_assets()
|
||||
if not thumbnail_docs:
|
||||
if len(asset_docs) > 0:
|
||||
thumbnail_docs = asset_docs
|
||||
|
||||
self.data["widgets"]["thumbnail"].set_thumbnail(thumbnail_docs)
|
||||
self._thumbnail_widget.set_thumbnail(thumbnail_docs)
|
||||
|
||||
representations = self.data["widgets"]["representations"]
|
||||
version_ids = [doc["_id"] for doc in version_docs or []]
|
||||
representations.set_version_ids(version_ids)
|
||||
if self._repres_widget is not None:
|
||||
version_ids = [doc["_id"] for doc in version_docs or []]
|
||||
self._repres_widget.set_version_ids(version_ids)
|
||||
|
||||
# representations.change_visibility("subset", len(rows) > 1)
|
||||
# representations.change_visibility("asset", len(asset_docs) > 1)
|
||||
# self._repres_widget.change_visibility("subset", len(rows) > 1)
|
||||
# self._repres_widget.change_visibility(
|
||||
# "asset", len(asset_docs) > 1
|
||||
# )
|
||||
|
||||
def _set_context(self, context, refresh=True):
|
||||
"""Set the selection in the interface using a context.
|
||||
|
|
@ -438,16 +471,15 @@ class LoaderWindow(QtWidgets.QDialog):
|
|||
# scheduled refresh and the silo tabs are not shown.
|
||||
self._refresh()
|
||||
|
||||
asset_widget = self.data["widgets"]["assets"]
|
||||
asset_widget.select_assets(asset)
|
||||
self._assets_widget.select_assets(asset)
|
||||
|
||||
def _on_message_timeout(self):
|
||||
self._message_label.setText("")
|
||||
|
||||
def echo(self, message):
|
||||
widget = self.data["label"]["message"]
|
||||
widget.setText(str(message))
|
||||
widget.show()
|
||||
self._message_label.setText(str(message))
|
||||
print(message)
|
||||
|
||||
lib.schedule(widget.hide, 5000, channel="message")
|
||||
self._message_timer.start()
|
||||
|
||||
def closeEvent(self, event):
|
||||
# Kill on holding SHIFT
|
||||
|
|
@ -475,7 +507,7 @@ class LoaderWindow(QtWidgets.QDialog):
|
|||
event.setAccepted(True) # Avoid interfering other widgets
|
||||
|
||||
def show_grouping_dialog(self):
|
||||
subsets = self.data["widgets"]["subsets"]
|
||||
subsets = self._subsets_widget
|
||||
if not subsets.is_groupable():
|
||||
self.echo("Grouping not enabled.")
|
||||
return
|
||||
|
|
@ -514,7 +546,8 @@ class SubsetGroupingDialog(QtWidgets.QDialog):
|
|||
|
||||
self.items = items
|
||||
self.groups_config = groups_config
|
||||
self.subsets = parent.data["widgets"]["subsets"]
|
||||
# TODO do not touch inner attributes
|
||||
self.subsets = parent._subsets_widget
|
||||
self.asset_ids = parent.data["state"]["assetIds"]
|
||||
|
||||
name = QtWidgets.QLineEdit()
|
||||
|
|
@ -633,7 +666,6 @@ def show(debug=False, parent=None, use_context=False):
|
|||
|
||||
with lib.application():
|
||||
window = LoaderWindow(parent)
|
||||
window.setStyleSheet(style.load_stylesheet())
|
||||
window.show()
|
||||
|
||||
if use_context:
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 5 KiB |
|
|
@ -37,12 +37,13 @@ class OverlayFrame(QtWidgets.QFrame):
|
|||
super(OverlayFrame, self).__init__(parent)
|
||||
|
||||
label_widget = QtWidgets.QLabel(label, self)
|
||||
label_widget.setAttribute(QtCore.Qt.WA_TranslucentBackground)
|
||||
|
||||
main_layout = QtWidgets.QVBoxLayout(self)
|
||||
main_layout.addWidget(label_widget, 1, QtCore.Qt.AlignCenter)
|
||||
|
||||
self.label_widget = label_widget
|
||||
|
||||
label_widget.setStyleSheet("background: transparent;")
|
||||
self.setStyleSheet((
|
||||
"background: rgba(0, 0, 0, 127);"
|
||||
"font-size: 60pt;"
|
||||
|
|
@ -159,36 +160,40 @@ class SubsetWidget(QtWidgets.QWidget):
|
|||
grouping=enable_grouping
|
||||
)
|
||||
proxy = SubsetFilterProxyModel()
|
||||
proxy.setSourceModel(model)
|
||||
proxy.setDynamicSortFilter(True)
|
||||
proxy.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive)
|
||||
|
||||
family_proxy = FamiliesFilterProxyModel()
|
||||
family_proxy.setSourceModel(proxy)
|
||||
|
||||
subset_filter = QtWidgets.QLineEdit()
|
||||
subset_filter = QtWidgets.QLineEdit(self)
|
||||
subset_filter.setPlaceholderText("Filter subsets..")
|
||||
|
||||
groupable = QtWidgets.QCheckBox("Enable Grouping")
|
||||
groupable.setChecked(enable_grouping)
|
||||
group_checkbox = QtWidgets.QCheckBox("Enable Grouping", self)
|
||||
group_checkbox.setChecked(enable_grouping)
|
||||
|
||||
top_bar_layout = QtWidgets.QHBoxLayout()
|
||||
top_bar_layout.addWidget(subset_filter)
|
||||
top_bar_layout.addWidget(groupable)
|
||||
top_bar_layout.addWidget(group_checkbox)
|
||||
|
||||
view = TreeViewSpinner()
|
||||
view = TreeViewSpinner(self)
|
||||
view.setModel(family_proxy)
|
||||
view.setObjectName("SubsetView")
|
||||
view.setIndentation(20)
|
||||
view.setStyleSheet("""
|
||||
QTreeView::item{
|
||||
padding: 5px 1px;
|
||||
border: 0px;
|
||||
}
|
||||
""")
|
||||
view.setAllColumnsShowFocus(True)
|
||||
view.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
|
||||
view.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
|
||||
view.setSortingEnabled(True)
|
||||
view.sortByColumn(1, QtCore.Qt.AscendingOrder)
|
||||
view.setAlternatingRowColors(True)
|
||||
|
||||
# Set view delegates
|
||||
version_delegate = VersionDelegate(self.dbcon)
|
||||
version_delegate = VersionDelegate(self.dbcon, view)
|
||||
column = model.Columns.index("version")
|
||||
view.setItemDelegateForColumn(column, version_delegate)
|
||||
|
||||
time_delegate = PrettyTimeDelegate()
|
||||
time_delegate = PrettyTimeDelegate(view)
|
||||
column = model.Columns.index("time")
|
||||
view.setItemDelegateForColumn(column, time_delegate)
|
||||
|
||||
|
|
@ -197,54 +202,39 @@ class SubsetWidget(QtWidgets.QWidget):
|
|||
layout.addLayout(top_bar_layout)
|
||||
layout.addWidget(view)
|
||||
|
||||
view.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
|
||||
view.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
|
||||
view.setSortingEnabled(True)
|
||||
view.sortByColumn(1, QtCore.Qt.AscendingOrder)
|
||||
view.setAlternatingRowColors(True)
|
||||
|
||||
self.data = {
|
||||
"delegates": {
|
||||
"version": version_delegate,
|
||||
"time": time_delegate
|
||||
},
|
||||
"state": {
|
||||
"groupable": groupable
|
||||
}
|
||||
}
|
||||
|
||||
self.proxy = proxy
|
||||
self.model = model
|
||||
self.view = view
|
||||
self.filter = subset_filter
|
||||
self.family_proxy = family_proxy
|
||||
|
||||
# settings and connections
|
||||
self.proxy.setSourceModel(self.model)
|
||||
self.proxy.setDynamicSortFilter(True)
|
||||
self.proxy.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive)
|
||||
|
||||
self.view.setModel(self.family_proxy)
|
||||
self.view.customContextMenuRequested.connect(self.on_context_menu)
|
||||
|
||||
for column_name, width in self.default_widths:
|
||||
idx = model.Columns.index(column_name)
|
||||
view.setColumnWidth(idx, width)
|
||||
|
||||
self.model = model
|
||||
self.view = view
|
||||
|
||||
actual_project = dbcon.Session["AVALON_PROJECT"]
|
||||
self.on_project_change(actual_project)
|
||||
|
||||
view.customContextMenuRequested.connect(self.on_context_menu)
|
||||
|
||||
selection = view.selectionModel()
|
||||
selection.selectionChanged.connect(self.active_changed)
|
||||
|
||||
version_delegate.version_changed.connect(self.version_changed)
|
||||
|
||||
groupable.stateChanged.connect(self.set_grouping)
|
||||
group_checkbox.stateChanged.connect(self.set_grouping)
|
||||
|
||||
self.filter.textChanged.connect(self.proxy.setFilterRegExp)
|
||||
self.filter.textChanged.connect(self.view.expandAll)
|
||||
subset_filter.textChanged.connect(proxy.setFilterRegExp)
|
||||
subset_filter.textChanged.connect(view.expandAll)
|
||||
model.refreshed.connect(self.refreshed)
|
||||
|
||||
self.proxy = proxy
|
||||
self.family_proxy = family_proxy
|
||||
|
||||
self._subset_filter = subset_filter
|
||||
self._group_checkbox = group_checkbox
|
||||
|
||||
self._version_delegate = version_delegate
|
||||
self._time_delegate = time_delegate
|
||||
|
||||
self.model.refresh()
|
||||
|
||||
def get_subsets_families(self):
|
||||
|
|
@ -254,7 +244,7 @@ class SubsetWidget(QtWidgets.QWidget):
|
|||
self.family_proxy.setFamiliesFilter(families)
|
||||
|
||||
def is_groupable(self):
|
||||
return self.data["state"]["groupable"].checkState()
|
||||
return self._group_checkbox.isChecked()
|
||||
|
||||
def set_grouping(self, state):
|
||||
with tools_lib.preserve_selection(tree_view=self.view,
|
||||
|
|
@ -755,6 +745,7 @@ class ThumbnailWidget(QtWidgets.QLabel):
|
|||
"default_thumbnail.png"
|
||||
)
|
||||
self.default_pix = QtGui.QPixmap(default_pix_path)
|
||||
self.set_pixmap()
|
||||
|
||||
def height(self):
|
||||
width = self.width()
|
||||
|
|
@ -1131,7 +1122,8 @@ class RepresentationWidget(QtWidgets.QWidget):
|
|||
|
||||
label = QtWidgets.QLabel("Representations", self)
|
||||
|
||||
tree_view = DeselectableTreeView()
|
||||
tree_view = DeselectableTreeView(parent=self)
|
||||
tree_view.setObjectName("RepresentationView")
|
||||
tree_view.setModel(proxy_model)
|
||||
tree_view.setAllColumnsShowFocus(True)
|
||||
tree_view.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
|
||||
|
|
@ -1141,12 +1133,6 @@ class RepresentationWidget(QtWidgets.QWidget):
|
|||
tree_view.sortByColumn(1, QtCore.Qt.AscendingOrder)
|
||||
tree_view.setAlternatingRowColors(True)
|
||||
tree_view.setIndentation(20)
|
||||
tree_view.setStyleSheet("""
|
||||
QTreeView::item{
|
||||
padding: 5px 1px;
|
||||
border: 0px;
|
||||
}
|
||||
""")
|
||||
tree_view.collapseAll()
|
||||
|
||||
for column_name, width in self.default_widths:
|
||||
|
|
|
|||
|
|
@ -2,20 +2,27 @@ import sys
|
|||
import time
|
||||
import logging
|
||||
|
||||
from Qt import QtWidgets, QtCore
|
||||
|
||||
from openpype.hosts.maya.api.lib import assign_look_by_version
|
||||
|
||||
from avalon import style, io
|
||||
from avalon.tools import lib
|
||||
from avalon.vendor.Qt import QtWidgets, QtCore
|
||||
|
||||
from maya import cmds
|
||||
# old api for MFileIO
|
||||
import maya.OpenMaya
|
||||
import maya.api.OpenMaya as om
|
||||
|
||||
from . import widgets
|
||||
from . import commands
|
||||
from . vray_proxies import vrayproxy_assign_look
|
||||
from .widgets import (
|
||||
AssetOutliner,
|
||||
LookOutliner
|
||||
)
|
||||
from .commands import (
|
||||
get_workfile,
|
||||
remove_unused_looks
|
||||
)
|
||||
from .vray_proxies import vrayproxy_assign_look
|
||||
|
||||
|
||||
module = sys.modules[__name__]
|
||||
|
|
@ -32,7 +39,7 @@ class App(QtWidgets.QWidget):
|
|||
# Store callback references
|
||||
self._callbacks = []
|
||||
|
||||
filename = commands.get_workfile()
|
||||
filename = get_workfile()
|
||||
|
||||
self.setObjectName("lookManager")
|
||||
self.setWindowTitle("Look Manager 1.3.0 - [{}]".format(filename))
|
||||
|
|
@ -57,13 +64,13 @@ class App(QtWidgets.QWidget):
|
|||
"""Build the UI"""
|
||||
|
||||
# Assets (left)
|
||||
asset_outliner = widgets.AssetOutliner()
|
||||
asset_outliner = AssetOutliner()
|
||||
|
||||
# Looks (right)
|
||||
looks_widget = QtWidgets.QWidget()
|
||||
looks_layout = QtWidgets.QVBoxLayout(looks_widget)
|
||||
|
||||
look_outliner = widgets.LookOutliner() # Database look overview
|
||||
look_outliner = LookOutliner() # Database look overview
|
||||
|
||||
assign_selected = QtWidgets.QCheckBox("Assign to selected only")
|
||||
assign_selected.setToolTip("Whether to assign only to selected nodes "
|
||||
|
|
@ -124,7 +131,7 @@ class App(QtWidgets.QWidget):
|
|||
lambda: self.echo("Loaded assets.."))
|
||||
|
||||
self.look_outliner.menu_apply_action.connect(self.on_process_selected)
|
||||
self.remove_unused.clicked.connect(commands.remove_unused_looks)
|
||||
self.remove_unused.clicked.connect(remove_unused_looks)
|
||||
|
||||
# Maya renderlayer switch callback
|
||||
callback = om.MEventMessage.addEventCallback(
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ from openpype.hosts.maya.api import lib
|
|||
from avalon import io, api
|
||||
|
||||
|
||||
import vray_proxies
|
||||
from .vray_proxies import get_alembic_ids_cache
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
|
@ -146,7 +146,7 @@ def create_items_from_nodes(nodes):
|
|||
vray_proxy_nodes = cmds.ls(nodes, type="VRayProxy")
|
||||
for vp in vray_proxy_nodes:
|
||||
path = cmds.getAttr("{}.fileName".format(vp))
|
||||
ids = vray_proxies.get_alembic_ids_cache(path)
|
||||
ids = get_alembic_ids_cache(path)
|
||||
parent_id = {}
|
||||
for k, _ in ids.items():
|
||||
pid = k.split(":")[0]
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
from collections import defaultdict
|
||||
from avalon.tools import models
|
||||
|
||||
from avalon.vendor.Qt import QtCore
|
||||
from Qt import QtCore
|
||||
|
||||
from avalon.tools import models
|
||||
from avalon.vendor import qtawesome
|
||||
from avalon.style import colors
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
from avalon.vendor.Qt import QtWidgets, QtCore
|
||||
from Qt import QtWidgets, QtCore
|
||||
|
||||
|
||||
DEFAULT_COLOR = "#fb9c15"
|
||||
|
|
|
|||
|
|
@ -1,13 +1,16 @@
|
|||
import logging
|
||||
from collections import defaultdict
|
||||
|
||||
from avalon.vendor.Qt import QtWidgets, QtCore
|
||||
from Qt import QtWidgets, QtCore
|
||||
|
||||
# TODO: expose this better in avalon core
|
||||
from avalon.tools import lib
|
||||
from avalon.tools.models import TreeModel
|
||||
|
||||
from . import models
|
||||
from .models import (
|
||||
AssetModel,
|
||||
LookModel
|
||||
)
|
||||
from . import commands
|
||||
from . import views
|
||||
|
||||
|
|
@ -30,7 +33,7 @@ class AssetOutliner(QtWidgets.QWidget):
|
|||
title.setAlignment(QtCore.Qt.AlignCenter)
|
||||
title.setStyleSheet("font-weight: bold; font-size: 12px")
|
||||
|
||||
model = models.AssetModel()
|
||||
model = AssetModel()
|
||||
view = views.View()
|
||||
view.setModel(model)
|
||||
view.customContextMenuRequested.connect(self.right_mouse_menu)
|
||||
|
|
@ -201,7 +204,7 @@ class LookOutliner(QtWidgets.QWidget):
|
|||
title.setStyleSheet("font-weight: bold; font-size: 12px")
|
||||
title.setAlignment(QtCore.Qt.AlignCenter)
|
||||
|
||||
model = models.LookModel()
|
||||
model = LookModel()
|
||||
|
||||
# Proxy for dynamic sorting
|
||||
proxy = QtCore.QSortFilterProxyModel()
|
||||
|
|
@ -257,5 +260,3 @@ class LookOutliner(QtWidgets.QWidget):
|
|||
menu.addAction(apply_action)
|
||||
|
||||
menu.exec_(globalpos)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,65 @@
|
|||
from Qt import QtWidgets
|
||||
from openpype.tools.experimental_tools import (
|
||||
ExperimentalTools,
|
||||
LOCAL_EXPERIMENTAL_KEY
|
||||
)
|
||||
|
||||
|
||||
__all__ = (
|
||||
"LocalExperimentalToolsWidgets",
|
||||
"LOCAL_EXPERIMENTAL_KEY"
|
||||
)
|
||||
|
||||
|
||||
class LocalExperimentalToolsWidgets(QtWidgets.QWidget):
|
||||
def __init__(self, parent):
|
||||
super(LocalExperimentalToolsWidgets, self).__init__(parent)
|
||||
|
||||
self._loading_local_settings = False
|
||||
|
||||
layout = QtWidgets.QFormLayout(self)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
# Label that says there are no experimental tools available
|
||||
empty_label = QtWidgets.QLabel(self)
|
||||
empty_label.setText(
|
||||
"There are no experimental tools available..."
|
||||
)
|
||||
|
||||
layout.addRow(empty_label)
|
||||
|
||||
experimental_defs = ExperimentalTools(filter_hosts=False)
|
||||
checkboxes_by_identifier = {}
|
||||
for tool in experimental_defs.tools:
|
||||
checkbox = QtWidgets.QCheckBox(self)
|
||||
label_widget = QtWidgets.QLabel(tool.label, self)
|
||||
checkbox.setToolTip(tool.tooltip)
|
||||
label_widget.setToolTip(tool.tooltip)
|
||||
layout.addRow(label_widget, checkbox)
|
||||
|
||||
checkboxes_by_identifier[tool.identifier] = checkbox
|
||||
|
||||
empty_label.setVisible(len(checkboxes_by_identifier) == 0)
|
||||
|
||||
self._empty_label = empty_label
|
||||
self._checkboxes_by_identifier = checkboxes_by_identifier
|
||||
self._experimental_defs = experimental_defs
|
||||
|
||||
def update_local_settings(self, value):
|
||||
self._loading_local_settings = True
|
||||
value = value or {}
|
||||
|
||||
for identifier, checkbox in self._checkboxes_by_identifier.items():
|
||||
checked = value.get(identifier, False)
|
||||
checkbox.setChecked(checked)
|
||||
|
||||
self._loading_local_settings = False
|
||||
|
||||
def settings_value(self):
|
||||
# Add changed
|
||||
# If these have changed then
|
||||
output = {}
|
||||
for identifier, checkbox in self._checkboxes_by_identifier.items():
|
||||
if checkbox.isChecked():
|
||||
output[identifier] = True
|
||||
return output
|
||||
|
|
@ -20,6 +20,10 @@ from .widgets import (
|
|||
)
|
||||
from .mongo_widget import OpenPypeMongoWidget
|
||||
from .general_widget import LocalGeneralWidgets
|
||||
from .experimental_widget import (
|
||||
LocalExperimentalToolsWidgets,
|
||||
LOCAL_EXPERIMENTAL_KEY
|
||||
)
|
||||
from .apps_widget import LocalApplicationsWidgets
|
||||
from .projects_widget import ProjectSettingsWidget
|
||||
|
||||
|
|
@ -44,11 +48,13 @@ class LocalSettingsWidget(QtWidgets.QWidget):
|
|||
|
||||
self.pype_mongo_widget = None
|
||||
self.general_widget = None
|
||||
self.experimental_widget = None
|
||||
self.apps_widget = None
|
||||
self.projects_widget = None
|
||||
|
||||
self._create_pype_mongo_ui()
|
||||
self._create_general_ui()
|
||||
self._create_experimental_ui()
|
||||
self._create_app_ui()
|
||||
self._create_project_ui()
|
||||
|
||||
|
|
@ -85,6 +91,26 @@ class LocalSettingsWidget(QtWidgets.QWidget):
|
|||
|
||||
self.general_widget = general_widget
|
||||
|
||||
def _create_experimental_ui(self):
|
||||
# General
|
||||
experimental_expand_widget = ExpandingWidget(
|
||||
"Experimental tools", self
|
||||
)
|
||||
|
||||
experimental_content = QtWidgets.QWidget(self)
|
||||
experimental_layout = QtWidgets.QVBoxLayout(experimental_content)
|
||||
experimental_layout.setContentsMargins(CHILD_OFFSET, 5, 0, 0)
|
||||
experimental_expand_widget.set_content_widget(experimental_content)
|
||||
|
||||
experimental_widget = LocalExperimentalToolsWidgets(
|
||||
experimental_content
|
||||
)
|
||||
experimental_layout.addWidget(experimental_widget)
|
||||
|
||||
self.main_layout.addWidget(experimental_expand_widget)
|
||||
|
||||
self.experimental_widget = experimental_widget
|
||||
|
||||
def _create_app_ui(self):
|
||||
# Applications
|
||||
app_expand_widget = ExpandingWidget("Applications", self)
|
||||
|
|
@ -135,6 +161,9 @@ class LocalSettingsWidget(QtWidgets.QWidget):
|
|||
self.projects_widget.update_local_settings(
|
||||
value.get(LOCAL_PROJECTS_KEY)
|
||||
)
|
||||
self.experimental_widget.update_local_settings(
|
||||
value.get(LOCAL_EXPERIMENTAL_KEY)
|
||||
)
|
||||
|
||||
def settings_value(self):
|
||||
output = {}
|
||||
|
|
@ -149,6 +178,10 @@ class LocalSettingsWidget(QtWidgets.QWidget):
|
|||
projects_value = self.projects_widget.settings_value()
|
||||
if projects_value:
|
||||
output[LOCAL_PROJECTS_KEY] = projects_value
|
||||
|
||||
experimental_value = self.experimental_widget.settings_value()
|
||||
if experimental_value:
|
||||
output[LOCAL_EXPERIMENTAL_KEY] = experimental_value
|
||||
return output
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -142,18 +142,23 @@ class ConsoleTrayApp:
|
|||
self.tray_reconnect = False
|
||||
ConsoleTrayApp.webserver_client.close()
|
||||
|
||||
def _send_text(self, new_text):
|
||||
def _send_text_queue(self):
|
||||
"""Sends lines and purges queue"""
|
||||
lines = tuple(self.new_text)
|
||||
self.new_text.clear()
|
||||
|
||||
if lines:
|
||||
self._send_lines(lines)
|
||||
|
||||
def _send_lines(self, lines):
|
||||
""" Send console content. """
|
||||
if not ConsoleTrayApp.webserver_client:
|
||||
return
|
||||
|
||||
if isinstance(new_text, str):
|
||||
new_text = collections.deque(new_text.split("\n"))
|
||||
|
||||
payload = {
|
||||
"host": self.host_id,
|
||||
"action": host_console_listener.MsgAction.ADD,
|
||||
"text": "\n".join(new_text)
|
||||
"text": "\n".join(lines)
|
||||
}
|
||||
|
||||
self._send(payload)
|
||||
|
|
@ -174,14 +179,7 @@ class ConsoleTrayApp:
|
|||
if self.tray_reconnect:
|
||||
self._connect() # reconnect
|
||||
|
||||
if ConsoleTrayApp.webserver_client and self.new_text:
|
||||
self._send_text(self.new_text)
|
||||
self.new_text = collections.deque()
|
||||
|
||||
if self.new_text: # no webserver_client, text keeps stashing
|
||||
start = max(len(self.new_text) - self.MAX_LINES, 0)
|
||||
self.new_text = itertools.islice(self.new_text,
|
||||
start, self.MAX_LINES)
|
||||
self._send_text_queue()
|
||||
|
||||
if not self.initialized:
|
||||
if self.initializing:
|
||||
|
|
@ -191,7 +189,7 @@ class ConsoleTrayApp:
|
|||
elif not host_connected:
|
||||
text = "{} process is not alive. Exiting".format(self.host)
|
||||
print(text)
|
||||
self._send_text([text])
|
||||
self._send_lines([text])
|
||||
ConsoleTrayApp.websocket_server.stop()
|
||||
sys.exit(1)
|
||||
elif host_connected:
|
||||
|
|
@ -205,14 +203,15 @@ class ConsoleTrayApp:
|
|||
self.initializing = True
|
||||
|
||||
self.launch_method(*self.subprocess_args)
|
||||
elif ConsoleTrayApp.process.poll() is not None:
|
||||
self.exit()
|
||||
elif ConsoleTrayApp.callback_queue:
|
||||
elif ConsoleTrayApp.callback_queue and \
|
||||
not ConsoleTrayApp.callback_queue.empty():
|
||||
try:
|
||||
callback = ConsoleTrayApp.callback_queue.get(block=False)
|
||||
callback()
|
||||
except queue.Empty:
|
||||
pass
|
||||
elif ConsoleTrayApp.process.poll() is not None:
|
||||
self.exit()
|
||||
|
||||
@classmethod
|
||||
def execute_in_main_thread(cls, func_to_call_from_main_thread):
|
||||
|
|
@ -232,8 +231,9 @@ class ConsoleTrayApp:
|
|||
self._close()
|
||||
if ConsoleTrayApp.websocket_server:
|
||||
ConsoleTrayApp.websocket_server.stop()
|
||||
ConsoleTrayApp.process.kill()
|
||||
ConsoleTrayApp.process.wait()
|
||||
if ConsoleTrayApp.process:
|
||||
ConsoleTrayApp.process.kill()
|
||||
ConsoleTrayApp.process.wait()
|
||||
if self.timer:
|
||||
self.timer.stop()
|
||||
QtCore.QCoreApplication.exit()
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import Qt
|
|||
from Qt import QtWidgets, QtGui, QtCore
|
||||
|
||||
from avalon.lib import HeroVersionType
|
||||
from openpype.style import get_objected_colors
|
||||
from .models import (
|
||||
AssetModel,
|
||||
TreeModel
|
||||
|
|
@ -24,6 +25,19 @@ log = logging.getLogger(__name__)
|
|||
class AssetDelegate(QtWidgets.QItemDelegate):
|
||||
bar_height = 3
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(AssetDelegate, self).__init__(*args, **kwargs)
|
||||
asset_view_colors = get_objected_colors()["loader"]["asset-view"]
|
||||
self._selected_color = (
|
||||
asset_view_colors["selected"].get_qcolor()
|
||||
)
|
||||
self._hover_color = (
|
||||
asset_view_colors["hover"].get_qcolor()
|
||||
)
|
||||
self._selected_hover_color = (
|
||||
asset_view_colors["selected-hover"].get_qcolor()
|
||||
)
|
||||
|
||||
def sizeHint(self, option, index):
|
||||
result = super(AssetDelegate, self).sizeHint(option, index)
|
||||
height = result.height()
|
||||
|
|
@ -66,17 +80,20 @@ class AssetDelegate(QtWidgets.QItemDelegate):
|
|||
counter += 1
|
||||
|
||||
# Background
|
||||
bg_color = QtGui.QColor(60, 60, 60)
|
||||
if option.state & QtWidgets.QStyle.State_Selected:
|
||||
if len(subset_colors) == 0:
|
||||
item_rect.setTop(item_rect.top() + (self.bar_height / 2))
|
||||
|
||||
if option.state & QtWidgets.QStyle.State_MouseOver:
|
||||
bg_color.setRgb(70, 70, 70)
|
||||
bg_color = self._selected_hover_color
|
||||
else:
|
||||
bg_color = self._selected_color
|
||||
else:
|
||||
item_rect.setTop(item_rect.top() + (self.bar_height / 2))
|
||||
if option.state & QtWidgets.QStyle.State_MouseOver:
|
||||
bg_color.setAlpha(100)
|
||||
bg_color = self._hover_color
|
||||
else:
|
||||
bg_color = QtGui.QColor()
|
||||
bg_color.setAlpha(0)
|
||||
|
||||
# When not needed to do a rounded corners (easier and without
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ class HostToolsHelper:
|
|||
self._scene_inventory_tool = None
|
||||
self._library_loader_tool = None
|
||||
self._look_assigner_tool = None
|
||||
self._experimental_tools_dialog = None
|
||||
|
||||
@property
|
||||
def log(self):
|
||||
|
|
@ -40,7 +41,6 @@ class HostToolsHelper:
|
|||
def get_workfiles_tool(self, parent):
|
||||
"""Create, cache and return workfiles tool window."""
|
||||
if self._workfiles_tool is None:
|
||||
from avalon import style
|
||||
from openpype.tools.workfiles.app import (
|
||||
Window, validate_host_requirements
|
||||
)
|
||||
|
|
@ -49,13 +49,14 @@ class HostToolsHelper:
|
|||
validate_host_requirements(host)
|
||||
|
||||
workfiles_window = Window(parent=parent)
|
||||
workfiles_window.setStyleSheet(style.load_stylesheet())
|
||||
self._workfiles_tool = workfiles_window
|
||||
|
||||
return self._workfiles_tool
|
||||
|
||||
def show_workfiles(self, parent=None, use_context=None, save=None):
|
||||
"""Workfiles tool for changing context and saving workfiles."""
|
||||
from avalon import style
|
||||
|
||||
if use_context is None:
|
||||
use_context = True
|
||||
|
||||
|
|
@ -79,24 +80,28 @@ class HostToolsHelper:
|
|||
# Pull window to the front.
|
||||
workfiles_tool.raise_()
|
||||
workfiles_tool.activateWindow()
|
||||
workfiles_tool.setStyleSheet(style.load_stylesheet())
|
||||
|
||||
def get_loader_tool(self, parent):
|
||||
"""Create, cache and return loader tool window."""
|
||||
if self._loader_tool is None:
|
||||
from avalon import style
|
||||
from openpype.tools.loader import LoaderWindow
|
||||
|
||||
loader_window = LoaderWindow(parent=parent or self._parent)
|
||||
loader_window.setStyleSheet(style.load_stylesheet())
|
||||
self._loader_tool = loader_window
|
||||
|
||||
return self._loader_tool
|
||||
|
||||
def show_loader(self, parent=None, use_context=None):
|
||||
"""Loader tool for loading representations."""
|
||||
loader_tool = self.get_loader_tool(parent)
|
||||
|
||||
loader_tool.show()
|
||||
loader_tool.raise_()
|
||||
loader_tool.activateWindow()
|
||||
|
||||
if use_context is None:
|
||||
use_context = False
|
||||
loader_tool = self.get_loader_tool(parent)
|
||||
|
||||
if use_context:
|
||||
context = {"asset": avalon.api.Session["AVALON_ASSET"]}
|
||||
|
|
@ -104,29 +109,26 @@ class HostToolsHelper:
|
|||
else:
|
||||
loader_tool.refresh()
|
||||
|
||||
loader_tool.show()
|
||||
loader_tool.raise_()
|
||||
loader_tool.activateWindow()
|
||||
loader_tool.refresh()
|
||||
|
||||
def get_creator_tool(self, parent):
|
||||
"""Create, cache and return creator tool window."""
|
||||
if self._creator_tool is None:
|
||||
from avalon import style
|
||||
from avalon.tools.creator.app import Window
|
||||
|
||||
creator_window = Window(parent=parent or self._parent)
|
||||
creator_window.setStyleSheet(style.load_stylesheet())
|
||||
self._creator_tool = creator_window
|
||||
|
||||
return self._creator_tool
|
||||
|
||||
def show_creator(self, parent=None):
|
||||
"""Show tool to create new instantes for publishing."""
|
||||
from avalon import style
|
||||
|
||||
creator_tool = self.get_creator_tool(parent)
|
||||
creator_tool.refresh()
|
||||
creator_tool.show()
|
||||
|
||||
creator_tool.setStyleSheet(style.load_stylesheet())
|
||||
|
||||
# Pull window to the front.
|
||||
creator_tool.raise_()
|
||||
creator_tool.activateWindow()
|
||||
|
|
@ -134,20 +136,22 @@ class HostToolsHelper:
|
|||
def get_subset_manager_tool(self, parent):
|
||||
"""Create, cache and return subset manager tool window."""
|
||||
if self._subset_manager_tool is None:
|
||||
from avalon import style
|
||||
from avalon.tools.subsetmanager import Window
|
||||
|
||||
subset_manager_window = Window(parent=parent or self._parent)
|
||||
subset_manager_window.setStyleSheet(style.load_stylesheet())
|
||||
self._subset_manager_tool = subset_manager_window
|
||||
|
||||
return self._subset_manager_tool
|
||||
|
||||
def show_subset_manager(self, parent=None):
|
||||
"""Show tool display/remove existing created instances."""
|
||||
from avalon import style
|
||||
|
||||
subset_manager_tool = self.get_subset_manager_tool(parent)
|
||||
subset_manager_tool.show()
|
||||
|
||||
subset_manager_tool.setStyleSheet(style.load_stylesheet())
|
||||
|
||||
# Pull window to the front.
|
||||
subset_manager_tool.raise_()
|
||||
subset_manager_tool.activateWindow()
|
||||
|
|
@ -155,20 +159,21 @@ class HostToolsHelper:
|
|||
def get_scene_inventory_tool(self, parent):
|
||||
"""Create, cache and return scene inventory tool window."""
|
||||
if self._scene_inventory_tool is None:
|
||||
from avalon import style
|
||||
from avalon.tools.sceneinventory.app import Window
|
||||
|
||||
scene_inventory_window = Window(parent=parent or self._parent)
|
||||
scene_inventory_window.setStyleSheet(style.load_stylesheet())
|
||||
self._scene_inventory_tool = scene_inventory_window
|
||||
|
||||
return self._scene_inventory_tool
|
||||
|
||||
def show_scene_inventory(self, parent=None):
|
||||
"""Show tool maintain loaded containers."""
|
||||
from avalon import style
|
||||
|
||||
scene_inventory_tool = self.get_scene_inventory_tool(parent)
|
||||
scene_inventory_tool.show()
|
||||
scene_inventory_tool.refresh()
|
||||
scene_inventory_tool.setStyleSheet(style.load_stylesheet())
|
||||
|
||||
# Pull window to the front.
|
||||
scene_inventory_tool.raise_()
|
||||
|
|
@ -177,13 +182,11 @@ class HostToolsHelper:
|
|||
def get_library_loader_tool(self, parent):
|
||||
"""Create, cache and return library loader tool window."""
|
||||
if self._library_loader_tool is None:
|
||||
from avalon import style
|
||||
from openpype.tools.libraryloader import LibraryLoaderWindow
|
||||
|
||||
library_window = LibraryLoaderWindow(
|
||||
parent=parent or self._parent
|
||||
)
|
||||
library_window.setStyleSheet(style.load_stylesheet())
|
||||
self._library_loader_tool = library_window
|
||||
|
||||
return self._library_loader_tool
|
||||
|
|
@ -205,18 +208,46 @@ class HostToolsHelper:
|
|||
def get_look_assigner_tool(self, parent):
|
||||
"""Create, cache and return look assigner tool window."""
|
||||
if self._look_assigner_tool is None:
|
||||
from avalon import style
|
||||
import mayalookassigner
|
||||
|
||||
mayalookassigner_window = mayalookassigner.App(parent)
|
||||
mayalookassigner_window.setStyleSheet(style.load_stylesheet())
|
||||
self._look_assigner_tool = mayalookassigner_window
|
||||
return self._look_assigner_tool
|
||||
|
||||
def show_look_assigner(self, parent=None):
|
||||
"""Look manager is Maya specific tool for look management."""
|
||||
from avalon import style
|
||||
|
||||
look_assigner_tool = self.get_look_assigner_tool(parent)
|
||||
look_assigner_tool.show()
|
||||
look_assigner_tool.setStyleSheet(style.load_stylesheet())
|
||||
|
||||
def get_experimental_tools_dialog(self, parent=None):
|
||||
"""Dialog of experimental tools.
|
||||
|
||||
For some hosts it is not easy to modify menu of tools. For
|
||||
those cases was addded experimental tools dialog which is Qt based
|
||||
and can dynamically filled by experimental tools so
|
||||
host need only single "Experimental tools" button to see them.
|
||||
|
||||
Dialog can be also empty with a message that there are not available
|
||||
experimental tools.
|
||||
"""
|
||||
if self._experimental_tools_dialog is None:
|
||||
from openpype.tools.experimental_tools import (
|
||||
ExperimentalToolsDialog
|
||||
)
|
||||
|
||||
self._experimental_tools_dialog = ExperimentalToolsDialog(parent)
|
||||
return self._experimental_tools_dialog
|
||||
|
||||
def show_experimental_tools_dialog(self, parent=None):
|
||||
"""Show dialog with experimental tools."""
|
||||
dialog = self.get_experimental_tools_dialog(parent)
|
||||
|
||||
dialog.show()
|
||||
dialog.raise_()
|
||||
dialog.activateWindow()
|
||||
|
||||
def get_tool_by_name(self, tool_name, parent=None, *args, **kwargs):
|
||||
"""Show tool by it's name.
|
||||
|
|
@ -247,6 +278,9 @@ class HostToolsHelper:
|
|||
elif tool_name == "publish":
|
||||
self.log.info("Can't return publish tool window.")
|
||||
|
||||
elif tool_name == "experimental_tools":
|
||||
return self.get_experimental_tools_dialog(parent, *args, **kwargs)
|
||||
|
||||
else:
|
||||
self.log.warning(
|
||||
"Can't show unknown tool name: \"{}\"".format(tool_name)
|
||||
|
|
@ -281,6 +315,9 @@ class HostToolsHelper:
|
|||
elif tool_name == "publish":
|
||||
self.show_publish(parent, *args, **kwargs)
|
||||
|
||||
elif tool_name == "experimental_tools":
|
||||
self.show_experimental_tools_dialog(parent, *args, **kwargs)
|
||||
|
||||
else:
|
||||
self.log.warning(
|
||||
"Can't show unknown tool name: \"{}\"".format(tool_name)
|
||||
|
|
@ -355,3 +392,7 @@ def show_look_assigner(parent=None):
|
|||
|
||||
def show_publish(parent=None):
|
||||
_SingletonPoint.show_tool_by_name("publish", parent)
|
||||
|
||||
|
||||
def show_experimental_tools_dialog(parent=None):
|
||||
_SingletonPoint.show_tool_by_name("experimental_tools", parent)
|
||||
|
|
|
|||
|
|
@ -68,8 +68,8 @@ class AssetsView(TreeViewSpinner, DeselectableTreeView):
|
|||
This implements a context menu.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super(AssetsView, self).__init__()
|
||||
def __init__(self, parent=None):
|
||||
super(AssetsView, self).__init__(parent)
|
||||
self.setIndentation(15)
|
||||
self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
|
||||
self.setHeaderHidden(True)
|
||||
|
|
|
|||
|
|
@ -35,28 +35,19 @@ class AssetWidget(QtWidgets.QWidget):
|
|||
|
||||
self.dbcon = dbcon
|
||||
|
||||
self.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
layout = QtWidgets.QVBoxLayout(self)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.setSpacing(4)
|
||||
|
||||
# Tree View
|
||||
model = AssetModel(dbcon=self.dbcon, parent=self)
|
||||
proxy = RecursiveSortFilterProxyModel()
|
||||
proxy.setSourceModel(model)
|
||||
proxy.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive)
|
||||
|
||||
view = AssetsView()
|
||||
view = AssetsView(self)
|
||||
view.setModel(proxy)
|
||||
if multiselection:
|
||||
asset_delegate = AssetDelegate()
|
||||
view.setSelectionMode(view.ExtendedSelection)
|
||||
view.setItemDelegate(asset_delegate)
|
||||
|
||||
# Header
|
||||
header = QtWidgets.QHBoxLayout()
|
||||
|
||||
icon = qtawesome.icon("fa.arrow-down", color=style.colors.light)
|
||||
set_current_asset_btn = QtWidgets.QPushButton(icon, "")
|
||||
set_current_asset_btn.setToolTip("Go to Asset from current Session")
|
||||
|
|
@ -64,22 +55,28 @@ class AssetWidget(QtWidgets.QWidget):
|
|||
set_current_asset_btn.setVisible(False)
|
||||
|
||||
icon = qtawesome.icon("fa.refresh", color=style.colors.light)
|
||||
refresh = QtWidgets.QPushButton(icon, "")
|
||||
refresh = QtWidgets.QPushButton(icon, "", parent=self)
|
||||
refresh.setToolTip("Refresh items")
|
||||
|
||||
filter = QtWidgets.QLineEdit()
|
||||
filter.textChanged.connect(proxy.setFilterFixedString)
|
||||
filter.setPlaceholderText("Filter assets..")
|
||||
filter_input = QtWidgets.QLineEdit(self)
|
||||
filter_input.setPlaceholderText("Filter assets..")
|
||||
|
||||
header.addWidget(filter)
|
||||
header.addWidget(set_current_asset_btn)
|
||||
header.addWidget(refresh)
|
||||
# Header
|
||||
header_layout = QtWidgets.QHBoxLayout()
|
||||
header_layout.addWidget(filter_input)
|
||||
header_layout.addWidget(set_current_asset_btn)
|
||||
header_layout.addWidget(refresh)
|
||||
|
||||
# Layout
|
||||
layout.addLayout(header)
|
||||
layout = QtWidgets.QVBoxLayout(self)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.setSpacing(4)
|
||||
layout.addLayout(header_layout)
|
||||
layout.addWidget(view)
|
||||
|
||||
# Signals/Slots
|
||||
filter_input.textChanged.connect(proxy.setFilterFixedString)
|
||||
|
||||
selection = view.selectionModel()
|
||||
selection.selectionChanged.connect(self.selection_changed)
|
||||
selection.currentChanged.connect(self.current_changed)
|
||||
|
|
@ -313,7 +310,6 @@ class OptionalMenu(QtWidgets.QMenu):
|
|||
actions that were instances of `QtWidgets.QWidgetAction`.
|
||||
|
||||
"""
|
||||
|
||||
def mouseReleaseEvent(self, event):
|
||||
"""Emit option clicked signal if mouse released on it"""
|
||||
active = self.actionAt(event.pos())
|
||||
|
|
@ -352,6 +348,7 @@ class OptionalAction(QtWidgets.QWidgetAction):
|
|||
self.use_option = use_option
|
||||
self.option_tip = ""
|
||||
self.optioned = False
|
||||
self.widget = None
|
||||
|
||||
def createWidget(self, parent):
|
||||
widget = OptionalActionWidget(self.label, parent)
|
||||
|
|
@ -377,20 +374,10 @@ class OptionalAction(QtWidgets.QWidgetAction):
|
|||
self.optioned = True
|
||||
|
||||
def set_highlight(self, state, global_pos=None):
|
||||
body = self.widget.body
|
||||
option = self.widget.option
|
||||
|
||||
role = QtGui.QPalette.Highlight if state else QtGui.QPalette.Window
|
||||
body.setBackgroundRole(role)
|
||||
body.setAutoFillBackground(state)
|
||||
|
||||
if not self.use_option:
|
||||
return
|
||||
|
||||
state = option.is_hovered(global_pos)
|
||||
role = QtGui.QPalette.Highlight if state else QtGui.QPalette.Window
|
||||
option.setBackgroundRole(role)
|
||||
option.setAutoFillBackground(state)
|
||||
option_state = False
|
||||
if self.use_option:
|
||||
option_state = self.widget.option.is_hovered(global_pos)
|
||||
self.widget.set_hover_properties(state, option_state)
|
||||
|
||||
|
||||
class OptionalActionWidget(QtWidgets.QWidget):
|
||||
|
|
@ -399,30 +386,33 @@ class OptionalActionWidget(QtWidgets.QWidget):
|
|||
def __init__(self, label, parent=None):
|
||||
super(OptionalActionWidget, self).__init__(parent)
|
||||
|
||||
body = QtWidgets.QWidget()
|
||||
body.setStyleSheet("background: transparent;")
|
||||
body_widget = QtWidgets.QWidget(self)
|
||||
body_widget.setObjectName("OptionalActionBody")
|
||||
|
||||
icon = QtWidgets.QLabel()
|
||||
label = QtWidgets.QLabel(label)
|
||||
option = OptionBox(body)
|
||||
icon = QtWidgets.QLabel(body_widget)
|
||||
label = QtWidgets.QLabel(label, body_widget)
|
||||
# (NOTE) For removing ugly QLable shadow FX when highlighted in Nuke.
|
||||
# See https://stackoverflow.com/q/52838690/4145300
|
||||
label.setStyle(QtWidgets.QStyleFactory.create("Plastique"))
|
||||
option = OptionBox(body_widget)
|
||||
option.setObjectName("OptionalActionOption")
|
||||
|
||||
icon.setFixedSize(24, 16)
|
||||
option.setFixedSize(30, 30)
|
||||
|
||||
layout = QtWidgets.QHBoxLayout(body)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.setSpacing(2)
|
||||
layout.addWidget(icon)
|
||||
layout.addWidget(label)
|
||||
layout.addSpacing(6)
|
||||
body_layout = QtWidgets.QHBoxLayout(body_widget)
|
||||
body_layout.setContentsMargins(4, 0, 4, 0)
|
||||
body_layout.setSpacing(2)
|
||||
body_layout.addWidget(icon)
|
||||
body_layout.addWidget(label)
|
||||
|
||||
layout = QtWidgets.QHBoxLayout(self)
|
||||
layout.setContentsMargins(6, 1, 2, 1)
|
||||
layout.setContentsMargins(2, 1, 2, 1)
|
||||
layout.setSpacing(0)
|
||||
layout.addWidget(body)
|
||||
layout.addWidget(body_widget)
|
||||
layout.addWidget(option)
|
||||
|
||||
body.setMouseTracking(True)
|
||||
body_widget.setMouseTracking(True)
|
||||
label.setMouseTracking(True)
|
||||
option.setMouseTracking(True)
|
||||
self.setMouseTracking(True)
|
||||
|
|
@ -431,11 +421,24 @@ class OptionalActionWidget(QtWidgets.QWidget):
|
|||
self.icon = icon
|
||||
self.label = label
|
||||
self.option = option
|
||||
self.body = body
|
||||
self.body = body_widget
|
||||
|
||||
# (NOTE) For removing ugly QLable shadow FX when highlighted in Nuke.
|
||||
# See https://stackoverflow.com/q/52838690/4145300
|
||||
label.setStyle(QtWidgets.QStyleFactory.create("Plastique"))
|
||||
def set_hover_properties(self, hovered, option_hovered):
|
||||
body_state = ""
|
||||
option_state = ""
|
||||
if hovered:
|
||||
body_state = "hover"
|
||||
|
||||
if option_hovered:
|
||||
option_state = "hover"
|
||||
|
||||
if self.body.property("state") != body_state:
|
||||
self.body.setProperty("state", body_state)
|
||||
self.body.style().polish(self.body)
|
||||
|
||||
if self.option.property("state") != option_state:
|
||||
self.option.setProperty("state", option_state)
|
||||
self.option.style().polish(self.option)
|
||||
|
||||
def setIcon(self, icon):
|
||||
pixmap = icon.pixmap(16, 16)
|
||||
|
|
@ -456,8 +459,6 @@ class OptionBox(QtWidgets.QLabel):
|
|||
pixmap = icon.pixmap(18, 18)
|
||||
self.setPixmap(pixmap)
|
||||
|
||||
self.setStyleSheet("background: transparent;")
|
||||
|
||||
def is_hovered(self, global_pos):
|
||||
if global_pos is None:
|
||||
return False
|
||||
|
|
@ -476,20 +477,20 @@ class OptionDialog(QtWidgets.QDialog):
|
|||
def create(self, options):
|
||||
parser = qargparse.QArgumentParser(arguments=options)
|
||||
|
||||
decision = QtWidgets.QWidget()
|
||||
accept = QtWidgets.QPushButton("Accept")
|
||||
cancel = QtWidgets.QPushButton("Cancel")
|
||||
decision_widget = QtWidgets.QWidget(self)
|
||||
accept_btn = QtWidgets.QPushButton("Accept", decision_widget)
|
||||
cancel_btn = QtWidgets.QPushButton("Cancel", decision_widget)
|
||||
|
||||
layout = QtWidgets.QHBoxLayout(decision)
|
||||
layout.addWidget(accept)
|
||||
layout.addWidget(cancel)
|
||||
decision_layout = QtWidgets.QHBoxLayout(decision_widget)
|
||||
decision_layout.addWidget(accept_btn)
|
||||
decision_layout.addWidget(cancel_btn)
|
||||
|
||||
layout = QtWidgets.QVBoxLayout(self)
|
||||
layout.addWidget(parser)
|
||||
layout.addWidget(decision)
|
||||
layout.addWidget(decision_widget)
|
||||
|
||||
accept.clicked.connect(self.accept)
|
||||
cancel.clicked.connect(self.reject)
|
||||
accept_btn.clicked.connect(self.accept)
|
||||
cancel_btn.clicked.connect(self.reject)
|
||||
parser.changed.connect(self.on_changed)
|
||||
|
||||
def on_changed(self, argument):
|
||||
|
|
|
|||
104
openpype/vendor/python/common/capture.py
vendored
104
openpype/vendor/python/common/capture.py
vendored
|
|
@ -161,37 +161,62 @@ def capture(camera=None,
|
|||
cmds.currentTime(cmds.currentTime(query=True))
|
||||
|
||||
padding = 10 # Extend panel to accommodate for OS window manager
|
||||
|
||||
with _independent_panel(width=width + padding,
|
||||
height=height + padding,
|
||||
off_screen=off_screen) as panel:
|
||||
cmds.setFocus(panel)
|
||||
|
||||
with contextlib.nested(
|
||||
_disabled_inview_messages(),
|
||||
_maintain_camera(panel, camera),
|
||||
_applied_viewport_options(viewport_options, panel),
|
||||
_applied_camera_options(camera_options, panel),
|
||||
_applied_display_options(display_options),
|
||||
_applied_viewport2_options(viewport2_options),
|
||||
_isolated_nodes(isolate, panel),
|
||||
_maintained_time()):
|
||||
all_playblast_kwargs = {
|
||||
"compression": compression,
|
||||
"format": format,
|
||||
"percent": 100,
|
||||
"quality": quality,
|
||||
"viewer": viewer,
|
||||
"startTime": start_frame,
|
||||
"endTime": end_frame,
|
||||
"offScreen": off_screen,
|
||||
"showOrnaments": show_ornaments,
|
||||
"forceOverwrite": overwrite,
|
||||
"filename": filename,
|
||||
"widthHeight": [width, height],
|
||||
"rawFrameNumbers": raw_frame_numbers,
|
||||
"framePadding": frame_padding
|
||||
}
|
||||
all_playblast_kwargs.update(playblast_kwargs)
|
||||
|
||||
output = cmds.playblast(
|
||||
compression=compression,
|
||||
format=format,
|
||||
percent=100,
|
||||
quality=quality,
|
||||
viewer=viewer,
|
||||
startTime=start_frame,
|
||||
endTime=end_frame,
|
||||
offScreen=off_screen,
|
||||
showOrnaments=show_ornaments,
|
||||
forceOverwrite=overwrite,
|
||||
filename=filename,
|
||||
widthHeight=[width, height],
|
||||
rawFrameNumbers=raw_frame_numbers,
|
||||
framePadding=frame_padding,
|
||||
**playblast_kwargs)
|
||||
if getattr(contextlib, "nested", None):
|
||||
with contextlib.nested(
|
||||
_disabled_inview_messages(),
|
||||
_maintain_camera(panel, camera),
|
||||
_applied_viewport_options(viewport_options, panel),
|
||||
_applied_camera_options(camera_options, panel),
|
||||
_applied_display_options(display_options),
|
||||
_applied_viewport2_options(viewport2_options),
|
||||
_isolated_nodes(isolate, panel),
|
||||
_maintained_time()
|
||||
):
|
||||
output = cmds.playblast(**all_playblast_kwargs)
|
||||
else:
|
||||
with contextlib.ExitStack() as stack:
|
||||
stack.enter_context(_disabled_inview_messages())
|
||||
stack.enter_context(_maintain_camera(panel, camera))
|
||||
stack.enter_context(
|
||||
_applied_viewport_options(viewport_options, panel)
|
||||
)
|
||||
stack.enter_context(
|
||||
_applied_camera_options(camera_options, panel)
|
||||
)
|
||||
stack.enter_context(
|
||||
_applied_display_options(display_options)
|
||||
)
|
||||
stack.enter_context(
|
||||
_applied_viewport2_options(viewport2_options)
|
||||
)
|
||||
stack.enter_context(_isolated_nodes(isolate, panel))
|
||||
stack.enter_context(_maintained_time())
|
||||
|
||||
output = cmds.playblast(**all_playblast_kwargs)
|
||||
|
||||
return output
|
||||
|
||||
|
|
@ -364,7 +389,8 @@ def apply_view(panel, **options):
|
|||
|
||||
# Display options
|
||||
display_options = options.get("display_options", {})
|
||||
for key, value in display_options.iteritems():
|
||||
_iteritems = getattr(display_options, "iteritems", display_options.items)
|
||||
for key, value in _iteritems():
|
||||
if key in _DisplayOptionsRGB:
|
||||
cmds.displayRGBColor(key, *value)
|
||||
else:
|
||||
|
|
@ -372,16 +398,21 @@ def apply_view(panel, **options):
|
|||
|
||||
# Camera options
|
||||
camera_options = options.get("camera_options", {})
|
||||
for key, value in camera_options.iteritems():
|
||||
_iteritems = getattr(camera_options, "iteritems", camera_options.items)
|
||||
for key, value in _iteritems:
|
||||
cmds.setAttr("{0}.{1}".format(camera, key), value)
|
||||
|
||||
# Viewport options
|
||||
viewport_options = options.get("viewport_options", {})
|
||||
for key, value in viewport_options.iteritems():
|
||||
_iteritems = getattr(viewport_options, "iteritems", viewport_options.items)
|
||||
for key, value in _iteritems():
|
||||
cmds.modelEditor(panel, edit=True, **{key: value})
|
||||
|
||||
viewport2_options = options.get("viewport2_options", {})
|
||||
for key, value in viewport2_options.iteritems():
|
||||
_iteritems = getattr(
|
||||
viewport2_options, "iteritems", viewport2_options.items
|
||||
)
|
||||
for key, value in _iteritems():
|
||||
attr = "hardwareRenderingGlobals.{0}".format(key)
|
||||
cmds.setAttr(attr, value)
|
||||
|
||||
|
|
@ -629,14 +660,16 @@ def _applied_camera_options(options, panel):
|
|||
"for capture: %s" % opt)
|
||||
options.pop(opt)
|
||||
|
||||
for opt, value in options.iteritems():
|
||||
_iteritems = getattr(options, "iteritems", options.items)
|
||||
for opt, value in _iteritems():
|
||||
cmds.setAttr(camera + "." + opt, value)
|
||||
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
if old_options:
|
||||
for opt, value in old_options.iteritems():
|
||||
_iteritems = getattr(old_options, "iteritems", old_options.items)
|
||||
for opt, value in _iteritems():
|
||||
cmds.setAttr(camera + "." + opt, value)
|
||||
|
||||
|
||||
|
|
@ -722,14 +755,16 @@ def _applied_viewport2_options(options):
|
|||
options.pop(opt)
|
||||
|
||||
# Apply settings
|
||||
for opt, value in options.iteritems():
|
||||
_iteritems = getattr(options, "iteritems", options.items)
|
||||
for opt, value in _iteritems():
|
||||
cmds.setAttr("hardwareRenderingGlobals." + opt, value)
|
||||
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
# Restore previous settings
|
||||
for opt, value in original.iteritems():
|
||||
_iteritems = getattr(original, "iteritems", original.items)
|
||||
for opt, value in _iteritems():
|
||||
cmds.setAttr("hardwareRenderingGlobals." + opt, value)
|
||||
|
||||
|
||||
|
|
@ -769,7 +804,8 @@ def _maintain_camera(panel, camera):
|
|||
try:
|
||||
yield
|
||||
finally:
|
||||
for camera, renderable in state.iteritems():
|
||||
_iteritems = getattr(state, "iteritems", state.items)
|
||||
for camera, renderable in _iteritems():
|
||||
cmds.setAttr(camera + ".rnd", renderable)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Package declaring Pype version."""
|
||||
__version__ = "3.5.0"
|
||||
__version__ = "3.6.0-nightly.1"
|
||||
|
|
|
|||
94
poetry.lock
generated
94
poetry.lock
generated
|
|
@ -782,7 +782,7 @@ six = "*"
|
|||
|
||||
[[package]]
|
||||
name = "pillow"
|
||||
version = "8.2.0"
|
||||
version = "8.3.2"
|
||||
description = "Python Imaging Library (Fork)"
|
||||
category = "main"
|
||||
optional = false
|
||||
|
|
@ -1538,7 +1538,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pyt
|
|||
[metadata]
|
||||
lock-version = "1.1"
|
||||
python-versions = "3.7.*"
|
||||
content-hash = "ff2bfa35a7304378917a0c25d7d7af9f81a130288d95789bdf7429f071e80b69"
|
||||
content-hash = "fb6db80d126fe7ef2d1d06d0381b6d11445d6d3e54b33585f6b0a0b6b0b9d372"
|
||||
|
||||
[metadata.files]
|
||||
acre = []
|
||||
|
|
@ -2058,40 +2058,59 @@ pathlib2 = [
|
|||
{file = "pathlib2-2.3.5.tar.gz", hash = "sha256:6cd9a47b597b37cc57de1c05e56fb1a1c9cc9fab04fe78c29acd090418529868"},
|
||||
]
|
||||
pillow = [
|
||||
{file = "Pillow-8.2.0-cp36-cp36m-macosx_10_10_x86_64.whl", hash = "sha256:dc38f57d8f20f06dd7c3161c59ca2c86893632623f33a42d592f097b00f720a9"},
|
||||
{file = "Pillow-8.2.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a013cbe25d20c2e0c4e85a9daf438f85121a4d0344ddc76e33fd7e3965d9af4b"},
|
||||
{file = "Pillow-8.2.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:8bb1e155a74e1bfbacd84555ea62fa21c58e0b4e7e6b20e4447b8d07990ac78b"},
|
||||
{file = "Pillow-8.2.0-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:c5236606e8570542ed424849f7852a0ff0bce2c4c8d0ba05cc202a5a9c97dee9"},
|
||||
{file = "Pillow-8.2.0-cp36-cp36m-win32.whl", hash = "sha256:12e5e7471f9b637762453da74e390e56cc43e486a88289995c1f4c1dc0bfe727"},
|
||||
{file = "Pillow-8.2.0-cp36-cp36m-win_amd64.whl", hash = "sha256:5afe6b237a0b81bd54b53f835a153770802f164c5570bab5e005aad693dab87f"},
|
||||
{file = "Pillow-8.2.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:cb7a09e173903541fa888ba010c345893cd9fc1b5891aaf060f6ca77b6a3722d"},
|
||||
{file = "Pillow-8.2.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:0d19d70ee7c2ba97631bae1e7d4725cdb2ecf238178096e8c82ee481e189168a"},
|
||||
{file = "Pillow-8.2.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:083781abd261bdabf090ad07bb69f8f5599943ddb539d64497ed021b2a67e5a9"},
|
||||
{file = "Pillow-8.2.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:c6b39294464b03457f9064e98c124e09008b35a62e3189d3513e5148611c9388"},
|
||||
{file = "Pillow-8.2.0-cp37-cp37m-win32.whl", hash = "sha256:01425106e4e8cee195a411f729cff2a7d61813b0b11737c12bd5991f5f14bcd5"},
|
||||
{file = "Pillow-8.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:3b570f84a6161cf8865c4e08adf629441f56e32f180f7aa4ccbd2e0a5a02cba2"},
|
||||
{file = "Pillow-8.2.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:031a6c88c77d08aab84fecc05c3cde8414cd6f8406f4d2b16fed1e97634cc8a4"},
|
||||
{file = "Pillow-8.2.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:66cc56579fd91f517290ab02c51e3a80f581aba45fd924fcdee01fa06e635812"},
|
||||
{file = "Pillow-8.2.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:6c32cc3145928c4305d142ebec682419a6c0a8ce9e33db900027ddca1ec39178"},
|
||||
{file = "Pillow-8.2.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:624b977355cde8b065f6d51b98497d6cd5fbdd4f36405f7a8790e3376125e2bb"},
|
||||
{file = "Pillow-8.2.0-cp38-cp38-win32.whl", hash = "sha256:5cbf3e3b1014dddc45496e8cf38b9f099c95a326275885199f427825c6522232"},
|
||||
{file = "Pillow-8.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:463822e2f0d81459e113372a168f2ff59723e78528f91f0bd25680ac185cf797"},
|
||||
{file = "Pillow-8.2.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:95d5ef984eff897850f3a83883363da64aae1000e79cb3c321915468e8c6add5"},
|
||||
{file = "Pillow-8.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b91c36492a4bbb1ee855b7d16fe51379e5f96b85692dc8210831fbb24c43e484"},
|
||||
{file = "Pillow-8.2.0-cp39-cp39-manylinux1_i686.whl", hash = "sha256:d68cb92c408261f806b15923834203f024110a2e2872ecb0bd2a110f89d3c602"},
|
||||
{file = "Pillow-8.2.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f217c3954ce5fd88303fc0c317af55d5e0204106d86dea17eb8205700d47dec2"},
|
||||
{file = "Pillow-8.2.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:5b70110acb39f3aff6b74cf09bb4169b167e2660dabc304c1e25b6555fa781ef"},
|
||||
{file = "Pillow-8.2.0-cp39-cp39-win32.whl", hash = "sha256:a7d5e9fad90eff8f6f6106d3b98b553a88b6f976e51fce287192a5d2d5363713"},
|
||||
{file = "Pillow-8.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:238c197fc275b475e87c1453b05b467d2d02c2915fdfdd4af126145ff2e4610c"},
|
||||
{file = "Pillow-8.2.0-pp36-pypy36_pp73-macosx_10_10_x86_64.whl", hash = "sha256:0e04d61f0064b545b989126197930807c86bcbd4534d39168f4aa5fda39bb8f9"},
|
||||
{file = "Pillow-8.2.0-pp36-pypy36_pp73-manylinux2010_i686.whl", hash = "sha256:63728564c1410d99e6d1ae8e3b810fe012bc440952168af0a2877e8ff5ab96b9"},
|
||||
{file = "Pillow-8.2.0-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:c03c07ed32c5324939b19e36ae5f75c660c81461e312a41aea30acdd46f93a7c"},
|
||||
{file = "Pillow-8.2.0-pp37-pypy37_pp73-macosx_10_10_x86_64.whl", hash = "sha256:4d98abdd6b1e3bf1a1cbb14c3895226816e666749ac040c4e2554231068c639b"},
|
||||
{file = "Pillow-8.2.0-pp37-pypy37_pp73-manylinux2010_i686.whl", hash = "sha256:aac00e4bc94d1b7813fe882c28990c1bc2f9d0e1aa765a5f2b516e8a6a16a9e4"},
|
||||
{file = "Pillow-8.2.0-pp37-pypy37_pp73-manylinux2010_x86_64.whl", hash = "sha256:22fd0f42ad15dfdde6c581347eaa4adb9a6fc4b865f90b23378aa7914895e120"},
|
||||
{file = "Pillow-8.2.0-pp37-pypy37_pp73-win32.whl", hash = "sha256:e98eca29a05913e82177b3ba3d198b1728e164869c613d76d0de4bde6768a50e"},
|
||||
{file = "Pillow-8.2.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:8b56553c0345ad6dcb2e9b433ae47d67f95fc23fe28a0bde15a120f25257e291"},
|
||||
{file = "Pillow-8.2.0.tar.gz", hash = "sha256:a787ab10d7bb5494e5f76536ac460741788f1fbce851068d73a87ca7c35fc3e1"},
|
||||
{file = "Pillow-8.3.2-cp310-cp310-macosx_10_10_universal2.whl", hash = "sha256:c691b26283c3a31594683217d746f1dad59a7ae1d4cfc24626d7a064a11197d4"},
|
||||
{file = "Pillow-8.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f514c2717012859ccb349c97862568fdc0479aad85b0270d6b5a6509dbc142e2"},
|
||||
{file = "Pillow-8.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be25cb93442c6d2f8702c599b51184bd3ccd83adebd08886b682173e09ef0c3f"},
|
||||
{file = "Pillow-8.3.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d675a876b295afa114ca8bf42d7f86b5fb1298e1b6bb9a24405a3f6c8338811c"},
|
||||
{file = "Pillow-8.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59697568a0455764a094585b2551fd76bfd6b959c9f92d4bdec9d0e14616303a"},
|
||||
{file = "Pillow-8.3.2-cp310-cp310-win32.whl", hash = "sha256:2d5e9dc0bf1b5d9048a94c48d0813b6c96fccfa4ccf276d9c36308840f40c228"},
|
||||
{file = "Pillow-8.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:11c27e74bab423eb3c9232d97553111cc0be81b74b47165f07ebfdd29d825875"},
|
||||
{file = "Pillow-8.3.2-cp36-cp36m-macosx_10_10_x86_64.whl", hash = "sha256:11eb7f98165d56042545c9e6db3ce394ed8b45089a67124298f0473b29cb60b2"},
|
||||
{file = "Pillow-8.3.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f23b2d3079522fdf3c09de6517f625f7a964f916c956527bed805ac043799b8"},
|
||||
{file = "Pillow-8.3.2-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:19ec4cfe4b961edc249b0e04b5618666c23a83bc35842dea2bfd5dfa0157f81b"},
|
||||
{file = "Pillow-8.3.2-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5a31c07cea5edbaeb4bdba6f2b87db7d3dc0f446f379d907e51cc70ea375629"},
|
||||
{file = "Pillow-8.3.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:15ccb81a6ffc57ea0137f9f3ac2737ffa1d11f786244d719639df17476d399a7"},
|
||||
{file = "Pillow-8.3.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:8f284dc1695caf71a74f24993b7c7473d77bc760be45f776a2c2f4e04c170550"},
|
||||
{file = "Pillow-8.3.2-cp36-cp36m-win32.whl", hash = "sha256:4abc247b31a98f29e5224f2d31ef15f86a71f79c7f4d2ac345a5d551d6393073"},
|
||||
{file = "Pillow-8.3.2-cp36-cp36m-win_amd64.whl", hash = "sha256:a048dad5ed6ad1fad338c02c609b862dfaa921fcd065d747194a6805f91f2196"},
|
||||
{file = "Pillow-8.3.2-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:06d1adaa284696785375fa80a6a8eb309be722cf4ef8949518beb34487a3df71"},
|
||||
{file = "Pillow-8.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd24054aaf21e70a51e2a2a5ed1183560d3a69e6f9594a4bfe360a46f94eba83"},
|
||||
{file = "Pillow-8.3.2-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27a330bf7014ee034046db43ccbb05c766aa9e70b8d6c5260bfc38d73103b0ba"},
|
||||
{file = "Pillow-8.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13654b521fb98abdecec105ea3fb5ba863d1548c9b58831dd5105bb3873569f1"},
|
||||
{file = "Pillow-8.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a1bd983c565f92779be456ece2479840ec39d386007cd4ae83382646293d681b"},
|
||||
{file = "Pillow-8.3.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:4326ea1e2722f3dc00ed77c36d3b5354b8fb7399fb59230249ea6d59cbed90da"},
|
||||
{file = "Pillow-8.3.2-cp37-cp37m-win32.whl", hash = "sha256:085a90a99404b859a4b6c3daa42afde17cb3ad3115e44a75f0d7b4a32f06a6c9"},
|
||||
{file = "Pillow-8.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:18a07a683805d32826c09acfce44a90bf474e6a66ce482b1c7fcd3757d588df3"},
|
||||
{file = "Pillow-8.3.2-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:4e59e99fd680e2b8b11bbd463f3c9450ab799305d5f2bafb74fefba6ac058616"},
|
||||
{file = "Pillow-8.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4d89a2e9219a526401015153c0e9dd48319ea6ab9fe3b066a20aa9aee23d9fd3"},
|
||||
{file = "Pillow-8.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56fd98c8294f57636084f4b076b75f86c57b2a63a8410c0cd172bc93695ee979"},
|
||||
{file = "Pillow-8.3.2-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b11c9d310a3522b0fd3c35667914271f570576a0e387701f370eb39d45f08a4"},
|
||||
{file = "Pillow-8.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0412516dcc9de9b0a1e0ae25a280015809de8270f134cc2c1e32c4eeb397cf30"},
|
||||
{file = "Pillow-8.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bcb04ff12e79b28be6c9988f275e7ab69f01cc2ba319fb3114f87817bb7c74b6"},
|
||||
{file = "Pillow-8.3.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:0b9911ec70731711c3b6ebcde26caea620cbdd9dcb73c67b0730c8817f24711b"},
|
||||
{file = "Pillow-8.3.2-cp38-cp38-win32.whl", hash = "sha256:ce2e5e04bb86da6187f96d7bab3f93a7877830981b37f0287dd6479e27a10341"},
|
||||
{file = "Pillow-8.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:35d27687f027ad25a8d0ef45dd5208ef044c588003cdcedf05afb00dbc5c2deb"},
|
||||
{file = "Pillow-8.3.2-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:04835e68ef12904bc3e1fd002b33eea0779320d4346082bd5b24bec12ad9c3e9"},
|
||||
{file = "Pillow-8.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:10e00f7336780ca7d3653cf3ac26f068fa11b5a96894ea29a64d3dc4b810d630"},
|
||||
{file = "Pillow-8.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cde7a4d3687f21cffdf5bb171172070bb95e02af448c4c8b2f223d783214056"},
|
||||
{file = "Pillow-8.3.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c3ff00110835bdda2b1e2b07f4a2548a39744bb7de5946dc8e95517c4fb2ca6"},
|
||||
{file = "Pillow-8.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:35d409030bf3bd05fa66fb5fdedc39c521b397f61ad04309c90444e893d05f7d"},
|
||||
{file = "Pillow-8.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6bff50ba9891be0a004ef48828e012babaaf7da204d81ab9be37480b9020a82b"},
|
||||
{file = "Pillow-8.3.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:7dbfbc0020aa1d9bc1b0b8bcf255a7d73f4ad0336f8fd2533fcc54a4ccfb9441"},
|
||||
{file = "Pillow-8.3.2-cp39-cp39-win32.whl", hash = "sha256:963ebdc5365d748185fdb06daf2ac758116deecb2277ec5ae98139f93844bc09"},
|
||||
{file = "Pillow-8.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:cc9d0dec711c914ed500f1d0d3822868760954dce98dfb0b7382a854aee55d19"},
|
||||
{file = "Pillow-8.3.2-pp36-pypy36_pp73-macosx_10_10_x86_64.whl", hash = "sha256:2c661542c6f71dfd9dc82d9d29a8386287e82813b0375b3a02983feac69ef864"},
|
||||
{file = "Pillow-8.3.2-pp36-pypy36_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:548794f99ff52a73a156771a0402f5e1c35285bd981046a502d7e4793e8facaa"},
|
||||
{file = "Pillow-8.3.2-pp36-pypy36_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8b68f565a4175e12e68ca900af8910e8fe48aaa48fd3ca853494f384e11c8bcd"},
|
||||
{file = "Pillow-8.3.2-pp36-pypy36_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:838eb85de6d9307c19c655c726f8d13b8b646f144ca6b3771fa62b711ebf7624"},
|
||||
{file = "Pillow-8.3.2-pp36-pypy36_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:feb5db446e96bfecfec078b943cc07744cc759893cef045aa8b8b6d6aaa8274e"},
|
||||
{file = "Pillow-8.3.2-pp37-pypy37_pp73-macosx_10_10_x86_64.whl", hash = "sha256:fc0db32f7223b094964e71729c0361f93db43664dd1ec86d3df217853cedda87"},
|
||||
{file = "Pillow-8.3.2-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:fd4fd83aa912d7b89b4b4a1580d30e2a4242f3936882a3f433586e5ab97ed0d5"},
|
||||
{file = "Pillow-8.3.2-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d0c8ebbfd439c37624db98f3877d9ed12c137cadd99dde2d2eae0dab0bbfc355"},
|
||||
{file = "Pillow-8.3.2-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6cb3dd7f23b044b0737317f892d399f9e2f0b3a02b22b2c692851fb8120d82c6"},
|
||||
{file = "Pillow-8.3.2-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a66566f8a22561fc1a88dc87606c69b84fa9ce724f99522cf922c801ec68f5c1"},
|
||||
{file = "Pillow-8.3.2-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:ce651ca46d0202c302a535d3047c55a0131a720cf554a578fc1b8a2aff0e7d96"},
|
||||
{file = "Pillow-8.3.2.tar.gz", hash = "sha256:dde3f3ed8d00c72631bc19cbfff8ad3b6215062a5eed402381ad365f82f0c18c"},
|
||||
]
|
||||
pluggy = [
|
||||
{file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"},
|
||||
|
|
@ -2294,6 +2313,7 @@ pynput = [
|
|||
pyobjc-core = [
|
||||
{file = "pyobjc-core-7.3.tar.gz", hash = "sha256:5081aedf8bb40aac1a8ad95adac9e44e148a882686ded614adf46bb67fd67574"},
|
||||
{file = "pyobjc_core-7.3-1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a1f1e6b457127cbf2b5bd2b94520a7c89fb590b739911eadb2b0499a3a5b0e6f"},
|
||||
{file = "pyobjc_core-7.3-1-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:ed708cc47bae8b711f81f252af09898a5f986c7a38cec5ad5623d571d328bff8"},
|
||||
{file = "pyobjc_core-7.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4e93ad769a20b908778fe950f62a843a6d8f0fa71996e5f3cc9fab5ae7d17771"},
|
||||
{file = "pyobjc_core-7.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:9f63fd37bbf3785af4ddb2f86cad5ca81c62cfc7d1c0099637ca18343c3656c1"},
|
||||
{file = "pyobjc_core-7.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e9b1311f72f2e170742a7ee3a8149f52c35158dc024a21e88d6f1e52ba5d718b"},
|
||||
|
|
@ -2303,6 +2323,7 @@ pyobjc-core = [
|
|||
pyobjc-framework-cocoa = [
|
||||
{file = "pyobjc-framework-Cocoa-7.3.tar.gz", hash = "sha256:b18d05e7a795a3455ad191c3e43d6bfa673c2a4fd480bb1ccf57191051b80b7e"},
|
||||
{file = "pyobjc_framework_Cocoa-7.3-1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1e31376806e5de883a1d7c7c87d9ff2a8b09fc05d267e0dfce6e42409fb70c67"},
|
||||
{file = "pyobjc_framework_Cocoa-7.3-1-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:d999387927284346035cb63ebb51f86331abc41f9376f9a6970e7f18207db392"},
|
||||
{file = "pyobjc_framework_Cocoa-7.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9edffdfa6dd1f71f21b531c3e61fdd3e4d5d3bf6c5a528c98e88828cd60bac11"},
|
||||
{file = "pyobjc_framework_Cocoa-7.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:35a6340437a4e0109a302150b7d1f6baf57004ccf74834f9e6062fcafe2fd8d7"},
|
||||
{file = "pyobjc_framework_Cocoa-7.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7c3886f2608ab3ed02482f8b2ebf9f782b324c559e84b52cfd92dba8a1109872"},
|
||||
|
|
@ -2312,6 +2333,7 @@ pyobjc-framework-cocoa = [
|
|||
pyobjc-framework-quartz = [
|
||||
{file = "pyobjc-framework-Quartz-7.3.tar.gz", hash = "sha256:98812844c34262def980bdf60923a875cd43428a8375b6fd53bd2cd800eccf0b"},
|
||||
{file = "pyobjc_framework_Quartz-7.3-1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1139bc6874c0f8b58f0b8602015e0994198bc506a6bcec1071208de32b55ed26"},
|
||||
{file = "pyobjc_framework_Quartz-7.3-1-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:d94a3ed7051266c52392ec07d3b5adbf28d4be83341a24df0d88639344dcd84f"},
|
||||
{file = "pyobjc_framework_Quartz-7.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1ef18f5a16511ded65980bf4f5983ea5d35c88224dbad1b3112abd29c60413ea"},
|
||||
{file = "pyobjc_framework_Quartz-7.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3b41eec8d4b10c7c7e011e2f9051367f5499ef315ba52dfbae573c3a2e05469c"},
|
||||
{file = "pyobjc_framework_Quartz-7.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2c65456ed045dfe1711d0298734e5a3ad670f8c770f7eb3b19979256c388bdd2"},
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[tool.poetry]
|
||||
name = "OpenPype"
|
||||
version = "3.0.0"
|
||||
version = "3.6.0-nightly.1" # OpenPype
|
||||
description = "Open VFX and Animation pipeline with support."
|
||||
authors = ["OpenPype Team <info@openpype.io>"]
|
||||
license = "MIT License"
|
||||
|
|
@ -45,7 +45,7 @@ jsonschema = "^3.2.0"
|
|||
keyring = "^22.0.1"
|
||||
log4mongo = "^1.7"
|
||||
pathlib2= "^2.3.5" # deadline submit publish job only (single place, maybe not needed?)
|
||||
Pillow = "^8.1" # only used for slates prototype
|
||||
Pillow = "^8.3" # only used for slates prototype
|
||||
pyblish-base = "^1.8.8"
|
||||
pynput = "^1.7.2" # idle manager in tray
|
||||
pymongo = "^3.11.2"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,94 @@
|
|||
import pytest
|
||||
import os
|
||||
import shutil
|
||||
|
||||
from tests.lib.testing_classes import PublishTest
|
||||
|
||||
|
||||
class TestPublishInPhotoshop(PublishTest):
|
||||
"""Basic test case for publishing in Photoshop
|
||||
|
||||
Uses generic TestCase to prepare fixtures for test data, testing DBs,
|
||||
env vars.
|
||||
|
||||
Opens Maya, run publish on prepared workile.
|
||||
|
||||
Then checks content of DB (if subset, version, representations were
|
||||
created.
|
||||
Checks tmp folder if all expected files were published.
|
||||
|
||||
"""
|
||||
PERSIST = True
|
||||
|
||||
TEST_FILES = [
|
||||
("1Bciy2pCwMKl1UIpxuPnlX_LHMo_Xkq0K", "test_photoshop_publish.zip", "")
|
||||
]
|
||||
|
||||
APP = "photoshop"
|
||||
APP_VARIANT = "2020"
|
||||
|
||||
APP_NAME = "{}/{}".format(APP, APP_VARIANT)
|
||||
|
||||
TIMEOUT = 120 # publish timeout
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def last_workfile_path(self, download_test_data):
|
||||
"""Get last_workfile_path from source data.
|
||||
|
||||
Maya expects workfile in proper folder, so copy is done first.
|
||||
"""
|
||||
src_path = os.path.join(download_test_data,
|
||||
"input",
|
||||
"workfile",
|
||||
"test_project_test_asset_TestTask_v001.psd")
|
||||
dest_folder = os.path.join(download_test_data,
|
||||
self.PROJECT,
|
||||
self.ASSET,
|
||||
"work",
|
||||
self.TASK)
|
||||
os.makedirs(dest_folder)
|
||||
dest_path = os.path.join(dest_folder,
|
||||
"test_project_test_asset_TestTask_v001.psd")
|
||||
shutil.copy(src_path, dest_path)
|
||||
|
||||
yield dest_path
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def startup_scripts(self, monkeypatch_session, download_test_data):
|
||||
"""Points Maya to userSetup file from input data"""
|
||||
os.environ["IS_HEADLESS"] = "true"
|
||||
|
||||
def test_db_asserts(self, dbcon, publish_finished):
|
||||
"""Host and input data dependent expected results in DB."""
|
||||
print("test_db_asserts")
|
||||
assert 5 == dbcon.count_documents({"type": "version"}), \
|
||||
"Not expected no of versions"
|
||||
|
||||
assert 0 == dbcon.count_documents({"type": "version",
|
||||
"name": {"$ne": 1}}), \
|
||||
"Only versions with 1 expected"
|
||||
|
||||
assert 1 == dbcon.count_documents({"type": "subset",
|
||||
"name": "modelMain"}), \
|
||||
"modelMain subset must be present"
|
||||
|
||||
assert 1 == dbcon.count_documents({"type": "subset",
|
||||
"name": "workfileTest_task"}), \
|
||||
"workfileTest_task subset must be present"
|
||||
|
||||
assert 11 == dbcon.count_documents({"type": "representation"}), \
|
||||
"Not expected no of representations"
|
||||
|
||||
assert 2 == dbcon.count_documents({"type": "representation",
|
||||
"context.subset": "modelMain",
|
||||
"context.ext": "abc"}), \
|
||||
"Not expected no of representations with ext 'abc'"
|
||||
|
||||
assert 2 == dbcon.count_documents({"type": "representation",
|
||||
"context.subset": "modelMain",
|
||||
"context.ext": "ma"}), \
|
||||
"Not expected no of representations with ext 'abc'"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_case = TestPublishInPhotoshop()
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue