Source code for PyR3.shortcut.context

# -*- coding: utf-8 -*-
from __future__ import annotations

from contextlib import contextmanager
from typing import List

import bpy

Object = bpy.types.Object


class _ContextMeta(type):
    @property
    def active(cls) -> Object:
        return bpy.context.active_object

    @active.setter
    def active(cls, object_: Object):
        bpy.context.view_layer.objects.active = object_

    @property
    def selected(cls) -> Objects[Object]:
        return Objects(bpy.context.selected_objects)


[docs]class Objects(list, metaclass=_ContextMeta): """As a class, provides set of static functionalities for managing global selection and currently selected and active objects. As a instance, is a container for objects with possibility to select or deselect object(s) contained inside. """ #: Currently active object. It can be set to change active object. active: Object #: List of currently selected objects. #: This property is read-only. #: Use methods to alter selection. selected: Objects[Object]
[docs] @staticmethod def select(*ob: Object): """Select given object(s). :param ob: object(s) to select :type ob: Object """ for ob_ in ob: ob_.select_set(True)
[docs] @staticmethod def deselect(*ob: Object): """Deselect given object(s). :param ob: object(s) to deselect :type ob: Object """ for ob_ in ob: ob_.select_set(False)
[docs] @staticmethod def select_all(): """Selects all objects available in viewport.""" bpy.ops.object.select_all(action="SELECT")
[docs] @staticmethod def deselect_all(): """Deselects all objects available in viewport.""" bpy.ops.object.select_all(action="DESELECT")
[docs] @classmethod def select_only(cls, *ob: Object): """Deselects all objects and selects only given one(s). :param ob: Object(s) to select :type ob: Object """ cls.deselect_all() cls.select(*ob)
[docs] @classmethod def delete(cls, *ob: Object) -> None: """Delete object(s) ob. It keeps previously selected object(s) selected. :param ob: Object(s) to select :type ob: Object """ cls.deselect(*ob) with temporarily_selected(*ob): bpy.ops.object.delete()
[docs] @classmethod def delete_all(cls) -> None: """Deletes all selectable objects.""" cls.select_all() bpy.ops.object.delete()
[docs] @classmethod def duplicate(cls, *ob: Object) -> None: """Duplicates object(s) :param ob: object(s) to duplicate :type ob: Object """ with temporarily_selected(*ob): bpy.ops.object.duplicate()
[docs] @staticmethod def inverse_selection(): bpy.ops.object.select_all(action="INVERT")
[docs] def select_contained(self): """Selects elements contained in this sequence.""" self.select(*self)
[docs] def deselect_contained(self): """Deselects elements contained in this sequence.""" self.deselect(*self)
[docs] def select_only_contained(self): """Selects only elements contained in this sequence.""" self.select_only(*self)
[docs] def only(self) -> Object: """Returns only element in sequence. :raises ValueError: If sequence is empty or has more than one element. :return: Only element from sequence :rtype: Object """ if len(self) == 1: return self[0] else: raise ValueError("Objects list doesn't contain one element.")
def __str__(self) -> str: return f"Objects{super().__str__()}"
[docs] @staticmethod def all(): return Objects(bpy.context.scene.objects)
[docs]@contextmanager def temporarily_selected(*ob: Object): """For context manager usage, on enter selects only objects passed to constructor, on exit restores selection on previously selected objects.""" # preparation previously_selected = Objects.selected Objects.select_only(*ob) # yield execution to caller yield # back here for clean-up Objects.select_only(*previously_selected)
[docs]@contextmanager def temporary_scene(): """Creates temporary scene and sets it as currently used. After exit, all objects selected in temporary scene are copied into previous scene and previous scene is set as currently used. :yield: (new, old) scenes :rtype: Tuple[bpy.types.Scene, bpy.types.Scene] """ # preperation old = getScene() newScene() new = getScene() # yield execution to caller yield new, old # clean-up code here for selected in Objects.selected: old.collection.objects.link(selected) for selected in Objects.selected: new.collection.objects.unlink(selected) delScene()
[docs]def getScene() -> bpy.types.Scene: """Returns currently used scene. :return: Scene :rtype: bpy.types.Scene """ return bpy.context.window.scene
[docs]def setScene(scene: bpy.types.Scene) -> None: """Sets new scene to use. :param scene: Scene :type scene: bpy.types.Scene """ bpy.context.window.scene = scene
[docs]def newScene() -> None: """Creates new Scene object and automatically sets it as currently used one.""" bpy.ops.scene.new()
[docs]def delScene() -> None: """Deletes currently used scene.""" bpy.ops.scene.delete()
[docs]def cleanScene() -> None: """Deletes current scene and creates new one. Be aware that for total clean-up you should call wipeScenes() instead, as it destroys **ALL** scenes, not only current one, as cleanScene() does. """ old_scene = getScene() newScene() new_scene = getScene() setScene(old_scene) delScene() setScene(new_scene)
[docs]def listScenes() -> List[bpy.types.Scene]: """Returns list of existing scenes.""" return list(bpy.data.scenes)
[docs]def wipeScenes() -> None: """Destroys all existing ones and creates new empty one.""" for _ in listScenes()[:-1]: delScene() cleanScene()