[Automated] Merged develop into main

This commit is contained in:
pypebot 2022-02-01 12:37:46 +01:00 committed by GitHub
commit c4eecfa88c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
89 changed files with 541 additions and 263 deletions

View file

@ -6,6 +6,8 @@ class AddLastWorkfileToLaunchArgs(PreLaunchHook):
"""Add last workfile path to launch arguments.
This is not possible to do for all applications the same way.
Checks 'start_last_workfile', if set to False, it will not open last
workfile. This property is set explicitly in Launcher.
"""
# Execute after workfile template copy

View file

@ -43,6 +43,7 @@ class GlobalHostDataHook(PreLaunchHook):
"env": self.launch_context.env,
"start_last_workfile": self.data.get("start_last_workfile"),
"last_workfile_path": self.data.get("last_workfile_path"),
"log": self.log

View file

@ -40,7 +40,10 @@ class NonPythonHostHook(PreLaunchHook):
)
# Add workfile path if exists
workfile_path = self.data["last_workfile_path"]
if os.path.exists(workfile_path):
if (
self.data.get("start_last_workfile")
and workfile_path
and os.path.exists(workfile_path)):
new_launch_args.append(workfile_path)
# Append as whole list as these areguments should not be separated

View file

@ -71,7 +71,7 @@ class ExtractSubsetResources(openpype.api.Extractor):
staging_dir = self.staging_dir(instance)
# add default preset type for thumbnail and reviewable video
# update them with settings and overide in case the same
# update them with settings and override in case the same
# are found in there
export_presets = deepcopy(self.default_presets)
export_presets.update(self.export_presets_mapping)

View file

@ -218,12 +218,10 @@ def on_task_changed(*args):
)
def before_workfile_save(workfile_path):
if not workfile_path:
return
workdir = os.path.dirname(workfile_path)
copy_workspace_mel(workdir)
def before_workfile_save(event):
workdir_path = event.workdir_path
if workdir_path:
copy_workspace_mel(workdir_path)
class MayaDirmap(HostDirmap):

View file

@ -1490,6 +1490,7 @@ def _prepare_last_workfile(data, workdir):
import avalon.api
log = data["log"]
_workdir_data = data.get("workdir_data")
if not _workdir_data:
log.info(
@ -1503,9 +1504,15 @@ def _prepare_last_workfile(data, workdir):
project_name = data["project_name"]
task_name = data["task_name"]
task_type = data["task_type"]
start_last_workfile = should_start_last_workfile(
project_name, app.host_name, task_name, task_type
)
start_last_workfile = data.get("start_last_workfile")
if start_last_workfile is None:
start_last_workfile = should_start_last_workfile(
project_name, app.host_name, task_name, task_type
)
else:
log.info("Opening of last workfile was disabled by user")
data["start_last_workfile"] = start_last_workfile
workfile_startup = should_workfile_tool_start(

View file

@ -34,11 +34,17 @@ def get_vendor_bin_path(bin_app):
def get_oiio_tools_path(tool="oiiotool"):
"""Path to vendorized OpenImageIO tool executables.
On Window it adds .exe extension if missing from tool argument.
Args:
tool (string): Tool name (oiiotool, maketx, ...).
Default is "oiiotool".
"""
oiio_dir = get_vendor_bin_path("oiio")
if platform.system().lower() == "windows" and not tool.lower().endswith(
".exe"
):
tool = "{}.exe".format(tool)
return os.path.join(oiio_dir, tool)

View file

@ -164,7 +164,7 @@ class ProcessEventHub(SocketBaseEventHub):
sys.exit(0)
def wait(self, duration=None):
"""Overriden wait
"""Overridden wait
Event are loaded from Mongo DB when queue is empty. Handled event is
set as processed in Mongo DB.
"""

View file

@ -95,7 +95,7 @@ class DropboxHandler(AbstractProvider):
"key": "acting_as_member",
"label": "Acting As Member"
},
# roots could be overriden only on Project level, User cannot
# roots could be overridden only on Project level, User cannot
{
"key": "root",
"label": "Roots",

View file

@ -119,7 +119,7 @@ class GDriveHandler(AbstractProvider):
# {platform} tells that value is multiplatform and only specific OS
# should be returned
editable = [
# credentials could be overriden on Project or User level
# credentials could be overridden on Project or User level
{
"type": "path",
"key": "credentials_url",
@ -127,7 +127,7 @@ class GDriveHandler(AbstractProvider):
"multiplatform": True,
"placeholder": "Credentials url"
},
# roots could be overriden only on Project leve, User cannot
# roots could be overridden only on Project level, User cannot
{
"key": "root",
"label": "Roots",
@ -414,7 +414,7 @@ class GDriveHandler(AbstractProvider):
def delete_folder(self, path, force=False):
"""
Deletes folder on GDrive. Checks if folder contains any files or
subfolders. In that case raises error, could be overriden by
subfolders. In that case raises error, could be overridden by
'force' argument.
In that case deletes folder on 'path' and all its children.

View file

@ -97,7 +97,7 @@ class SFTPHandler(AbstractProvider):
# {platform} tells that value is multiplatform and only specific OS
# should be returned
editable = [
# credentials could be overriden on Project or User level
# credentials could be overridden on Project or User level
{
'key': "sftp_host",
'label': "SFTP host name",
@ -129,7 +129,7 @@ class SFTPHandler(AbstractProvider):
'label': "SFTP user ssh key password",
'type': 'text'
},
# roots could be overriden only on Project leve, User cannot
# roots could be overridden only on Project level, User cannot
{
"key": "root",
"label": "Roots",

View file

@ -1073,7 +1073,7 @@ class SyncServerModule(OpenPypeModule, ITrayModule):
"""
Returns settings for 'studio' and user's local site
Returns base values from setting, not overriden by Local Settings,
Returns base values from setting, not overridden by Local Settings,
eg. value used to push TO LS not to get actual value for syncing.
"""
if not project_name:

View file

@ -115,7 +115,7 @@ class ITrayAction(ITrayModule):
Add action to tray menu which will trigger `on_action_trigger`.
It is expected to be used for showing tools.
Methods `tray_start`, `tray_exit` and `connect_with_modules` are overriden
Methods `tray_start`, `tray_exit` and `connect_with_modules` are overridden
as it's not expected that action will use them. But it is possible if
necessary.
"""

View file

@ -72,7 +72,7 @@ class WorkerRpc(JsonRpc):
self._job_queue.remove_worker(worker)
async def handle_websocket_request(self, http_request):
"""Overide this method to catch CLOSING messages."""
"""Override this method to catch CLOSING messages."""
http_request.msg_id = 0
http_request.pending = {}

View file

@ -1,3 +1,8 @@
from .events import (
BaseEvent,
BeforeWorkfileSave
)
from .attribute_definitions import (
AbtractAttrDef,
UnknownDef,
@ -9,6 +14,9 @@ from .attribute_definitions import (
__all__ = (
"BaseEvent",
"BeforeWorkfileSave",
"AbtractAttrDef",
"UnknownDef",
"NumberDef",

View file

@ -0,0 +1,51 @@
"""Events holding data about specific event."""
# Inherit from 'object' for Python 2 hosts
class BaseEvent(object):
"""Base event object.
Can be used to anything because data are not much specific. Only required
argument is topic which defines why event is happening and may be used for
filtering.
Arg:
topic (str): Identifier of event.
data (Any): Data specific for event. Dictionary is recommended.
"""
_data = {}
def __init__(self, topic, data=None):
self._topic = topic
if data is None:
data = {}
self._data = data
@property
def data(self):
return self._data
@property
def topic(self):
return self._topic
@classmethod
def emit(cls, *args, **kwargs):
"""Create object of event and emit.
Args:
Same args as '__init__' expects which may be class specific.
"""
from avalon import pipeline
obj = cls(*args, **kwargs)
pipeline.emit(obj.topic, [obj])
return obj
class BeforeWorkfileSave(BaseEvent):
"""Before workfile changes event data."""
def __init__(self, filename, workdir):
super(BeforeWorkfileSave, self).__init__("before.workfile.save")
self.filename = filename
self.workdir_path = workdir

View file

@ -31,7 +31,7 @@ class DiscoverResult:
def publish_plugins_discover(paths=None):
"""Find and return available pyblish plug-ins
Overriden function from `pyblish` module to be able collect crashed files
Overridden function from `pyblish` module to be able collect crashed files
and reason of their crash.
Arguments:

View file

@ -11,6 +11,7 @@ class CollectSceneVersion(pyblish.api.ContextPlugin):
order = pyblish.api.CollectorOrder
label = 'Collect Scene Version'
# configurable in Settings
hosts = [
"aftereffects",
"blender",
@ -26,7 +27,19 @@ class CollectSceneVersion(pyblish.api.ContextPlugin):
"tvpaint"
]
# in some cases of headless publishing (for example webpublisher using PS)
# you want to ignore version from name and let integrate use next version
skip_hosts_headless_publish = []
def process(self, context):
# tests should be close to regular publish as possible
if (
os.environ.get("HEADLESS_PUBLISH")
and not os.environ.get("IS_TEST")
and context.data["hostName"] in self.skip_hosts_headless_publish):
self.log.debug("Skipping for headless publishing")
return
assert context.data.get('currentFile'), "Cannot get current file"
filename = os.path.basename(context.data.get('currentFile'))

View file

@ -252,7 +252,7 @@ class ModifiedBurnins(ffmpeg_burnins.Burnins):
- required IF start frame is not set when using frames or timecode burnins
On initializing class can be set General options through "options_init" arg.
General can be overriden when adding burnin
General can be overridden when adding burnin
'''
TOP_CENTERED = ffmpeg_burnins.TOP_CENTERED
@ -549,7 +549,7 @@ def burnins_from_data(
codec_data (list): All codec related arguments in list.
options (dict): Options for burnins.
burnin_values (dict): Contain positioned values.
overwrite (bool): Output will be overriden if already exists,
overwrite (bool): Output will be overwritten if already exists,
True by default.
Presets must be set separately. Should be dict with 2 keys:

View file

@ -2,14 +2,14 @@ import re
# Metadata keys for work with studio and project overrides
M_OVERRIDEN_KEY = "__overriden_keys__"
M_OVERRIDDEN_KEY = "__overriden_keys__"
# Metadata key for storing information about environments
M_ENVIRONMENT_KEY = "__environment_keys__"
# Metadata key for storing dynamic created labels
M_DYNAMIC_KEY_LABEL = "__dynamic_keys_labels__"
METADATA_KEYS = (
M_OVERRIDEN_KEY,
M_OVERRIDDEN_KEY,
M_ENVIRONMENT_KEY,
M_DYNAMIC_KEY_LABEL
)
@ -32,7 +32,7 @@ KEY_REGEX = re.compile(r"^[{}]+$".format(KEY_ALLOWED_SYMBOLS))
__all__ = (
"M_OVERRIDEN_KEY",
"M_OVERRIDDEN_KEY",
"M_ENVIRONMENT_KEY",
"M_DYNAMIC_KEY_LABEL",

View file

@ -3,6 +3,24 @@
"CollectAnatomyInstanceData": {
"follow_workfile_version": false
},
"CollectSceneVersion": {
"hosts": [
"aftereffects",
"blender",
"celaction",
"fusion",
"harmony",
"hiero",
"houdini",
"maya",
"nuke",
"photoshop",
"resolve",
"tvpaint"
],
"skip_hosts_headless_publish": [
]
},
"ValidateEditorialAssetName": {
"enabled": true,
"optional": false

View file

@ -12,7 +12,7 @@
{
"color_code": [],
"layer_name_regex": [],
"family": "",
"family": "image",
"subset_template_name": ""
}
]

View file

@ -752,7 +752,7 @@ class BaseItemEntity(BaseEntity):
@abstractmethod
def _add_to_project_override(self, on_change_trigger):
"""Item's implementation to set values as overriden for project.
"""Item's implementation to set values as overridden for project.
Mark item and all it's children to be stored as project overrides.
"""
@ -794,7 +794,7 @@ class BaseItemEntity(BaseEntity):
"""Item's implementation to remove project overrides.
Mark item as does not have project overrides. Must not change
`was_overriden` attribute value.
`was_overridden` attribute value.
Args:
on_change_trigger (list): Callbacks of `on_change` should be stored

View file

@ -6,7 +6,7 @@ from .lib import (
)
from openpype.settings.constants import (
METADATA_KEYS,
M_OVERRIDEN_KEY,
M_OVERRIDDEN_KEY,
KEY_REGEX
)
from . import (
@ -119,7 +119,7 @@ class DictConditionalEntity(ItemEntity):
# `current_metadata` are still when schema is loaded
# - only metadata stored with dict item are gorup overrides in
# M_OVERRIDEN_KEY
# M_OVERRIDDEN_KEY
self._current_metadata = {}
self._metadata_are_modified = False
@ -377,9 +377,9 @@ class DictConditionalEntity(ItemEntity):
):
continue
if M_OVERRIDEN_KEY not in current_metadata:
current_metadata[M_OVERRIDEN_KEY] = []
current_metadata[M_OVERRIDEN_KEY].append(key)
if M_OVERRIDDEN_KEY not in current_metadata:
current_metadata[M_OVERRIDDEN_KEY] = []
current_metadata[M_OVERRIDDEN_KEY].append(key)
# Define if current metadata are avaialble for current override state
metadata = NOT_SET
@ -535,7 +535,7 @@ class DictConditionalEntity(ItemEntity):
enum_value = value.get(self.enum_key)
old_metadata = metadata.get(M_OVERRIDEN_KEY)
old_metadata = metadata.get(M_OVERRIDDEN_KEY)
if old_metadata:
old_metadata_set = set(old_metadata)
new_metadata = []
@ -547,7 +547,7 @@ class DictConditionalEntity(ItemEntity):
for key in old_metadata_set:
new_metadata.append(key)
metadata[M_OVERRIDEN_KEY] = new_metadata
metadata[M_OVERRIDDEN_KEY] = new_metadata
return value, metadata

View file

@ -9,7 +9,7 @@ from .lib import (
)
from openpype.settings.constants import (
METADATA_KEYS,
M_OVERRIDEN_KEY,
M_OVERRIDDEN_KEY,
KEY_REGEX
)
from . import (
@ -183,7 +183,7 @@ class DictImmutableKeysEntity(ItemEntity):
# `current_metadata` are still when schema is loaded
# - only metadata stored with dict item are gorup overrides in
# M_OVERRIDEN_KEY
# M_OVERRIDDEN_KEY
self._current_metadata = {}
self._metadata_are_modified = False
@ -257,9 +257,9 @@ class DictImmutableKeysEntity(ItemEntity):
):
continue
if M_OVERRIDEN_KEY not in current_metadata:
current_metadata[M_OVERRIDEN_KEY] = []
current_metadata[M_OVERRIDEN_KEY].append(key)
if M_OVERRIDDEN_KEY not in current_metadata:
current_metadata[M_OVERRIDDEN_KEY] = []
current_metadata[M_OVERRIDDEN_KEY].append(key)
# Define if current metadata are avaialble for current override state
metadata = NOT_SET
@ -399,7 +399,7 @@ class DictImmutableKeysEntity(ItemEntity):
if key in value:
metadata[key] = value.pop(key)
old_metadata = metadata.get(M_OVERRIDEN_KEY)
old_metadata = metadata.get(M_OVERRIDDEN_KEY)
if old_metadata:
old_metadata_set = set(old_metadata)
new_metadata = []
@ -410,7 +410,7 @@ class DictImmutableKeysEntity(ItemEntity):
for key in old_metadata_set:
new_metadata.append(key)
metadata[M_OVERRIDEN_KEY] = new_metadata
metadata[M_OVERRIDDEN_KEY] = new_metadata
return value, metadata

View file

@ -222,7 +222,7 @@ class DictMutableKeysEntity(EndpointEntity):
self.required_keys = self.schema_data.get("required_keys") or []
self.collapsible_key = self.schema_data.get("collapsible_key") or False
# GUI attributes
self.hightlight_content = (
self.highlight_content = (
self.schema_data.get("highlight_content") or False
)

View file

@ -121,6 +121,20 @@ class EnumEntity(BaseEnumEntity):
)
super(EnumEntity, self).schema_validations()
def set_override_state(self, *args, **kwargs):
super(EnumEntity, self).set_override_state(*args, **kwargs)
# Make sure current value is valid
if self.multiselection:
new_value = []
for key in self._current_value:
if key in self.valid_keys:
new_value.append(key)
self._current_value = new_value
elif self._current_value not in self.valid_keys:
self._current_value = self.value_on_not_set
class HostsEnumEntity(BaseEnumEntity):
"""Enumeration of host names.

View file

@ -101,7 +101,7 @@ class OverrideState:
- DEFAULTS - Entity cares only about default values. It is not
possible to set higher state if any entity does not have filled
default value.
- STUDIO - First layer of overrides. Hold only studio overriden values
- STUDIO - First layer of overrides. Hold only studio overridden values
that are applied on top of defaults.
- PROJECT - Second layer of overrides. Hold only project overrides that are
applied on top of defaults and studio overrides.

View file

@ -10,7 +10,7 @@
- `"is_file"` - this key is for storing openpype defaults in `openpype` repo
- reasons of existence: developing new schemas does not require to create defaults manually
- key is validated, must be once in hierarchy else it won't be possible to store openpype defaults
- `"is_group"` - define that all values under key in hierarchy will be overriden if any value is modified, this information is also stored to overrides
- `"is_group"` - define that all values under key in hierarchy will be overridden if any value is modified, this information is also stored to overrides
- this keys is not allowed for all inputs as they may have not reason for that
- key is validated, can be only once in hierarchy but is not required
- currently there are `system settings` and `project settings`
@ -767,7 +767,7 @@ Anatomy represents data stored on project document.
### anatomy
- entity works similarly to `dict`
- anatomy has always all keys overriden with overrides
- anatomy has always all keys overridden with overrides
- overrides are not applied as all anatomy data must be available from project document
- all children must be groups

View file

@ -18,6 +18,27 @@
}
]
},
{
"type": "dict",
"collapsible": true,
"key": "CollectSceneVersion",
"label": "Collect Version from Workfile",
"is_group": true,
"children": [
{
"key": "hosts",
"label": "Host names",
"type": "hosts-enum",
"multiselection": true
},
{
"key": "skip_hosts_headless_publish",
"label": "Skip for host if headless publish",
"type": "hosts-enum",
"multiselection": true
}
]
},
{
"type": "dict",
"collapsible": true,

View file

@ -11,13 +11,13 @@
},
{
"type": "dict-conditional",
"key": "overriden_value",
"label": "Overriden value",
"enum_key": "overriden",
"key": "overridden_value",
"label": "Overridden value",
"enum_key": "overridden",
"enum_is_horizontal": true,
"enum_children": [
{
"key": "overriden",
"key": "overridden",
"label": "Override value",
"children": [
{

View file

@ -13,7 +13,7 @@ from .constants import (
PROJECT_SETTINGS_KEY,
PROJECT_ANATOMY_KEY,
LOCAL_SETTING_KEY,
M_OVERRIDEN_KEY
M_OVERRIDDEN_KEY
)
from .lib import load_json_file
@ -254,12 +254,12 @@ class MongoSettingsHandler(SettingsHandler):
continue
# Pop key from values
output[key] = general_data.pop(key)
# Pop key from overriden metadata
# Pop key from overridden metadata
if (
M_OVERRIDEN_KEY in general_data
and key in general_data[M_OVERRIDEN_KEY]
M_OVERRIDDEN_KEY in general_data
and key in general_data[M_OVERRIDDEN_KEY]
):
general_data[M_OVERRIDEN_KEY].remove(key)
general_data[M_OVERRIDDEN_KEY].remove(key)
return output
def _apply_global_settings(
@ -319,17 +319,17 @@ class MongoSettingsHandler(SettingsHandler):
system_general = {}
system_settings_data["general"] = system_general
overriden_keys = system_general.get(M_OVERRIDEN_KEY) or []
overridden_keys = system_general.get(M_OVERRIDDEN_KEY) or []
for key in self.global_general_keys:
if key not in globals_data:
continue
system_general[key] = globals_data[key]
if key not in overriden_keys:
overriden_keys.append(key)
if key not in overridden_keys:
overridden_keys.append(key)
if overriden_keys:
system_general[M_OVERRIDEN_KEY] = overriden_keys
if overridden_keys:
system_general[M_OVERRIDDEN_KEY] = overridden_keys
return system_settings_document

View file

@ -8,7 +8,7 @@ from .exceptions import (
SaveWarningExc
)
from .constants import (
M_OVERRIDEN_KEY,
M_OVERRIDDEN_KEY,
M_ENVIRONMENT_KEY,
METADATA_KEYS,
@ -546,13 +546,13 @@ def subkey_merge(_dict, value, keys):
def merge_overrides(source_dict, override_dict):
"""Merge data from override_dict to source_dict."""
if M_OVERRIDEN_KEY in override_dict:
overriden_keys = set(override_dict.pop(M_OVERRIDEN_KEY))
if M_OVERRIDDEN_KEY in override_dict:
overridden_keys = set(override_dict.pop(M_OVERRIDDEN_KEY))
else:
overriden_keys = set()
overridden_keys = set()
for key, value in override_dict.items():
if (key in overriden_keys or key not in source_dict):
if (key in overridden_keys or key not in source_dict):
source_dict[key] = value
elif isinstance(value, dict) and isinstance(source_dict[key], dict):
@ -574,7 +574,7 @@ def apply_local_settings_on_system_settings(system_settings, local_settings):
"""Apply local settings on studio system settings.
ATM local settings can modify only application executables. Executable
values are not overriden but prepended.
values are not overridden but prepended.
"""
if not local_settings or "applications" not in local_settings:
return
@ -914,7 +914,7 @@ def get_environments():
"""Calculated environment based on defaults and system settings.
Any default environment also found in the system settings will be fully
overriden by the one from the system settings.
overridden by the one from the system settings.
Returns:
dict: Output should be ready for `acre` module.

View file

@ -112,7 +112,7 @@
"breadcrumbs-btn-bg": "rgba(127, 127, 127, 60)",
"breadcrumbs-btn-bg-hover": "rgba(127, 127, 127, 90)",
"content-hightlighted": "rgba(19, 26, 32, 15)",
"content-highlighted": "rgba(19, 26, 32, 15)",
"focus-border": "#839caf",
"image-btn": "#bfccd6",
"image-btn-hover": "#189aea",

View file

@ -1093,16 +1093,16 @@ QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {
#ExpandLabel[state="modified"]:hover, #SettingsLabel[state="modified"]:hover {
color: {color:settings:modified-light};
}
#ExpandLabel[state="overriden-modified"], #SettingsLabel[state="overriden-modified"] {
#ExpandLabel[state="overridden-modified"], #SettingsLabel[state="overridden-modified"] {
color: {color:settings:modified-mid};
}
#ExpandLabel[state="overriden-modified"]:hover, #SettingsLabel[state="overriden-modified"]:hover {
#ExpandLabel[state="overridden-modified"]:hover, #SettingsLabel[state="overridden-modified"]:hover {
color: {color:settings:modified-light};
}
#ExpandLabel[state="overriden"], #SettingsLabel[state="overriden"] {
#ExpandLabel[state="overridden"], #SettingsLabel[state="overridden"] {
color: {color:settings:project-mid};
}
#ExpandLabel[state="overriden"]:hover, #SettingsLabel[state="overriden"]:hover {
#ExpandLabel[state="overridden"]:hover, #SettingsLabel[state="overridden"]:hover {
color: {color:settings:project-light};
}
#ExpandLabel[state="invalid"], #SettingsLabel[state="invalid"] {
@ -1116,10 +1116,10 @@ QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {
#SettingsMainWidget QWidget[input-state="modified"] {
border-color: {color:settings:modified-mid};
}
#SettingsMainWidget QWidget[input-state="overriden-modified"] {
#SettingsMainWidget QWidget[input-state="overridden-modified"] {
border-color: {color:settings:modified-mid};
}
#SettingsMainWidget QWidget[input-state="overriden"] {
#SettingsMainWidget QWidget[input-state="overridden"] {
border-color: {color:settings:project-mid};
}
#SettingsMainWidget QWidget[input-state="invalid"] {
@ -1145,8 +1145,8 @@ QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {
#ContentWidget {
background-color: transparent;
}
#ContentWidget[content_state="hightlighted"] {
background-color: {color:settings:content-hightlighted};
#ContentWidget[content_state="highlighted"] {
background-color: {color:settings:content-highlighted};
}
#SideLineWidget {
@ -1172,11 +1172,11 @@ QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {
#SideLineWidget[state="child-invalid"] {border-color: {color:settings:invalid-dark};}
#SideLineWidget[state="child-invalid"]:hover {border-color: {color:settings:invalid-light};}
#SideLineWidget[state="child-overriden"] {border-color: {color:settings:project-dark};}
#SideLineWidget[state="child-overriden"]:hover {border-color: {color:settings:project-mid};}
#SideLineWidget[state="child-overridden"] {border-color: {color:settings:project-dark};}
#SideLineWidget[state="child-overridden"]:hover {border-color: {color:settings:project-mid};}
#SideLineWidget[state="child-overriden-modified"] {border-color: {color:settings:modified-dark};}
#SideLineWidget[state="child-overriden-modified"]:hover {border-color: {color:settings:modified-mid};}
#SideLineWidget[state="child-overridden-modified"] {border-color: {color:settings:modified-dark};}
#SideLineWidget[state="child-overridden-modified"]:hover {border-color: {color:settings:modified-mid};}
#DictAsWidgetBody {
background: transparent;

View file

@ -32,9 +32,9 @@ def test_avalon_plugin_presets(monkeypatch, printer):
assert MyTestCreator in plugins
for p in plugins:
if p.__name__ == "MyTestCreator":
printer("Test if we have overriden existing property")
printer("Test if we have overridden existing property")
assert p.my_test_property == "B"
printer("Test if we have overriden superclass property")
printer("Test if we have overridden superclass property")
assert p.active is False
printer("Test if we have added new property")
assert p.new_property == "new"

View file

@ -87,7 +87,7 @@ class Window(QtWidgets.QDialog):
btn_layout = QtWidgets.QHBoxLayout(btns_widget)
btn_create_asset = QtWidgets.QPushButton("Create asset")
btn_create_asset.setToolTip(
"Creates all neccessary components for asset"
"Creates all necessary components for asset"
)
checkbox_app = None
if self.context is not None:
@ -231,7 +231,7 @@ class Window(QtWidgets.QDialog):
test_name = name.replace(' ', '')
error_message = None
message = QtWidgets.QMessageBox(self)
message.setWindowTitle("Some errors has occured")
message.setWindowTitle("Some errors have occurred")
message.setIcon(QtWidgets.QMessageBox.Critical)
# TODO: show error messages on any error
if self.valid_parent is not True and test_name == '':

View file

@ -44,7 +44,7 @@ def preserve_expanded_rows(tree_view,
This function is created to maintain the expand vs collapse status of
the model items. When refresh is triggered the items which are expanded
will stay expanded and vise versa.
will stay expanded and vice versa.
Arguments:
tree_view (QWidgets.QTreeView): the tree view which is
@ -94,7 +94,7 @@ def preserve_selection(tree_view,
This function is created to maintain the selection status of
the model items. When refresh is triggered the items which are expanded
will stay expanded and vise versa.
will stay expanded and vice versa.
tree_view (QWidgets.QTreeView): the tree view nested in the application
column (int): the column to retrieve the data from
@ -179,7 +179,7 @@ class AssetModel(TreeModel):
"""
if silos:
# WARNING: Silo item "_id" is set to silo value
# mainly because GUI issue with perserve selection and expanded row
# mainly because GUI issue with preserve selection and expanded row
# and because of easier hierarchy parenting (in "assets")
for silo in silos:
item = Item({

View file

@ -46,7 +46,7 @@ class ContextDialog(QtWidgets.QDialog):
# UI initialization
main_splitter = QtWidgets.QSplitter(self)
# Left side widget containt project combobox and asset widget
# Left side widget contains project combobox and asset widget
left_side_widget = QtWidgets.QWidget(main_splitter)
project_combobox = QtWidgets.QComboBox(left_side_widget)

View file

@ -354,7 +354,7 @@ class CreatorWindow(QtWidgets.QDialog):
Override keyPressEvent to do nothing so that Maya's panels won't
take focus when pressing "SHIFT" whilst mouse is over viewport or
outliner. This way users don't accidently perform Maya commands
outliner. This way users don't accidentally perform Maya commands
whilst trying to name an instance.
"""

View file

@ -107,7 +107,7 @@ class ExperimentalToolsDialog(QtWidgets.QDialog):
# Is dialog first shown
self._first_show = True
# Trigger refresh when window get's activity
# Trigger refresh when window gets activity
self._refresh_on_active = True
# Is window active
self._window_is_active = False

View file

@ -43,7 +43,7 @@ class ExperimentalTool:
self._enabled = enabled
def execute(self):
"""Trigger registerd callback."""
"""Trigger registered callback."""
self.callback()

View file

@ -62,6 +62,7 @@ class ApplicationAction(api.Action):
icon = None
color = None
order = 0
data = {}
_log = None
required_session_keys = (
@ -103,7 +104,8 @@ class ApplicationAction(api.Action):
self.application.launch(
project_name=project_name,
asset_name=asset_name,
task_name=task_name
task_name=task_name,
**self.data
)
except ApplictionExecutableNotFound as exc:

View file

@ -7,6 +7,8 @@ VARIANT_GROUP_ROLE = QtCore.Qt.UserRole + 2
ACTION_ID_ROLE = QtCore.Qt.UserRole + 3
ANIMATION_START_ROLE = QtCore.Qt.UserRole + 4
ANIMATION_STATE_ROLE = QtCore.Qt.UserRole + 5
FORCE_NOT_OPEN_WORKFILE_ROLE = QtCore.Qt.UserRole + 6
ACTION_TOOLTIP_ROLE = QtCore.Qt.UserRole + 7
# Animation length in seconds
ANIMATION_LEN = 7

View file

@ -2,7 +2,8 @@ import time
from Qt import QtCore, QtWidgets, QtGui
from .constants import (
ANIMATION_START_ROLE,
ANIMATION_STATE_ROLE
ANIMATION_STATE_ROLE,
FORCE_NOT_OPEN_WORKFILE_ROLE
)
@ -69,6 +70,16 @@ class ActionDelegate(QtWidgets.QStyledItemDelegate):
self._draw_animation(painter, option, index)
super(ActionDelegate, self).paint(painter, option, index)
if index.data(FORCE_NOT_OPEN_WORKFILE_ROLE):
rect = QtCore.QRectF(option.rect.x(), option.rect.height(),
5, 5)
painter.setPen(QtCore.Qt.transparent)
painter.setBrush(QtGui.QColor(200, 0, 0))
painter.drawEllipse(rect)
painter.setBrush(self.extender_bg_brush)
is_group = False
for group_role in self.group_roles:
is_group = index.data(group_role)

View file

@ -29,7 +29,7 @@ class ProjectHandler(QtCore.QObject):
Helps to organize two separate widgets handling current project selection.
It is easier to trigger project change callbacks from one place than from
multiple differect places without proper handling or sequence changes.
multiple different places without proper handling or sequence changes.
Args:
dbcon(AvalonMongoDB): Mongo connection with Session.
@ -42,7 +42,7 @@ class ProjectHandler(QtCore.QObject):
# that may require reshing of projects
refresh_interval = 10000
# Signal emmited when project has changed
# Signal emitted when project has changed
project_changed = QtCore.Signal(str)
projects_refreshed = QtCore.Signal()
timer_timeout = QtCore.Signal()

View file

@ -2,19 +2,21 @@ import uuid
import copy
import logging
import collections
import appdirs
from . import lib
from .constants import (
ACTION_ROLE,
GROUP_ROLE,
VARIANT_GROUP_ROLE,
ACTION_ID_ROLE
ACTION_ID_ROLE,
FORCE_NOT_OPEN_WORKFILE_ROLE
)
from .actions import ApplicationAction
from Qt import QtCore, QtGui
from avalon.vendor import qtawesome
from avalon import style, api
from openpype.lib import ApplicationManager
from openpype.lib import ApplicationManager, JSONSettingRegistry
log = logging.getLogger(__name__)
@ -30,6 +32,13 @@ class ActionModel(QtGui.QStandardItemModel):
# Cache of available actions
self._registered_actions = list()
self.items_by_id = {}
path = appdirs.user_data_dir("openpype", "pypeclub")
self.launcher_registry = JSONSettingRegistry("launcher", path)
try:
_ = self.launcher_registry.get_item("force_not_open_workfile")
except ValueError:
self.launcher_registry.set_item("force_not_open_workfile", [])
def discover(self):
"""Set up Actions cache. Run this for each new project."""
@ -75,7 +84,8 @@ class ActionModel(QtGui.QStandardItemModel):
"group": None,
"icon": app.icon,
"color": getattr(app, "color", None),
"order": getattr(app, "order", None) or 0
"order": getattr(app, "order", None) or 0,
"data": {}
}
)
@ -102,7 +112,7 @@ class ActionModel(QtGui.QStandardItemModel):
# Groups
group_name = getattr(action, "group", None)
# Lable variants
# Label variants
label = getattr(action, "label", None)
label_variant = getattr(action, "label_variant", None)
if label_variant and not label:
@ -179,11 +189,17 @@ class ActionModel(QtGui.QStandardItemModel):
self.beginResetModel()
stored = self.launcher_registry.get_item("force_not_open_workfile")
items = []
for order in sorted(items_by_order.keys()):
for item in items_by_order[order]:
item_id = str(uuid.uuid4())
item.setData(item_id, ACTION_ID_ROLE)
if self.is_force_not_open_workfile(item,
stored):
self.change_action_item(item, True)
self.items_by_id[item_id] = item
items.append(item)
@ -222,6 +238,90 @@ class ActionModel(QtGui.QStandardItemModel):
key=lambda action: (action.order, action.name)
)
def update_force_not_open_workfile_settings(self, is_checked, action_id):
"""Store/remove config for forcing to skip opening last workfile.
Args:
is_checked (bool): True to add, False to remove
action_id (str)
"""
action_item = self.items_by_id.get(action_id)
if not action_item:
return
action = action_item.data(ACTION_ROLE)
actual_data = self._prepare_compare_data(action)
stored = self.launcher_registry.get_item("force_not_open_workfile")
if is_checked:
stored.append(actual_data)
else:
final_values = []
for config in stored:
if config != actual_data:
final_values.append(config)
stored = final_values
self.launcher_registry.set_item("force_not_open_workfile", stored)
self.launcher_registry._get_item.cache_clear()
self.change_action_item(action_item, is_checked)
def change_action_item(self, item, checked):
"""Modifies tooltip and sets if opening of last workfile forbidden"""
tooltip = item.data(QtCore.Qt.ToolTipRole)
if checked:
tooltip += " (Not opening last workfile)"
item.setData(tooltip, QtCore.Qt.ToolTipRole)
item.setData(checked, FORCE_NOT_OPEN_WORKFILE_ROLE)
def is_application_action(self, action):
"""Checks if item is of a ApplicationAction type
Args:
action (action)
"""
if isinstance(action, list) and action:
action = action[0]
return ApplicationAction in action.__bases__
def is_force_not_open_workfile(self, item, stored):
"""Checks if application for task is marked to not open workfile
There might be specific tasks where is unwanted to open workfile right
always (broken file, low performance). This allows artist to mark to
skip opening for combination (project, asset, task_name, app)
Args:
item (QStandardItem)
stored (list) of dict
"""
action = item.data(ACTION_ROLE)
if not self.is_application_action(action):
return False
actual_data = self._prepare_compare_data(action)
for config in stored:
if config == actual_data:
return True
return False
def _prepare_compare_data(self, action):
if isinstance(action, list) and action:
action = action[0]
compare_data = {}
if action:
compare_data = {
"app_label": action.label.lower(),
"project_name": self.dbcon.Session["AVALON_PROJECT"],
"asset": self.dbcon.Session["AVALON_ASSET"],
"task_name": self.dbcon.Session["AVALON_TASK"]
}
return compare_data
class ProjectModel(QtGui.QStandardItemModel):
"""List of projects"""

View file

@ -15,7 +15,8 @@ from .constants import (
ACTION_ID_ROLE,
ANIMATION_START_ROLE,
ANIMATION_STATE_ROLE,
ANIMATION_LEN
ANIMATION_LEN,
FORCE_NOT_OPEN_WORKFILE_ROLE
)
@ -96,6 +97,7 @@ class ActionBar(QtWidgets.QWidget):
view.setViewMode(QtWidgets.QListView.IconMode)
view.setResizeMode(QtWidgets.QListView.Adjust)
view.setSelectionMode(QtWidgets.QListView.NoSelection)
view.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
view.setEditTriggers(QtWidgets.QListView.NoEditTriggers)
view.setWrapping(True)
view.setGridSize(QtCore.QSize(70, 75))
@ -135,8 +137,16 @@ class ActionBar(QtWidgets.QWidget):
project_handler.projects_refreshed.connect(self._on_projects_refresh)
view.clicked.connect(self.on_clicked)
view.customContextMenuRequested.connect(self.on_context_menu)
self._context_menu = None
self._discover_on_menu = False
def discover_actions(self):
if self._context_menu is not None:
self._discover_on_menu = True
return
if self._animation_timer.isActive():
self._animation_timer.stop()
self.model.discover()
@ -171,7 +181,7 @@ class ActionBar(QtWidgets.QWidget):
self.update()
def _start_animation(self, index):
# Offset refresh timout
# Offset refresh timeout
self.project_handler.start_timer()
action_id = index.data(ACTION_ID_ROLE)
item = self.model.items_by_id.get(action_id)
@ -181,6 +191,46 @@ class ActionBar(QtWidgets.QWidget):
self._animated_items.add(action_id)
self._animation_timer.start()
def on_context_menu(self, point):
"""Creates menu to force skip opening last workfile."""
index = self.view.indexAt(point)
if not index.isValid():
return
action_item = index.data(ACTION_ROLE)
if not self.model.is_application_action(action_item):
return
menu = QtWidgets.QMenu(self.view)
checkbox = QtWidgets.QCheckBox("Skip opening last workfile.",
menu)
if index.data(FORCE_NOT_OPEN_WORKFILE_ROLE):
checkbox.setChecked(True)
action_id = index.data(ACTION_ID_ROLE)
checkbox.stateChanged.connect(
lambda: self.on_checkbox_changed(checkbox.isChecked(),
action_id))
action = QtWidgets.QWidgetAction(menu)
action.setDefaultWidget(checkbox)
menu.addAction(action)
self._context_menu = menu
global_point = self.mapToGlobal(point)
menu.exec_(global_point)
self._context_menu = None
if self._discover_on_menu:
self._discover_on_menu = False
self.discover_actions()
def on_checkbox_changed(self, is_checked, action_id):
self.model.update_force_not_open_workfile_settings(is_checked,
action_id)
self.view.update()
if self._context_menu is not None:
self._context_menu.close()
def on_clicked(self, index):
if not index or not index.isValid():
return
@ -189,11 +239,15 @@ class ActionBar(QtWidgets.QWidget):
is_variant_group = index.data(VARIANT_GROUP_ROLE)
if not is_group and not is_variant_group:
action = index.data(ACTION_ROLE)
if index.data(FORCE_NOT_OPEN_WORKFILE_ROLE):
action.data["start_last_workfile"] = False
else:
action.data.pop("start_last_workfile", None)
self._start_animation(index)
self.action_clicked.emit(action)
return
# Offset refresh timout
# Offset refresh timeout
self.project_handler.start_timer()
actions = index.data(ACTION_ROLE)
@ -212,7 +266,7 @@ class ActionBar(QtWidgets.QWidget):
by_variant_label = collections.defaultdict(list)
orders = []
for action in actions:
# Lable variants
# Label variants
label = getattr(action, "label", None)
label_variant = getattr(action, "label_variant", None)
if label_variant and not label:

View file

@ -27,7 +27,7 @@ module.window = None
# Register callback on task change
# - callback can't be defined in Window as it is weak reference callback
# so `WeakSet` will remove it immidiatelly
# so `WeakSet` will remove it immediately
def on_context_task_change(*args, **kwargs):
if module.window:
module.window.on_context_task_change(*args, **kwargs)
@ -455,7 +455,7 @@ class LoaderWindow(QtWidgets.QDialog):
shift_pressed = QtCore.Qt.ShiftModifier & modifiers
if shift_pressed:
print("Force quitted..")
print("Force quit..")
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
print("Good bye")

View file

@ -46,7 +46,7 @@ def get_options(action, loader, parent, repre_contexts):
Args:
action (OptionalAction) - action in menu
loader (cls of api.Loader) - not initilized yet
loader (cls of api.Loader) - not initialized yet
parent (Qt element to parent dialog to)
repre_contexts (list) of dict with full info about selected repres
Returns:

View file

@ -233,8 +233,8 @@ class LookOutliner(QtWidgets.QWidget):
list: list of dictionaries
"""
datas = [i.data(TreeModel.ItemRole) for i in self.view.get_indices()]
return [d for d in datas if d is not None]
items = [i.data(TreeModel.ItemRole) for i in self.view.get_indices()]
return [item for item in items if item is not None]
def right_mouse_menu(self, pos):
"""Build RMB menu for look view"""

View file

@ -124,12 +124,12 @@ class HierarchyModel(QtCore.QAbstractItemModel):
Main part of ProjectManager.
Model should be able to load existing entities, create new, handle their
validations like name duplication and validate if is possible to save it's
validations like name duplication and validate if is possible to save its
data.
Args:
dbcon (AvalonMongoDB): Connection to MongoDB with set AVALON_PROJECT in
it's Session to current project.
its Session to current project.
"""
# Definition of all possible columns with their labels in default order
@ -799,7 +799,7 @@ class HierarchyModel(QtCore.QAbstractItemModel):
for row in range(parent_item.rowCount()):
child_item = parent_item.child(row)
child_id = child_item.id
# Not sure if this can happend
# Not sure if this can happen
# TODO validate this line it seems dangerous as start/end
# row is not changed
if child_id not in children:
@ -1902,7 +1902,7 @@ class AssetItem(BaseItem):
return self._data["name"]
def child_parents(self):
"""Chilren AssetItem can use this method to get it's parent names.
"""Children AssetItem can use this method to get it's parent names.
This is used for `data.parents` key on document.
"""
@ -2006,7 +2006,7 @@ class AssetItem(BaseItem):
@classmethod
def data_from_doc(cls, asset_doc):
"""Convert asset document from Mongo to item data."""
# Minimum required data for cases that it is new AssetItem withoud doc
# Minimum required data for cases that it is new AssetItem without doc
data = {
"name": None,
"type": "asset"
@ -2253,7 +2253,7 @@ class TaskItem(BaseItem):
"""Item representing Task item on Asset document.
Always should be AssetItem children and never should have any other
childrens.
children.
It's name value should be validated with it's parent which only knows if
has same name as other sibling under same parent.

View file

@ -110,7 +110,7 @@ class MultiSelectionComboBox(QtWidgets.QComboBox):
elif event.type() == QtCore.QEvent.KeyPress:
# TODO: handle QtCore.Qt.Key_Enter, Key_Return?
if event.key() == QtCore.Qt.Key_Space:
# toogle the current items check state
# toggle the current items check state
if (
index_flags & QtCore.Qt.ItemIsUserCheckable
and index_flags & QtCore.Qt.ItemIsTristate

View file

@ -555,7 +555,7 @@ class PublisherController:
self.create_context.reset_avalon_context()
self._reset_plugins()
# Publish part must be resetted after plugins
# Publish part must be reset after plugins
self._reset_publish()
self._reset_instances()
@ -690,7 +690,7 @@ class PublisherController:
def remove_instances(self, instances):
""""""
# QUESTION Expect that instaces are really removed? In that case save
# QUESTION Expect that instances are really removed? In that case save
# reset is not required and save changes too.
self.save_changes()

View file

@ -51,7 +51,7 @@ class _HBottomLineWidget(QtWidgets.QWidget):
Corners may have curve set by radius (`set_radius`). Radius should expect
height of widget.
Bottom line is drawed at the bottom of widget. If radius is 0 then height
Bottom line is drawn at the bottom of widget. If radius is 0 then height
of widget should be 1px.
It is expected that parent widget will set height and radius.
@ -94,7 +94,7 @@ class _HTopCornerLineWidget(QtWidgets.QWidget):
or
``````
Horizontal line is drawed in the middle of widget.
Horizontal line is drawn in the middle of widget.
Widget represents left or right corner. Corner may have curve set by
radius (`set_radius`). Radius should expect height of widget (maximum half
@ -225,7 +225,7 @@ class BorderedLabelWidget(QtWidgets.QFrame):
self._radius = radius
side_width = 1 + radius
# Dont't use fixed width/height as that would set also set
# Don't use fixed width/height as that would set also set
# the other size (When fixed width is set then is also set
# fixed height).
self._left_w.setMinimumWidth(side_width)

View file

@ -388,7 +388,7 @@ class InstanceCardView(AbstractInstanceView):
def sizeHint(self):
"""Modify sizeHint based on visibility of scroll bars."""
# Calculate width hint by content widget and verticall scroll bar
# Calculate width hint by content widget and vertical scroll bar
scroll_bar = self._scroll_area.verticalScrollBar()
width = (
self._content_widget.sizeHint().width()

View file

@ -6,7 +6,7 @@ attribute on instance (Group defined by creator).
Each item can be enabled/disabled with their checkbox, whole group
can be enabled/disabled with checkbox on group or
selection can be enabled disabled using checkbox or keyboard key presses:
- Space - change state of selection to oposite
- Space - change state of selection to opposite
- Enter - enable selection
- Backspace - disable selection
@ -589,7 +589,7 @@ class InstanceListView(AbstractInstanceView):
# - create new instance, update existing and remove not existing
for group_name, group_item in self._group_items.items():
# Instance items to remove
# - will contain all exising instance ids at the start
# - will contain all existing instance ids at the start
# - instance ids may be removed when existing instances are checked
to_remove = set()
# Mapping of existing instances under group item
@ -659,7 +659,7 @@ class InstanceListView(AbstractInstanceView):
for instance_id in to_remove:
idx_to_remove.append(existing_mapping[instance_id])
# Remove them in reverse order to prevend row index changes
# Remove them in reverse order to prevent row index changes
for idx in reversed(sorted(idx_to_remove)):
group_item.removeRows(idx, 1)

View file

@ -276,7 +276,7 @@ class VerticallScrollArea(QtWidgets.QScrollArea):
The biggest difference is that the scroll area has scroll bar on left side
and resize of content will also resize scrollarea itself.
Resize if deffered by 100ms because at the moment of resize are not yet
Resize if deferred by 100ms because at the moment of resize are not yet
propagated sizes and visibility of scroll bars.
"""
def __init__(self, *args, **kwargs):

View file

@ -749,7 +749,7 @@ class TasksCombobox(QtWidgets.QComboBox):
self.value_changed.emit()
def set_text(self, text):
"""Set context shown in combobox without chaning selected items."""
"""Set context shown in combobox without changing selected items."""
if text == self._text:
return
@ -1000,7 +1000,7 @@ class VariantInputWidget(PlaceholderLineEdit):
self.value_changed.emit()
def reset_to_origin(self):
"""Set origin value of selected instnaces."""
"""Set origin value of selected instances."""
self.set_value(self._origin_value)
def get_value(self):
@ -1105,7 +1105,7 @@ class GlobalAttrsWidget(QtWidgets.QWidget):
Subset name is or may be affected on context. Gives abiity to modify
context and subset name of instance. This change is not autopromoted but
must be submited.
must be submitted.
Warning: Until artist hit `Submit` changes must not be propagated to
instance data.
@ -1179,7 +1179,7 @@ class GlobalAttrsWidget(QtWidgets.QWidget):
self.cancel_btn = cancel_btn
def _on_submit(self):
"""Commit changes for selected instnaces."""
"""Commit changes for selected instances."""
variant_value = None
asset_name = None
task_name = None
@ -1363,7 +1363,7 @@ class CreatorAttrsWidget(QtWidgets.QWidget):
self._attr_def_id_to_instances = {}
self._attr_def_id_to_attr_def = {}
# To store content of scroll area to prevend garbage collection
# To store content of scroll area to prevent garbage collection
self._content_widget = None
def set_instances_valid(self, valid):
@ -1375,7 +1375,7 @@ class CreatorAttrsWidget(QtWidgets.QWidget):
self._content_widget.setEnabled(valid)
def set_current_instances(self, instances):
"""Set current instances for which are attribute definitons shown."""
"""Set current instances for which are attribute definitions shown."""
prev_content_widget = self._scroll_area.widget()
if prev_content_widget:
self._scroll_area.takeWidget()
@ -1461,7 +1461,7 @@ class PublishPluginAttrsWidget(QtWidgets.QWidget):
self._attr_def_id_to_attr_def = {}
self._attr_def_id_to_plugin_name = {}
# Store content of scroll area to prevend garbage collection
# Store content of scroll area to prevent garbage collection
self._content_widget = None
def set_instances_valid(self, valid):
@ -1473,7 +1473,7 @@ class PublishPluginAttrsWidget(QtWidgets.QWidget):
self._content_widget.setEnabled(valid)
def set_current_instances(self, instances, context_selected):
"""Set current instances for which are attribute definitons shown."""
"""Set current instances for which are attribute definitions shown."""
prev_content_widget = self._scroll_area.widget()
if prev_content_widget:
self._scroll_area.takeWidget()

View file

@ -106,14 +106,14 @@ class Controller(QtCore.QObject):
# ??? Emitted for each process
was_processed = QtCore.Signal(dict)
# Emmited when reset
# Emitted when reset
# - all data are reset (plugins, processing, pari yielder, etc.)
was_reset = QtCore.Signal()
# Emmited when previous group changed
# Emitted when previous group changed
passed_group = QtCore.Signal(object)
# Emmited when want to change state of instances
# Emitted when want to change state of instances
switch_toggleability = QtCore.Signal(bool)
# On action finished
@ -322,7 +322,7 @@ class Controller(QtCore.QObject):
try:
result = pyblish.plugin.process(plugin, self.context, instance)
# Make note of the order at which the
# potential error error occured.
# potential error error occurred.
if result["error"] is not None:
self.processing["ordersWithError"].add(plugin.order)
@ -564,7 +564,7 @@ class Controller(QtCore.QObject):
case must be taken to ensure there are no memory leaks.
Explicitly deleting objects shines a light on where objects
may still be referenced in the form of an error. No errors
means this was uneccesary, but that's ok.
means this was unnecessary, but that's ok.
"""
for instance in self.context:

View file

@ -218,7 +218,7 @@ class OrderGroups:
def sort_groups(_groups_dict):
sorted_dict = collections.OrderedDict()
# make sure wont affect any dictionary as pointer
# make sure won't affect any dictionary as pointer
groups_dict = copy.deepcopy(_groups_dict)
last_order = None
if None in groups_dict:

View file

@ -558,7 +558,7 @@ class SwitchAssetDialog(QtWidgets.QDialog):
repre_docs = io.find(
{
"type": "rerpesentation",
"type": "representation",
"parent": subset_doc["_id"],
"name": {"$in": list(repre_names)}
},

View file

@ -125,7 +125,7 @@ class SceneInventoryWindow(QtWidgets.QDialog):
Override keyPressEvent to do nothing so that Maya's panels won't
take focus when pressing "SHIFT" whilst mouse is over viewport or
outliner. This way users don't accidently perform Maya commands
outliner. This way users don't accidentally perform Maya commands
whilst trying to name an instance.
"""

View file

@ -5,7 +5,7 @@ LABEL_REMOVE_PROJECT = "Remove from project"
LABEL_ADD_PROJECT = "Add to project"
LABEL_DISCARD_CHANGES = "Discard changes"
# Local setting contants
# Local setting constants
# TODO move to settings constants
LOCAL_GENERAL_KEY = "general"
LOCAL_PROJECTS_KEY = "projects"

View file

@ -126,7 +126,7 @@ class DynamicInputItem(QtCore.QObject):
return "studio"
else:
if current_value:
return "overriden"
return "overridden"
if self.value_item.default_value:
return "studio"
@ -512,7 +512,7 @@ class _SiteCombobox(QtWidgets.QWidget):
return "studio"
else:
if current_value:
return "overriden"
return "overridden"
studio_value = self._get_local_settings_item(DEFAULT_PROJECT_KEY)
if studio_value:

View file

@ -222,7 +222,7 @@ class LocalSettingsWindow(QtWidgets.QWidget):
# Do not create local settings widget in init phase as it's using
# settings objects that must be OK to be able create this widget
# - we want to show dialog if anything goes wrong
# - without reseting nothing is shown
# - without resetting nothing is shown
self._settings_widget = None
self._scroll_widget = scroll_widget
self.reset_btn = reset_btn

View file

@ -10,7 +10,7 @@
- `"is_file"` - this key is for storing openpype defaults in `openpype` repo
- reasons of existence: developing new schemas does not require to create defaults manually
- key is validated, must be once in hierarchy else it won't be possible to store openpype defaults
- `"is_group"` - define that all values under key in hierarchy will be overriden if any value is modified, this information is also stored to overrides
- `"is_group"` - define that all values under key in hierarchy will be overridden if any value is modified, this information is also stored to overrides
- this keys is not allowed for all inputs as they may have not reason for that
- key is validated, can be only once in hierarchy but is not required
- currently there are `system configurations` and `project configurations`
@ -199,7 +199,7 @@
- number input, can be used for both integer and float
- key `"decimal"` defines how many decimal places will be used, 0 is for integer input (Default: `0`)
- key `"minimum"` as minimum allowed number to enter (Default: `-99999`)
- key `"maxium"` as maximum allowed number to enter (Default: `99999`)
- key `"maximum"` as maximum allowed number to enter (Default: `99999`)
```
{
"type": "number",

View file

@ -93,7 +93,7 @@ class BaseWidget(QtWidgets.QWidget):
if is_modified:
return "modified"
if has_project_override:
return "overriden"
return "overridden"
if has_studio_override:
return "studio"
return ""
@ -168,7 +168,7 @@ class BaseWidget(QtWidgets.QWidget):
with self.category_widget.working_state_context():
self.entity.add_to_project_override
action = QtWidgets.QAction("Add to project project override")
action = QtWidgets.QAction("Add to project override")
actions_mapping[action] = add_to_project_override
menu.addAction(action)
@ -289,7 +289,7 @@ class BaseWidget(QtWidgets.QWidget):
action = QtWidgets.QAction("Paste", menu)
output.append((action, paste_value))
# Paste value to matchin entity
# Paste value to matching entity
def paste_value_to_path():
with self.category_widget.working_state_context():
_set_entity_value(matching_entity, value)

View file

@ -183,7 +183,7 @@ class DictConditionalWidget(BaseWidget):
content_widget.setObjectName("ContentWidget")
if self.entity.highlight_content:
content_state = "hightlighted"
content_state = "highlighted"
bottom_margin = 5
else:
content_state = ""

View file

@ -354,7 +354,7 @@ class ModifiableDictItem(QtWidgets.QWidget):
if self.entity.has_unsaved_changes:
return "modified"
if self.entity.has_project_override:
return "overriden"
return "overridden"
if self.entity.has_studio_override:
return "studio"
return ""
@ -600,8 +600,8 @@ class DictMutableKeysWidget(BaseWidget):
self.input_fields = []
self.required_inputs_by_key = {}
if self.entity.hightlight_content:
content_state = "hightlighted"
if self.entity.highlight_content:
content_state = "highlighted"
bottom_margin = 5
else:
content_state = ""

View file

@ -150,7 +150,7 @@ class DictImmutableKeysWidget(BaseWidget):
content_widget.setObjectName("ContentWidget")
if self.entity.highlight_content:
content_state = "hightlighted"
content_state = "highlighted"
bottom_margin = 5
else:
content_state = ""
@ -477,7 +477,7 @@ class OpenPypeVersionText(TextWidget):
self.entity.set(value)
self.update_style()
else:
# Manually trigger hierachical style update
# Manually trigger hierarchical style update
self.ignore_input_changes.set_ignore(True)
self.ignore_input_changes.set_ignore(False)
@ -675,7 +675,7 @@ class RawJsonWidget(InputWidget):
self.entity.set(self.input_field.json_value())
self.update_style()
else:
# Manually trigger hierachical style update
# Manually trigger hierarchical style update
self.ignore_input_changes.set_ignore(True)
self.ignore_input_changes.set_ignore(False)
@ -792,7 +792,7 @@ class PathWidget(BaseWidget):
self.input_field.hierarchical_style_update()
def _on_entity_change(self):
# No need to do anything. Styles will be updated from top hierachy.
# No need to do anything. Styles will be updated from top hierarchy.
pass
def update_style(self):

View file

@ -7,7 +7,7 @@ VALUE_CHANGE_OFFSET_MS = 300
def create_deffered_value_change_timer(callback):
"""Deffer value change callback.
"""Defer value change callback.
UI won't trigger all callbacks on each value change but after predefined
time. Timer is reset on each start so callback is triggered after user

View file

@ -28,7 +28,7 @@ class ListStrictWidget(BaseWidget):
break
self._any_children_has_label = any_children_has_label
# Change column stretch factor for verticall alignment
# Change column stretch factor for vertical alignment
if not self.entity.is_horizontal:
col_index = 2 if any_children_has_label else 1
content_layout.setColumnStretch(col_index, 1)

View file

@ -131,7 +131,7 @@ class MultiSelectionComboBox(QtWidgets.QComboBox):
elif event.type() == QtCore.QEvent.KeyPress:
# TODO: handle QtCore.Qt.Key_Enter, Key_Return?
if event.key() == QtCore.Qt.Key_Space:
# toogle the current items check state
# toggle the current items check state
if (
index_flags & QtCore.Qt.ItemIsUserCheckable
and index_flags & QtCore.Qt.ItemIsTristate

View file

@ -153,7 +153,7 @@ class MainWidget(QtWidgets.QWidget):
def _on_restart_required(self):
# Don't show dialog if there are not registered slots for
# `trigger_restart` signal.
# - For example when settings are runnin as standalone tool
# - For example when settings are running as standalone tool
# - PySide2 and PyQt5 compatible way how to find out
method_index = self.metaObject().indexOfMethod("trigger_restart()")
method = self.metaObject().method(method_index)

View file

@ -99,7 +99,7 @@ class Window(QtWidgets.QDialog):
return self._db
def on_start(self):
''' Things must be done when initilized.
''' Things must be done when initialized.
'''
# Refresh asset input in Family widget
self.on_asset_changed()

View file

@ -68,7 +68,7 @@ class AssetModel(TreeModel):
"""
if silos:
# WARNING: Silo item "_id" is set to silo value
# mainly because GUI issue with perserve selection and expanded row
# mainly because GUI issue with preserve selection and expanded row
# and because of easier hierarchy parenting (in "assets")
for silo in silos:
node = Node({

View file

@ -18,7 +18,7 @@ def preserve_expanded_rows(tree_view,
This function is created to maintain the expand vs collapse status of
the model items. When refresh is triggered the items which are expanded
will stay expanded and vise versa.
will stay expanded and vice versa.
Arguments:
tree_view (QWidgets.QTreeView): the tree view which is
@ -68,7 +68,7 @@ def preserve_selection(tree_view,
This function is created to maintain the selection status of
the model items. When refresh is triggered the items which are expanded
will stay expanded and vise versa.
will stay expanded and vice versa.
tree_view (QWidgets.QTreeView): the tree view nested in the application
column (int): the column to retrieve the data from
@ -390,7 +390,7 @@ class AssetWidget(QtWidgets.QWidget):
assets, (tuple, list)
), "Assets must be list or tuple"
# convert to list - tuple cant be modified
# convert to list - tuple can't be modified
assets = list(assets)
# Clear selection

View file

@ -101,7 +101,7 @@ class DropDataFrame(QtWidgets.QFrame):
return paths
def _add_item(self, data, actions=[]):
# Assign to self so garbage collector wont remove the component
# Assign to self so garbage collector won't remove the component
# during initialization
new_component = ComponentItem(self.components_list, self)
new_component.set_context(data)

View file

@ -373,7 +373,7 @@ class FamilyWidget(QtWidgets.QWidget):
Override keyPressEvent to do nothing so that Maya's panels won't
take focus when pressing "SHIFT" whilst mouse is over viewport or
outliner. This way users don't accidently perform Maya commands
outliner. This way users don't accidentally perform Maya commands
whilst trying to name an instance.
"""

View file

@ -433,7 +433,7 @@ class PypeInfoSubWidget(QtWidgets.QWidget):
pype_info.pop("version_type", self.not_applicable)
)
pype_info["version_value"] = version_value
# Prepare lable mapping
# Prepare label mapping
key_label_mapping = {
"version_value": "Running version:",
"build_verison": "Build version:",

View file

@ -238,7 +238,7 @@ class TrayManager:
@property
def doubleclick_callback(self):
"""Doubleclick callback for Tray icon."""
"""Double-click callback for Tray icon."""
callback_name = self.modules_manager.doubleclick_callback
return self.modules_manager.doubleclick_callbacks.get(callback_name)
@ -398,7 +398,7 @@ class TrayManager:
title = "Settings miss default values"
msg = (
"Your OpenPype will not work as expected! \n"
"Some default values in settigs are missing. \n\n"
"Some default values in settings are missing. \n\n"
"Please contact OpenPype team."
)
msg_box = QtWidgets.QMessageBox(
@ -430,7 +430,7 @@ class TrayManager:
message (str): Content of message.
icon (QSystemTrayIcon.MessageIcon): Message's icon. Default is
Information icon, may differ by Qt version.
msecs (int): Duration of message visibility in miliseconds.
msecs (int): Duration of message visibility in milliseconds.
Default is 10000 msecs, may differ by Qt version.
"""
args = [title, message]

View file

@ -268,7 +268,7 @@ class ConsoleTrayApp:
def _multiple_replace(text, adict):
"""Replace multiple tokens defined in dict.
Find and replace all occurances of strings defined in dict is
Find and replace all occurrences of strings defined in dict is
supplied string.
Args:

View file

@ -1,6 +1,6 @@
"""Single access point to all tools usable in hosts.
It is possible to create `HostToolsHelper` in host implementaion or
It is possible to create `HostToolsHelper` in host implementation or
use singleton approach with global functions (using helper anyway).
"""
@ -223,7 +223,7 @@ class HostToolsHelper:
"""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
those cases was added 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.
@ -347,7 +347,7 @@ class _SingletonPoint:
return cls.helper.get_tool_by_name(tool_name, parent, *args, **kwargs)
# Function callbacks using singleton acces point
# Function callbacks using singleton access point
def get_tool_by_name(tool_name, parent=None, *args, **kwargs):
return _SingletonPoint.get_tool_by_name(tool_name, parent, *args, **kwargs)

View file

@ -132,7 +132,7 @@ def preserve_expanded_rows(tree_view, column=0, role=None):
This function is created to maintain the expand vs collapse status of
the model items. When refresh is triggered the items which are expanded
will stay expanded and vise versa.
will stay expanded and vice versa.
Arguments:
tree_view (QWidgets.QTreeView): the tree view which is
@ -176,7 +176,7 @@ def preserve_selection(tree_view, column=0, role=None, current_index=True):
This function is created to maintain the selection status of
the model items. When refresh is triggered the items which are expanded
will stay expanded and vise versa.
will stay expanded and vice versa.
tree_view (QWidgets.QTreeView): the tree view nested in the application
column (int): the column to retrieve the data from
@ -372,7 +372,7 @@ class GroupsConfig:
group_configs = []
project_name = self.dbcon.Session.get("AVALON_PROJECT")
if project_name:
# Get pre-defined group name and apperance from project config
# Get pre-defined group name and appearance from project config
project_doc = self.dbcon.find_one(
{"type": "project"},
projection={"config.groups": True}

View file

@ -152,7 +152,7 @@ class OptionalMenu(QtWidgets.QMenu):
"""A subclass of `QtWidgets.QMenu` to work with `OptionalAction`
This menu has reimplemented `mouseReleaseEvent`, `mouseMoveEvent` and
`leaveEvent` to provide better action hightlighting and triggering for
`leaveEvent` to provide better action highlighting and triggering for
actions that were instances of `QtWidgets.QWidgetAction`.
"""

View file

@ -11,6 +11,7 @@ from Qt import QtWidgets, QtCore
from avalon import io, api, pipeline
from openpype import style
from openpype.pipeline.lib import BeforeWorkfileSave
from openpype.tools.utils.lib import (
qt_app_context
)
@ -162,7 +163,7 @@ class NameWindow(QtWidgets.QDialog):
# Build inputs
inputs_layout = QtWidgets.QFormLayout(inputs_widget)
# Add version only if template contain version key
# Add version only if template contains version key
# - since the version can be padded with "{version:0>4}" we only search
# for "{version".
if "{version" in self.template:
@ -170,7 +171,7 @@ class NameWindow(QtWidgets.QDialog):
else:
version_widget.setVisible(False)
# Add subversion only if template containt `{comment}`
# Add subversion only if template contains `{comment}`
if "{comment}" in self.template:
inputs_layout.addRow("Subversion:", subversion_input)
else:
@ -183,7 +184,7 @@ class NameWindow(QtWidgets.QDialog):
main_layout.addWidget(inputs_widget)
main_layout.addWidget(btns_widget)
# Singal callback registration
# Signal callback registration
version_input.valueChanged.connect(self.on_version_spinbox_changed)
last_version_check.stateChanged.connect(
self.on_version_checkbox_changed
@ -367,7 +368,8 @@ class FilesWidget(QtWidgets.QWidget):
self.template_key = "work"
# This is not root but workfile directory
self.root = None
self._workfiles_root = None
self._workdir_path = None
self.host = api.registered_host()
# Whether to automatically select the latest modified
@ -465,8 +467,9 @@ class FilesWidget(QtWidgets.QWidget):
# This way we can browse it even before we enter it.
if self._asset_id and self._task_name and self._task_type:
session = self._get_session()
self.root = self.host.work_root(session)
self.files_model.set_root(self.root)
self._workdir_path = session["AVALON_WORKDIR"]
self._workfiles_root = self.host.work_root(session)
self.files_model.set_root(self._workfiles_root)
else:
self.files_model.set_root(None)
@ -590,7 +593,7 @@ class FilesWidget(QtWidgets.QWidget):
window = NameWindow(
parent=self,
root=self.root,
root=self._workfiles_root,
anatomy=self.anatomy,
template_key=self.template_key,
session=session
@ -605,7 +608,7 @@ class FilesWidget(QtWidgets.QWidget):
return
src = self._get_selected_filepath()
dst = os.path.join(self.root, work_file)
dst = os.path.join(self._workfiles_root, work_file)
shutil.copy(src, dst)
self.workfile_created.emit(dst)
@ -638,98 +641,59 @@ class FilesWidget(QtWidgets.QWidget):
"filter": ext_filter
}
if Qt.__binding__ in ("PySide", "PySide2"):
kwargs["dir"] = self.root
kwargs["dir"] = self._workfiles_root
else:
kwargs["directory"] = self.root
kwargs["directory"] = self._workfiles_root
work_file = QtWidgets.QFileDialog.getOpenFileName(**kwargs)[0]
if work_file:
self.open_file(work_file)
def on_save_as_pressed(self):
work_file = self.get_filename()
if not work_file:
work_filename = self.get_filename()
if not work_filename:
return
# Initialize work directory if it has not been initialized before
if not os.path.exists(self.root):
log.debug("Initializing Work Directory: %s", self.root)
self.initialize_work_directory()
if not os.path.exists(self.root):
# Failed to initialize Work Directory
log.error(
"Failed to initialize Work Directory: {}".format(self.root)
)
return
file_path = os.path.join(os.path.normpath(self.root), work_file)
pipeline.emit("before.workfile.save", [file_path])
self._enter_session() # Make sure we are in the right session
self.host.save_file(file_path)
# Trigger before save event
BeforeWorkfileSave.emit(work_filename, self._workdir_path)
# Make sure workfiles root is updated
# - this triggers 'workio.work_root(...)' which may change value of
# '_workfiles_root'
self.set_asset_task(
self._asset_id, self._task_name, self._task_type
)
# Create workfiles root folder
if not os.path.exists(self._workfiles_root):
log.debug("Initializing Work Directory: %s", self._workfiles_root)
os.makedirs(self._workfiles_root)
# Update session if context has changed
self._enter_session()
# Prepare full path to workfile and save it
filepath = os.path.join(
os.path.normpath(self._workfiles_root), work_filename
)
self.host.save_file(filepath)
# Create extra folders
create_workdir_extra_folders(
self.root,
self._workdir_path,
api.Session["AVALON_APP"],
self._task_type,
self._task_name,
api.Session["AVALON_PROJECT"]
)
pipeline.emit("after.workfile.save", [file_path])
self.workfile_created.emit(file_path)
# Trigger after save events
pipeline.emit("after.workfile.save", [filepath])
self.workfile_created.emit(filepath)
# Refresh files model
self.refresh()
def on_file_select(self):
self.file_selected.emit(self._get_selected_filepath())
def initialize_work_directory(self):
"""Initialize Work Directory.
This is used when the Work Directory does not exist yet.
This finds the current AVALON_APP_NAME and tries to triggers its
`.toml` initialization step. Note that this will only be valid
whenever `AVALON_APP_NAME` is actually set in the current session.
"""
# Inputs (from the switched session and running app)
session = api.Session.copy()
changes = pipeline.compute_session_changes(
session,
asset=self._get_asset_doc(),
task=self._task_name,
template_key=self.template_key
)
session.update(changes)
# Prepare documents to get workdir data
project_doc = io.find_one({"type": "project"})
asset_doc = io.find_one(
{
"type": "asset",
"name": session["AVALON_ASSET"]
}
)
task_name = session["AVALON_TASK"]
host_name = session["AVALON_APP"]
# Get workdir from collected documents
workdir = get_workdir(project_doc, asset_doc, task_name, host_name)
# Create workdir if does not exist yet
if not os.path.exists(workdir):
os.makedirs(workdir)
# Force a full to the asset as opposed to just self.refresh() so
# that it will actually check again whether the Work directory exists
self.set_asset_task(self._asset_id, self._task_name, self._task_type)
def refresh(self):
"""Refresh listed files for current selection in the interface"""
self.files_model.refresh()
@ -833,7 +797,7 @@ class SidePanelWidget(QtWidgets.QWidget):
self.note_input.setEnabled(enabled)
self.btn_note_save.setEnabled(enabled)
# Make sure workfile doc is overriden
# Make sure workfile doc is overridden
self._workfile_doc = workfile_doc
# Disable inputs and remove texts if any required arguments are missing
if not enabled:
@ -978,7 +942,7 @@ class Window(QtWidgets.QMainWindow):
Override keyPressEvent to do nothing so that Maya's panels won't
take focus when pressing "SHIFT" whilst mouse is over viewport or
outliner. This way users don't accidently perform Maya commands
outliner. This way users don't accidentally perform Maya commands
whilst trying to name an instance.
"""

View file

@ -5,6 +5,9 @@ Contains end-to-end tests for automatic testing of OP.
Should run headless publish on all hosts to check basic publish use cases automatically
to limit regression issues.
Uses env var `HEADLESS_PUBLISH` (set in test data zip files) to differentiate between regular publish
and "automated" one.
How to run
----------
- activate `{OPENPYPE_ROOT}/.venv`

View file

@ -26,7 +26,7 @@ Each published variant can come out of the software in multiple representations.
### Family
Each published [subset][3b89d8e0] can have exactly one family assigned to it. Family determines the type of data that the subset holds. Family doesn't dictate the file type, but can enforce certain technical specifications. For example OpenPype default configuration expects `model` family to only contain geometry without any shaders or joins when it is published.
Each published [subset][3b89d8e0] can have exactly one family assigned to it. Family determines the type of data that the subset holds. Family doesn't dictate the file type, but can enforce certain technical specifications. For example OpenPype default configuration expects `model` family to only contain geometry without any shaders or joints when it is published.
[3b89d8e0]: #subset "subset"
@ -40,7 +40,7 @@ General term for Software or Application supported by OpenPype and Avalon. These
### Tool
Small piece of software usually dedicated to a particular purpose. Most of OpenPype and Avalon tools have GUI, but some are command line only
Small piece of software usually dedicated to a particular purpose. Most of OpenPype and Avalon tools have GUI, but some are command line only.
### Publish
@ -50,4 +50,4 @@ Process of exporting data from your work scene to versioned, immutable file that
### Load
Process of importing previously published subsets into your current scene, using any of the OpenPype tools.
Loading asset using proper tools will ensure that all your scene content stays version controlled and updatable at a later point
Loading asset using proper tools will ensure that all your scene content stays version controlled and updatable at a later point.