Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Blender 2.80 support (this will resolve #361) #466

Open
wants to merge 35 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
1a7911d
Add Blender 2.80 support
jasperges Oct 30, 2019
baf6c49
Some style changes and cleanup
jasperges Oct 30, 2019
d83484c
Fix: renamed open_file and save_file in __all__
jasperges Oct 30, 2019
4e2ab62
Fix: fix line lenght + unused var
jasperges Oct 30, 2019
0fbe9aa
Merge remote-tracking branch 'upstream/master' into add-blender28-sup…
jasperges Oct 30, 2019
a1bf183
Style fix
jasperges Oct 30, 2019
ee2954b
Import QtWidgets with namespace
jasperges Nov 1, 2019
809e53d
Style changes
jasperges Nov 1, 2019
aafbf85
Merge branch 'master' into add-blender28-support
jasperges Nov 5, 2019
16bd950
Merge branch 'master' into add-blender28-support
jasperges Nov 11, 2019
d3a4865
Merge branch 'master' into add-blender28-support
jasperges Dec 4, 2019
4be0f7c
Merge remote-tracking branch 'upstream/master' into add-blender28-sup…
jasperges Dec 16, 2019
ea5b85f
Merge branch 'master' into add-blender28-support
jasperges Dec 17, 2019
5bc3bb3
Add bpy.context wrapper
jasperges Dec 17, 2019
bf54052
Use bpy.context wrapper in TestApp
jasperges Dec 17, 2019
0570d60
Add `bpy.context` wrapper
jasperges Dec 17, 2019
834371b
Revert adding the bpy wrapper
jasperges Dec 30, 2019
2433fdc
Add library function to get selected objects
jasperges Dec 30, 2019
0a655ea
Make Avalon imports relative
jasperges Dec 30, 2019
d2ec489
Add note to change to `bpy.app.timers`
jasperges Dec 30, 2019
a78b312
Remove hardcoded 'scene' directory
jasperges Dec 30, 2019
3d46168
Use `bpy.app.timers` and remove test app
jasperges Dec 31, 2019
0840537
Cleanup
jasperges Dec 31, 2019
cc205ba
Merge branch 'master' into blender28-dev
jasperges Jan 6, 2020
d14d97b
Merge branch 'master' into blender28-dev
jasperges Jan 8, 2020
23331c4
Refactor to find_submodule (needed after merge of #501)
jasperges Jan 8, 2020
37c2fa8
Remove teardown function
jasperges Jan 8, 2020
12633c7
Make sure only 1 timer is registered for Qt apps
jasperges Jan 8, 2020
5019142
Fix check for self._window
jasperges Jan 8, 2020
cff9ad6
Exclude blender from tests
jasperges Jan 8, 2020
945c7a3
Merge branch 'master' into blender28-dev
jasperges Jan 13, 2020
a320eb3
Rename avalon_setup → setup_avalon and add comment about sys.excepthook
jasperges Jan 8, 2020
33d6e0c
Merge branch 'master' into blender28-dev
jasperges Jan 24, 2020
fe4bd5c
Remove unneeded `config` variable from blender (un)install
jasperges Jan 24, 2020
fd10091
Remove unused import + fix indentation
jasperges Jan 24, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions avalon/blender/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
"""Public API

Anything that isn't defined here is INTERNAL and unreliable for external use.

"""

from .pipeline import (
install,
uninstall,
Creator,
Loader,
ls,
publish,
containerise,
)

from .workio import (
jasperges marked this conversation as resolved.
Show resolved Hide resolved
open_file,
save_file,
current_file,
has_unsaved_changes,
file_extensions,
work_root,
)

from .lib import (
lsattr,
lsattrs,
read,
maintained_selection,
# unique_name,
)


__all__ = [
"install",
"uninstall",
"Creator",
"Loader",
"ls",
"publish",
"containerise",

# Workfiles API
"open",
"save",
"current_file",
"has_unsaved_changes",
"file_extensions",
"work_root",

# Utility functions
"maintained_selection",
"lsattr",
"lsattrs",
"read",
# "unique_name",
]
Binary file added avalon/blender/icons/pyblish-32x32.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
154 changes: 154 additions & 0 deletions avalon/blender/lib.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
"""Standalone helper functions."""

import contextlib
from typing import Dict, List, Union

import bpy

from ..lib import logger
from . import pipeline


def imprint(node: bpy.types.bpy_struct_meta_idprop, data: Dict):
r"""Write `data` to `node` as userDefined attributes
BigRoy marked this conversation as resolved.
Show resolved Hide resolved

Arguments:
node: Long name of node
data: Dictionary of key/value pairs

Example:
>>> import bpy
>>> def compute():
... return 6
...
>>> bpy.ops.mesh.primitive_cube_add()
>>> cube = bpy.context.view_layer.objects.active
>>> imprint(cube, {
... "regularString": "myFamily",
... "computedValue": lambda: compute()
... })
...
>>> cube['avalon']['computedValue']
6

"""

imprint_data = dict()

for key, value in data.items():
if value is None:
continue

if callable(value):
# Support values evaluated at imprint
value = value()

if not isinstance(value, (int, float, bool, str, list)):
raise TypeError(f"Unsupported type: {type(value)}")

imprint_data[key] = value

pipeline.metadata_update(node, imprint_data)


def lsattr(attr: str,
value: Union[str, int, bool, List, Dict, None] = None) -> List:
r"""Return nodes matching `attr` and `value`

Arguments:
attr: Name of Blender property
value: Value of attribute. If none
is provided, return all nodes with this attribute.

Example:
>>> lsattr("id", "myId")
... [bpy.data.objects["myNode"]
>>> lsattr("id")
... [bpy.data.objects["myNode"], bpy.data.objects["myOtherNode"]]

Returns:
list
"""
return lsattrs({attr: value})


def lsattrs(attrs: Dict) -> List:
r"""Return nodes with the given attribute(s).

Arguments:
attrs: Name and value pairs of expected matches

Example:
>>> lsattrs({"age": 5}) # Return nodes with an `age` of 5
# Return nodes with both `age` and `color` of 5 and blue
>>> lsattrs({"age": 5, "color": "blue"})

Returns a list.

"""
# For now return all objects, not filtered by scene/collection/view_layer.
matches = set()
for coll in dir(bpy.data):
if not isinstance(
getattr(bpy.data, coll),
bpy.types.bpy_prop_collection,
):
continue
for node in getattr(bpy.data, coll):
for attr, value in attrs.items():
avalon_prop = node.get(pipeline.AVALON_PROPERTY)
if not avalon_prop:
continue
if avalon_prop.get(attr) and (value is None or
avalon_prop.get(attr) == value):
matches.add(node)
return list(matches)


def read(node: bpy.types.bpy_struct_meta_idprop):
"""Return user-defined attributes from `node`"""
data = dict(node.get(pipeline.AVALON_PROPERTY))

# Ignore hidden/internal data
data = {
key: value
for key, value in data.items() if not key.startswith("_")
}

return data


@contextlib.contextmanager
def maintained_selection():
r"""Maintain selection during context

Example:
>>> with maintained_selection():
... # Modify selection
... bpy.ops.object.select_all(action='DESELECT')
>>> # Selection restored
"""

previous_selection = bpy.context.selected_objects
previous_active = bpy.context.view_layer.objects.active
try:
yield
finally:
# Clear the selection
for node in bpy.context.selected_objects:
node.select_set(state=False)
if previous_selection:
for node in previous_selection:
try:
node.select_set(state=True)
except ReferenceError:
# This could happen if a selected node was deleted during
# the context.
logger.exception("Failed to reselect")
continue
try:
bpy.context.view_layer.objects.active = previous_active
except ReferenceError:
# This could happen if the active node was deleted during the
# context.
logger.exception("Failed to set active object.")
Loading